Android 浅色状态栏设置

前言

转载说明:本文转载自Loyea 的个人博客

我们都知道,安卓可以自定义状态栏是从 API19(4.4) 开始的,Google 给 KitKat 引入了 translucentStatusBar 特性。从此,安卓也可以像 iOS 一样自定义状态栏的颜色,甚至可以将布局布到状态栏后面。然而,一如往常谷歌出的东西都是坑的尿性,谷歌却没考虑到将状态栏设置为浅色后,状态栏上的文字和图标辨识困难的问题。(这不叫黑,这叫爱的深沉)

直到 Google 在 2015 年的 Google I/O 大会上推出了一个安卓状态栏的新特性:LightStatusBarWindow,即所谓的浅色状态栏模式。从 API19 到 API23 过了整整四个大版本,这才解决了这个问题。没错,重要的东西总是晚来一步。

瞎扯淡

然而,早在谷歌之前,国内的魅族与小米早已在自家的 Rom 中添加了浅色状态栏的功能。最低甚至支持 4.4 。但是很多安卓开发者并不知道此事,以至于很多 app 使用了浅色的状态栏导致用户无法看清状态栏上的文字图标。

浅色状态栏

左图是数字尾巴,正确使用了浅色状态栏模式。右图是某知名P2P app,未使用浅色状态栏模式,右上角的充电图标证明了该 app 并不是全屏状态而是的确没用浅色状态栏

实现

MIUI、Flyme以及原生安卓的浅色状态栏实现各不相同。而且这三个实现该功能的最低版本要求也不同。MIUI 的要求为 MIUI V6 及以上,Flyme 为 Flyme4 及以上,原生安卓为 API23 及以上。考虑到国内 MIUI 及 Flyme 用户的数量,所以我们的 app 中势必需要对当前手机系统进行判断之后调用合适的方法来设置浅色状态栏。

以下是我总结的判断手机系统浅色状态栏是否可用的工具类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* Created by Loyea.com on 7月20日.
*/
public class RomUtils {
class AvailableRomType {
public static final int MIUI = 1;
public static final int FLYME = 2;
public static final int ANDROID_NATIVE = 3;
public static final int NA = 4;
}
public static boolean isLightStatusBarAvailable () {
if (isMIUIV6OrAbove() || isFlymeV4OrAbove() || isAndroidMOrAbove()) {
return true;
}
return false;
}
public static int getLightStatausBarAvailableRomType() {
if (isMIUIV6OrAbove()) {
return AvailableRomType.MIUI;
}
if (isFlymeV4OrAbove()) {
return AvailableRomType.FLYME;
}
if (isAndroidMOrAbove()) {
return AvailableRomType.ANDROID_NATIVE;
}
return AvailableRomType.NA;
}
//Flyme V4的displayId格式为 [Flyme OS 4.x.x.xA]
//Flyme V5的displayId格式为 [Flyme 5.x.x.x beta]
private static boolean isFlymeV4OrAbove() {
String displayId = Build.DISPLAY;
if (!TextUtils.isEmpty(displayId) && displayId.contains("Flyme")) {
String[] displayIdArray = displayId.split(" ");
for (String temp : displayIdArray) {
//版本号4以上,形如4.x.
if (temp.matches("^[4-9]\\.(\\d+\\.)+\\S*")) {
return true;
}
}
}
return false;
}
//MIUI V6对应的versionCode是4
//MIUI V7对应的versionCode是5
private static boolean isMIUIV6OrAbove() {
String miuiVersionCodeStr = getSystemProperty("ro.miui.ui.version.code");
if (!TextUtils.isEmpty(miuiVersionCodeStr)) {
try {
int miuiVersionCode = Integer.parseInt(miuiVersionCodeStr);
if (miuiVersionCode >= 4) {
return true;
}
} catch (Exception e) {}
}
return false;
}
//Android Api 23以上
private static boolean isAndroidMOrAbove() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return true;
}
return false;
}
private static String getSystemProperty(String propName) {
String line;
BufferedReader input = null;
try {
Process p = Runtime.getRuntime().exec("getprop " + propName);
input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
line = input.readLine();
input.close();
} catch (IOException ex) {
return null;
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
}
}
}
return line;
}
}

