android狀態列一體化(改變狀態列的背景顏色)
Android 沉浸式狀態列、狀態列一體化、透明狀態列、仿ios透明狀態列
http://blog.csdn.net/jdsjlzx/article/details/50437779
注:狀態列的字型顏色位白色, 如果狀態列背景為白色,上面的部落格已經解決了。
原文出處:http://niorgai.github.io/2016/03/20/Android-transulcent-status-bar/
最近業務上看到一個設計圖挺好看,所以研究了一下透明狀態列,注意不是沉浸式狀態列,在參考了網上的一些資料後,整理出了這篇部落格.
Github Demo 連結: StatusBarCompat
參考文章:首先強調,對於狀態列的處理有兩種不同的方式, 這裡從Translucent System Bar 的最佳實踐直接盜了兩張圖做對比~.
全屏( ContentView 可以進入狀態列) | 非全屏 ( ContentView 與狀態列分離, 狀態列直接著色) |
---|---|
先定義幾個名詞:
-
全屏模式: 左邊圖所示.
-
著色模式: 右邊圖所示.
-
ContentView: activity.findViewById(Window.ID_ANDROID_CONTENT) 獲取的 View , 即 setContentView 方法所設定的 View, 實質為 FrameLayout.
-
ContentParent: ContentView 的 parent , 實質為 LinearLayout.
-
ChildView: ContentView 的第一個子 View ,即佈局檔案中的 layout .
再介紹一下相關的函式:
-
fitsSystemWindows, 該屬性可以設定是否為系統 View 預留出空間, 當設定為 true 時,會預留出狀態列的空間.
-
ContentView, 實質為 ContentFrameLayout, 但是重寫了 dispatchFitSystemWindows 方法, 所以對其設定 fitsSystemWindows 無效.
-
ContentParent, 實質為 FitWindowsLinearLayout, 裡面第一個 View 是 ViewStubCompat, 如果主題沒有設定 title ,它就不會 inflate .第二個 View 就是 ContentView.
5.0以上的處理:
自5.0引入 Material Design ,狀態列對開發者更加直接,可以直接呼叫 setStatusBarColor 來設定狀態列的顏色.
全屏模式:
- Window window = activity.getWindow();
- //設定透明狀態列,這樣才能讓 ContentView 向上
- window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
- //需要設定這個 flag 才能呼叫 setStatusBarColor 來設定狀態列顏色
- window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- //設定狀態列顏色
- window.setStatusBarColor(statusColor);
- ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
- View mChildView = mContentView.getChildAt(0);
- if (mChildView != null) {
- //注意不是設定 ContentView 的 FitsSystemWindows, 而是設定 ContentView 的第一個子 View . 使其不為系統 View 預留空間.
- ViewCompat.setFitsSystemWindows(mChildView, false);
- }
著色模式:
- Window window = activity.getWindow();
- //取消設定透明狀態列,使 ContentView 內容不再覆蓋狀態列
- window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
- //需要設定這個 flag 才能呼叫 setStatusBarColor 來設定狀態列顏色
- window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- //設定狀態列顏色
- window.setStatusBarColor(statusColor);
- ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
- View mChildView = mContentView.getChildAt(0);
- if (mChildView != null) {
- //注意不是設定 ContentView 的 FitsSystemWindows, 而是設定 ContentView 的第一個子 View . 預留出系統 View 的空間.
- ViewCompat.setFitsSystemWindows(mChildView, true);
- }
4.4-5.0的處理:
4.4-5.0因為沒有直接的 API 可以呼叫,需要自己相容處理,網上的解決方法基本都是建立一下高度為狀態列的 View ,通過設定這個 View 的背景色來模擬狀態列. 這裡我嘗試了三種方法來相容處理.
方法1: 向 ContentView 新增假 View , 設定 ChildView 的 marginTop 屬性來模擬 fitsSystemWindows .
全屏模式:
- Window window = activity.getWindow();
- ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
- //首先使 ChildView 不預留空間
- View mChildView = mContentView.getChildAt(0);
- if (mChildView != null) {
- ViewCompat.setFitsSystemWindows(mChildView, false);
- }
- int statusBarHeight = getStatusBarHeight(activity);
- //需要設定這個 flag 才能設定狀態列
- window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
- //避免多次呼叫該方法時,多次移除了 View
- if (mChildView != null && mChildView.getLayoutParams() != null && mChildView.getLayoutParams().height == statusBarHeight) {
- //移除假的 View.
- mContentView.removeView(mChildView);
- mChildView = mContentView.getChildAt(0);
- }
- if (mChildView != null) {
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mChildView.getLayoutParams();
- //清除 ChildView 的 marginTop 屬性
- if (lp != null && lp.topMargin >= statusBarHeight) {
- lp.topMargin -= statusBarHeight;
- mChildView.setLayoutParams(lp);
- }
- }
著色模式:
- Window window = activity.getWindow();
- ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
- //First translucent status bar.
- window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
- int statusBarHeight = getStatusBarHeight(activity);
- View mChildView = mContentView.getChildAt(0);
- if (mChildView != null) {
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mChildView.getLayoutParams();
- //如果已經為 ChildView 設定過了 marginTop, 再次呼叫時直接跳過
- if (lp != null && lp.topMargin < statusBarHeight && lp.height != statusBarHeight) {
- //不預留系統空間
- ViewCompat.setFitsSystemWindows(mChildView, false);
- lp.topMargin += statusBarHeight;
- mChildView.setLayoutParams(lp);
- }
- }
- View statusBarView = mContentView.getChildAt(0);
- if (statusBarView != null && statusBarView.getLayoutParams() != null && statusBarView.getLayoutParams().height == statusBarHeight) {
- //避免重複呼叫時多次新增 View
- statusBarView.setBackgroundColor(statusColor);
- return;
- }
- statusBarView = new View(activity);
- ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, statusBarHeight);
- statusBarView.setBackgroundColor(statusColor);
- //向 ContentView 中新增假 View
- mContentView.addView(statusBarView, 0, lp);
方法2: 向 ContentParent 新增假 View ,設定 ContentView 和 ChildView 的 fitsSystemWindows.
全屏模式:
- Window window = activity.getWindow();
- window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
- ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
- ViewGroup mContentParent = (ViewGroup) mContentView.getParent();
- View statusBarView = mContentParent.getChildAt(0);
- if (statusBarView != null && statusBarView.getLayoutParams() != null && statusBarView.getLayoutParams().height == getStatusBarHeight(activity)) {
- //移除假的 View
- mContentParent.removeView(statusBarView);
- }
- //ContentView 不預留空間
- if (mContentParent.getChildAt(0) != null) {
- ViewCompat.setFitsSystemWindows(mContentParent.getChildAt(0), false);
- }
- //ChildView 不預留空間
- View mChildView = mContentView.getChildAt(0);
- if (mChildView != null) {
- ViewCompat.setFitsSystemWindows(mChildView, false);
- }
著色模式(會有一條黑線,無法解決):
- Window window = activity.getWindow();
- window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
- ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
- ViewGroup mContentParent = (ViewGroup) mContentView.getParent();
- View statusBarView = mContentParent.getChildAt(0);
- if (statusBarView != null && statusBarView.getLayoutParams() != null && statusBarView.getLayoutParams().height == getStatusBarHeight(activity)) {
- //避免重複呼叫時多次新增 View
- statusBarView.setBackgroundColor(statusColor);
- return;
- }
- //建立一個假的 View, 並新增到 ContentParent
- statusBarView = new View(activity);
- ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- getStatusBarHeight(activity));
- statusBarView.setBackgroundColor(statusColor);
- mContentParent.addView(statusBarView, 0, lp);
- //ChildView 不需要預留系統空間
- View mChildView = mContentView.getChildAt(0);
- if (mChildView != null) {
- ViewCompat.setFitsSystemWindows(mChildView, false);
- }
方法3:向 ContentView 新增假 View , 設定 ChildView 的 fitsSystemWindows.
全屏模式:
- Window window = activity.getWindow();
- window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
- ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
- View statusBarView = mContentView.getChildAt(0);
- //移除假的 View
- if (statusBarView != null && statusBarView.getLayoutParams() != null && statusBarView.getLayoutParams().height == getStatusBarHeight(activity)) {
- mContentView.removeView(statusBarView);
- }
- //不預留空間
- if (mContentView.getChildAt(0) != null) {
- ViewCompat.setFitsSystemWindows(mContentView.getChildAt(0), false);
- }
著色模式:
- Window window = activity.getWindow();
- window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
- ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
- int statusBarHeight = getStatusBarHeight(activity);
- View mTopView = mContentView.getChildAt(0);
- if (mTopView != null && mTopView.getLayoutParams() != null && mTopView.getLayoutParams().height == statusBarHeight) {
- //避免重複新增 View
- mTopView.setBackgroundColor(statusColor);
- return;
- }
- //使 ChildView 預留空間
- if (mTopView != null) {
- ViewCompat.setFitsSystemWindows(mTopView, true);
- }
- //新增假 View
- mTopView = new View(activity);
- ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, statusBarHeight);
- mTopView.setBackgroundColor(statusColor);
- mContentView.addView(mTopView, 0, lp);
其實全屏模式在三種模式下實現都是一樣的,主要是著色模式實現不同.
對比一下三種著色模式實現的方式:
方法1 | 方法2 | 方法3 | |
---|---|---|---|
原理 | 向 ContentView 中新增假 View, 然後利用 ChildView 的 marginTop 屬性來模擬 fitsSystemWindows ,主要是通過修改 marginTop 的值可以在全屏模式和著色模式之間切換. | 因為 ParentView 的實質是一個 LinearLayout , 可以再其頂部新增 View . | 向 ContentView 中新增假 View, 然後利用ChildView 的 fitsSystemWindows 屬性來控制位置, 但是實現缺陷就是不能隨時切換兩種模式. |
缺陷 | 改變了 ChildView 的 marginTop 值 | 著色模式下,會像由沉浸式狀態列引發的血案中一樣出現一條黑線 | 不能在不重啟 Activity 的情況下切換模式. |
對應 Github demo 中程式碼 | StatusBarCompat類 | StatusBarCompat1類 | StatusBarCompat2 類 |
總結
-
StatusBarCompat2 主要問題不能切換.
-
StatusBarCompat1 在4.4上會有一條黑線, 如果可以解決我覺得這是最靠譜的解決方法.
-
StatusBarCompat 類算是我最後給出的解決方案吧, 目前使用效果比較完善.推薦使用
-
使用者可以隨時在同一個 Activity 中切換不同的狀態列模式.
-
就運算元 View 重寫了 dispatchFitSystemWindows 也不會有影響.
-
推薦乾貨集中營Android客戶端,實現了沉浸式狀態列,無縫換膚,帶3D感覺的側滑選單
相關文章
- Flutter改變狀態列字型、狀態列背景顏色、Appbar背景顏色的方式FlutterAPP
- Android的標題欄,狀態列圖示文字顏色及背景動態變化Android
- Android 沉浸式狀態列 漸變顏色的實現Android
- 直播軟體搭建,狀態列顏色跟隨裝置系統變化而變化
- 直播系統搭建,設定透明背景(去掉蒙層)狀態列顏色不改變
- 修改狀態列與導航欄顏色
- 直播軟體搭建,修改狀態列、導航條顏色及文字顏色
- Android 狀態列透明Android
- 短視訊直播系統,Android狀態列設定顏色字型Android
- 搭建自己的直播平臺,更改狀態列的字型顏色
- 線上直播系統原始碼,iOS 根據滑動距離改變狀態列顏色原始碼iOS
- 狀態列
- 線上直播原始碼,單獨修改狀態列顏色原始碼
- Android獲取狀態列高度Android
- react-native android狀態列ReactAndroid
- Android全屏與透明狀態列Android
- Android 沉浸式狀態列的實現Android
- win10怎麼把狀態列變透明_win10狀態列變透明方法Win10
- Android 隱藏系統狀態列Android
- 一行程式碼使Android狀態列變沉浸式透明化行程Android
- Android-沉浸式狀態列的實現Android
- android判斷狀態列是否可見Android
- 沉浸式狀態列
- 狀態列相關
- 關於 Android 狀態列的適配總結Android
- React Native 中的狀態列React Native
- React Native Modal元件 Android覆蓋狀態列React Native元件Android
- 使用列舉實現狀態機來優雅你的狀態變更邏輯
- 狀態變化模式模式
- win10狀態列偏暗怎麼調節_win10工作列顏色很暗的調節方法Win10
- 05@多用列舉表示狀態、選項、狀態碼
- 【Android】狀態列通知Notification、NotificationManager詳解(轉載)Android
- Android開發——Java程式碼動態改變顏色字型的方法AndroidJava
- HarmonyOS NEXT應用開發案例—狀態列顯隱變化
- 直播軟體原始碼,改變button的背景顏色原始碼
- 狀態列Theme相關配置
- PyQt5 之狀態列QT
- iOS狀態列相關操作iOS
- HTTP狀態碼列舉(PHP)HTTPPHP