前段时间,忽然收到一个状态栏颜色优化设计的任务,将本来应用总体的黑色状态栏修改成根据标题栏颜色进行沉浸式设计,显示效果以下: 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