当判断当前系统可以使用浅色状态栏可以用后,根据得到的 Rom 类型调用对应的设置浅色状态栏方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/**
* Created by Loyea.com on 7月20日.
*/
public class LightStatusBarUtils {
public static void setLightStatusBar(Activity activity, boolean dark) {
switch (RomUtils.getLightStatausBarAvailableRomType()) {
case RomUtils.AvailableRomType.MIUI:
setMIUILightStatusBar(activity, dark);
break;
case RomUtils.AvailableRomType.FLYME:
setFlymeLightStatusBar(activity, dark);
break;
case RomUtils.AvailableRomType.ANDROID_NATIVE:
setAndroidNativeLightStatusBar(activity, dark);
break;
case RomUtils.AvailableRomType.NA:
// N/A do nothing
break;
}
}
private static boolean setMIUILightStatusBar(Activity activity, boolean darkmode) {
Class<? extends Window> clazz = activity.getWindow().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);
extraFlagField.invoke(activity.getWindow(), darkmode ? darkModeFlag : 0, darkModeFlag);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
private static boolean setFlymeLightStatusBar(Activity activity, boolean dark) {
boolean result = false;
if (activity != null) {
try {
WindowManager.LayoutParams lp = activity.getWindow().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);
activity.getWindow().setAttributes(lp);
result = true;
} catch (Exception e) {
}
}
return result;
}
private static void setAndroidNativeLightStatusBar(Activity activity, boolean dark) {
View decor = activity.getWindow().getDecorView();
if (dark) {
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
} else {
// We want to change tint color to white again.
// You can also record the flags in advance so that you can turn UI back completely if
// you have set other flags before, such as translucent or full screen.
decor.setSystemUiVisibility(0);
}
}
}

以上就是我总结的用于设置浅色状态栏的两个主要类:RomUtils 用于判定当前系统是否可用浅色状态栏模式,以及当前系统的类型。LightStatusBarUtils 包含了各个系统对应的设置浅色状态栏的方法,开发者可以根据 RomUtils 得到的 Rom 类型调用其中对应的方法。

效果

接着我们创建一个 app 项目,来试试效果。在 MainActivity 的布局中我放了两个 TextView 和一个 Button。两个 TextView 分别用于显示当前系统版本信息和显示当前系统是否可用浅色状态栏模式。Button 用于在浅色状态栏模式和普通模式之间切换。

以下是 MainActivity :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class MainActivity extends AppCompatActivity {
private boolean isLightStatusBarNow = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView sysInfoTv = (TextView) findViewById(R.id.tv_sys_info);
TextView hintTv = (TextView) findViewById(R.id.tv_hint);
Button switchBtn = (Button) findViewById(R.id.btn_switch);
sysInfoTv.setText(Build.BRAND + " - " + Build.MODEL + " - SDK Version:" + Build.VERSION.SDK_INT);
switch (RomUtils.getLightStatausBarAvailableRomType()) {
case RomUtils.AvailableRomType.MIUI:
hintTv.setText("当前系统为MIUI6或以上 浅色状态栏可用");
break;
case RomUtils.AvailableRomType.FLYME:
hintTv.setText("当前系统为Flyme4或以上 浅色状态栏可用");
break;
case RomUtils.AvailableRomType.ANDROID_NATIVE:
hintTv.setText("当前系统为Android M或以上 浅色状态栏可用");
break;
case RomUtils.AvailableRomType.NA:
hintTv.setText("当前系统浅色状态栏不可用");
switchBtn.setEnabled(false);
switchBtn.setText("light status bar mode not available");
break;
}
switchBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isLightStatusBarNow) {
LightStatusBarUtils.setLightStatusBar(MainActivity.this, false);
isLightStatusBarNow = false;
} else {
LightStatusBarUtils.setLightStatusBar(MainActivity.this, true);
isLightStatusBarNow = true;
}
}
});
}
}

手机上运行效果:

浅色状态栏运行效果

一些常见机型上的效果:

浅色状态栏运行效果

存在的坑

使用原生安卓的方法来实现浅色状态栏的前提是,当前 Activity 所在的 Window 必须有 windowDrawsSystemBarBackgrounds 属性,而且必须未设置过 windowTranslucentStatus 属性。
如果当前 Window 设置了 windowTranslucentStatus 属性,那么调用上面设置浅色状态栏的方法就不会生效。

详细见 API 说明:
https://developer.android.com/reference/android/R.attr.html#windowLightStatusBar

