前段時間,忽然收到一個狀態欄顏色優化設計的任務,將本來應用總體的黑色狀態欄修改成根據標題欄顏色進行沉浸式設計,顯示效果以下: android
通過分析及踩過N多坑,終於完成了APP全局的修改。現將一些須要注意的問題及踩過的坑進行梳理總結,主要從系統版本區別、各大廠商的ROM區別及具體的設置進行分析,期間也參考了不少資料,會在文末附上對應的連接git
首先咱們須要注意,Android不是各個版本都支持設置狀態欄的顏色,只有在5.0以上才支持。另外6.0以上才支持設置狀態欄黑色圖標(避免白色狀態欄及白色圖標緻使看不清電量 時間等問題)github
系統版本 | 是否支持設置狀態欄顏色 | 是否容許設置狀態欄黑色圖標 |
---|---|---|
4.4 | 否 | 否 |
5.0 | 是 | 否 |
6.0+ | 是 | 是 |
這個問題一開始也困擾了我,後面分析,在原生的系統雖然設置了狀態欄透明,可是狀態欄區域也會有一層半透明的遮罩(估計就是考慮到白色狀態欄引發的問題),可是測試發現部分國產ROM設置穿透欄透明則會徹底透明(例如MIUI)bash
原生系統效果以下: 微信
MIUI系統效果以下: ide
原生6.0以上有API支持,可是國產各ROM通過定製,有的須要特定的設置才能實現post
原生系統設置:測試
public void setLightStatusBar(Window window, boolean lightStatusBar) {
// 設置淺色狀態欄時的界面顯示
View decor = window.getDecorView();
int ui = decor.getSystemUiVisibility();
if (lightStatusBar) {
ui |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
} else {
ui &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
}
decor.setSystemUiVisibility(ui);
}
複製代碼
小米:字體
public static boolean MIUISetStatusBarLightMode(Window window, boolean dark) {
boolean result = false;
if (window != null) {
Class clazz = window.getClass();
try {
int darkModeFlag = 0;
Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
if (dark) {
extraFlagField.invoke(window, darkModeFlag, darkModeFlag);//狀態欄透明且黑色字體
} else {
extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字體
}
result = true;
} catch (Exception e) {
}
}
return result;
}
複製代碼
魅族:優化
public static boolean FlymeSetStatusBarLightMode(Window window, boolean dark) {
boolean result = false;
if (window != null) {
try {
WindowManager.LayoutParams lp = window.getAttributes();
Field darkFlag = WindowManager.LayoutParams.class
.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
Field meizuFlags = WindowManager.LayoutParams.class
.getDeclaredField("meizuFlags");
darkFlag.setAccessible(true);
meizuFlags.setAccessible(true);
int bit = darkFlag.getInt(null);
int value = meizuFlags.getInt(lp);
if (dark) {
value |= bit;
} else {
value &= ~bit;
}
meizuFlags.setInt(lp, value);
window.setAttributes(lp);
result = true;
} catch (Exception e) {
}
}
return result;
}
複製代碼
華爲手機: 部分測試發現華爲的EMUI手機狀態欄會跟系統桌面的狀態欄同樣,設置了沒用,這裏若是要特殊設置狀態欄顏色,只能參考4.4的處理方式(後續介紹)
經過上述的版本及分析,可見完善的的狀態欄兼容是一個大工程,須要綜合考慮系統版本及各個廠商ROM等因素。5.0以上有系統API進行支持,這裏咱們主要來分析一些4.4的實現原理。 簡單來講,4.4的實現方式就是使用透明的狀態欄,而後作一個和狀態欄同樣高度的View,加入到Windows的DecorView,而後給這個View設置背景色,達到實現狀態欄顏色。
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
ViewGroup decorViewGroup = (ViewGroup) window.getDecorView();
View statusBarView = decorViewGroup.findViewWithTag(STATUS_BAR_VIEW_TAG);
if (statusBarView == null) {
statusBarView = new StatusBarView(window.getContext());
statusBarView.setTag(STATUS_BAR_VIEW_TAG);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.TOP;
statusBarView.setLayoutParams(params);
decorViewGroup.addView(statusBarView);
}
statusBarView.setBackgroundColor(color);
StatusBarCompat.internalSetFitsSystemWindows(window, true);
複製代碼
注意5.0通常不用使用白色的狀態欄(由於不能設置狀態欄灰色圖標),可在資源文件定義一個rgb,區分版本,5.0使用白灰色
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void setStatusBarColor(Window window, int color) {
//取消設置透明狀態欄,使 ContentView 內容再也不覆蓋狀態欄
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//須要設置這個 flag 才能調用 setStatusBarColor 來設置狀態欄顏色
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//設置狀態欄顏色
window.setStatusBarColor(color);
}
複製代碼
@TargetApi(Build.VERSION_CODES.M)
@Override
public void setStatusBarColor(Window window, int color) {
//取消設置透明狀態欄,使 ContentView 內容再也不覆蓋狀態欄
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//須要設置這個 flag 才能調用 setStatusBarColor 來設置狀態欄顏色
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//設置狀態欄顏色
window.setStatusBarColor(color);
// 去掉系統狀態欄下的windowContentOverlay
View v = window.findViewById(android.R.id.content);
if (v != null) {
v.setForeground(null);
}
}
複製代碼
這裏不重複造輪子,先提供一下github上比較完善的處理方案
StatusBarCompat是一個用於設置系統狀態欄顏色的兼容庫,兼容Android 4.4.2(API 19)以上,使用簡單,僅須要一行代碼的調用。
支持4.4以上的,主要使用透明狀態欄的方式實現
推薦使用status-bar-compat,已考慮到總體的版本兼容機及各廠ROM,調用簡單。
在Activity的setContentView()方法調用以後,調用如下方法便可。
StatusBarCompat.setStatusBarColor(this, color, lightStatusBar);
或者是
StatusBarCompat.setStatusBarColor(this, color);
複製代碼
本文主要源碼使用status-bar-compat中的代碼進行說明
例如在應用中有全屏的看圖頁面,點擊返回爲非全屏(帶狀態欄)頁面,非全屏頁面因爲現實狀態,會出現頁面抖動。目前暫無完善的處理方案,項目中暫時使用的方式是延遲全屏頁面的finish,先顯示狀態欄後再關閉
複寫onBackPressed
getActivity().getWindow().clearFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN);
if(getView()!=null){
getView().postDelayed(new Runnable() {
@Override
public void run() {
getActivity().finish();
}
},10);
}
複製代碼
上面已有分析,要注意若是狀態欄爲白色,須要設置狀態欄的圖標顏色。status-bar-compat中會把顏色轉換成灰度值,而後本身控制狀態欄圖標顏色
public static void setStatusBarColor(Activity activity, @ColorInt int color) {
boolean isLightColor = toGrey(color) > 225;
setStatusBarColor(activity, color, isLightColor);
}
/**
* 把顏色轉換成灰度值。
* 代碼來自 Flyme 示例代碼
*/
public static int toGrey(@ColorInt int color) {
int blue = Color.blue(color);
int green = Color.green(color);
int red = Color.red(color);
return (red * 38 + green * 75 + blue * 15) >> 7;
}
複製代碼
這個目前也尚無方法,考慮能夠在彈層出現時,動態修改狀態的顏色,可是工做量比較大,可先適當調整彈層的rgb,減低透明度
歡迎關注個人我的公衆號
微信搜索:一碼一浮生,或者搜索公衆號ID:life2code