据说安卓原生实现方法在 MIUI android 版本为6.0上无效(见参考2,手头没设备测试)。所以我的代码中将 MIUI 和 Flyme 的判断放在原生安卓前面,这样如果是 MIUI 或 Flyme 的设备,优先调用各自的实现方法。

Demo代码

https://github.com/Loyea/LightStatusBarModeDemo

参考

  1. http://blog.isming.me/2016/01/09/chang-android-statusbar-text-color/
  2. http://www.jianshu.com/p/7f5a9969be53
  3. http://dev.xiaomi.com/doc/?p=254
  4. http://blog.csdn.net/devilkin64/article/details/19415717

评论

Ajax Android AndroidStudio Animation Anroid Studio AppBarLayout Babel Banner Buffer Bulma ByteBuffer C++ C11 C89 C99 CDN CMYK COM1 COM2 CSS Camera Raw, 直方图 Chrome Class ContentProvider CoordinatorLayout C语言 DML DOM Dagger Dagger2 Darktable Demo Document DownloadManage ES2015 ESLint Element Error Exception Extensions File FileProvider Flow Fresco GCC Git GitHub GitLab Gradle Groovy HTML5 Handler HandlerThread Hexo Hybrid I/O IDEA IO ImageMagick IntelliJ Intellij Interpolator JCenter JNI JS Java JavaScript JsBridge Kotlin Lab Lambda Lifecycle Lint Linux Looper MQTT MVC MVP Maven MessageQueue Modbus Momentum MySQL NDK NIO NexT Next Nodejs ObjectAnimator Oracle VM Permission PhotoShop Physics Python RGB RS-232 RTU Remote-SSH Retrofit Runnable RxAndroid RxJava SE0 SSH Spring SpringBoot Statubar Style Task Theme Thread Tkinter UI UIKit UML VM virtualBox VS Code VUE ValueAnimator ViewPropertyAnimator Vue Vue.js Web Web前端 Workbench api apk bookmark by关键字 cli compileOnly computed css c语言 databases demo hexo hotfix html iOS icarus implementation init jQuery javascript launchModel logo merge methods mvp offset photos pug query rxjava2 scss servlet shell svg tkinter tomcat transition unicode utf-8 vector virtual box vscode watch webpack 七牛 下载 中介者模式 串口 临潼石榴 主题 书签 事件 享元模式 仓库 代理模式 位运算 依赖注入 修改,tables 光和色 内存 内核 内部分享 函数 函数式编程 分支 分析 创建 删除 动画 单例模式 压缩图片 发布 可空性 合并 同向性 后期 启动模式 命令 命令模式 响应式 响应式编程 图层 图床 图片压缩 图片处理 图片轮播 地球 域名 基础 增加 备忘录模式 外观模式 多线程 大爆炸 天气APP 太白山 头文件 奇点 字符串 字符集 存储引擎 宇宙 宏定义 实践 属性 属性动画 岐山擀面皮 岐山肉臊子 岐山香醋 工具 工厂模式 年终总结 开发技巧 异常 弱引用 恒星 打包 技巧 指令 指针 插件 插值 摄影 操作系统 攻略 故事 数据库 数据类型 数组 文件 新功能 旅行 旋转木马 时序图 时空 时间简史 曲线 杂谈 权限 枚举 架构 查询 标准库 标签选择器 样式 核心 框架 案例 桥接模式 检测工具 模块化 模板 模板引擎 模板方法模式 油泼辣子 泛型 洛川苹果 浅色状态栏 渲染 源码 源码分析 瀑布流 热修复 版本 版本控制 状态栏 状态模式 生活 留言板 相册 相对论 眉县猕猴桃 知识点 码云 磁盘 科学 笔记 策略模式 类图 系统,发行版, GNU 索引 组件 组合模式 绑定 结构 结构体 编码 网易云信 网格布局 网站广播 网站通知 网络 美化 联合 脚手架 膨胀的宇宙 自定义 自定义View 自定义插件 蒙版 虚拟 虚拟机 补码 补齐 表单 表达式 装饰模式 西安 观察者模式 规范 视图 视频 解耦器模式 设计 设计原则 设计模式 访问者模式 语法 责任链模式 贪吃蛇 转换 软件工程 软引用 运算符 迭代子模式 适配器模式 选择器 通信 通道 配置 链表 锐化 错误 键盘 闭包 降噪 陕西地方特产 面向对象 项目优化 项目构建 黑洞
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×