我尽量不打错别字,用词准确,不造成阅读障碍。
昨天在OPPO开发者后台清理未读消息时看到一篇介绍状态栏反色(白底黑字)的文章,心血来潮看了一下,然后就查阅了国内各个ROM的不同处理方式,写一篇小总结,希望能帮助到需要的人。
Android6.0(M)以后,Google给出了官方修改状态栏白底黑字的方式 ,各大厂商基本都适用,所以应该不用适配 (“应该”是因为我没有那么多手机,没法逐个确认),在6.0之前的系统,国内不同厂家有的提供了实现方法,有的没有。比如我查阅到的几家,MIUI6开始提供了方法,OPPO的Color3.0开始且Android5.1提供了方法,魅族Flyme4.0提供了方法,华为开发者文档里没查到,vivo开发者文档里没查到,论坛里有说是Android5.0以后才提供沉浸式状态栏设置,其他手机没查了比如三星、索尼什么的(其实是因为没有测试机)。
注意:个人觉得状态栏基本有三个修改点:1.透明,全屏 ;2.改背景颜色与主题适配(感觉和上一个差不多);3.白底黑字或黑底白字;这三个修改点Google给出的官方方法时间不一样,前两个5.0就可以了,如果是4.4,网上也有很多实现的方法和思路,最后一个官方在6.0以后才给出方法,所以项目中写状态栏工具的时候还是要考虑很多东西的。
我手里的测试机有
华为荣耀6P:Android6.0 & EMUI4.0.1;//由于华为手机没有文档,所以还是很有测试价值的
红米R3s:Android6.0 & MIUI9; //MIUI6实在找不到,那得什么年代的机子了?
OPPOR9m:Android5.1 & Color3.0; //6.0以下的只有这一部
小米8:Android8.1.0 & MIUI10.0;
各种模拟器;
虽然设备条件不足,但是还是有一些可取之处的,所以只希望能提供一些帮助。
效果四连:
主要就是改成黑色字体,背景其实好改很多。
代码如下:
public static void setStatusColor(Activity activity, boolean isTranslate, boolean isDarkText, @ColorRes int bgColor) {
//如果系统为6.0及以上,就可以使用Android自带的方式
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Window window = activity.getWindow();
View decorView = window.getDecorView();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); //可有可无
decorView.setSystemUiVisibility((isTranslate ? View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN : 0) | (isDarkText ? View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR : 0));
window.setStatusBarColor(isTranslate ? Color.TRANSPARENT : ContextCompat.getColor(activity, bgColor)); //Android5.0就可以用
}
}
在setSystemUiVisibility里面设置就可以分别实现全屏和文字黑色;
小米文档写的是MIUI全面支持原生写法,MIUI中Android6.0及以上用原生的,MIUI6以上Android6.0以下用MIUI自己的,所以你得写两套。首先要判断是否是MIUI6以后的版本:
private static final String KEY_VERSION_MIUI = "ro.miui.ui.version.name";
private static boolean isMIUI6Later() {
try {
Class<?> cla = Class.forName("android.os.SystemProperties");
Method mtd = cla.getMethod("get", String.class);
String val = (String) mtd.invoke(null, KEY_VERSION_MIUI);
val = val.replaceAll("[vV]", "");
int version = Integer.parseInt(val);
return version >= 6;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
网上还有一种获取厂商自定义系统的方法,具体忘记了,那个方法很耗时,几百毫秒左右,使用反射一般就1~2毫秒。
根据官方文档,设置透明和设置状态栏反色是两个步骤:
//设置透明
private static void setMIUI6Translate(Activity activity, boolean on) {
Window win = activity.getWindow();
WindowManager.LayoutParams winParams = win.getAttributes();
final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
if (on) {
winParams.flags |= bits;
} else {
winParams.flags &= ~bits;
}
win.setAttributes(winParams);
}
//设置黑色字符
private static void setMIUI6StatusBarDarkMode(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);
} catch (Exception e) {
e.printStackTrace();
}
}
由于我没有MIUI6以上&Android6.0以下的手机,所以没有测试,但是这是官方文档给的方法,应该是有保证的。
链接:https://dev.mi.com/console/doc/detail?pId=1159
官方还给了实现白色字符的方式:
int flag =window.getDecorView().getSystemUiVisibility()&~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
window.getDecorView().setSystemUiVisibility(flag);
官方只给了Color3.0&Android5.1的情况,其它的也没说不行或行,是不是Color3.0以上Android6.0以下只有这一种机器?先判断系统是不是Color3.0以上的,与判断MIUI6的方法一样:
private static final String KEY_VERSION_OPPO = "ro.build.version.opporom";
private static boolean isColorOS_3() {
try {
Class<?> cla = Class.forName("android.os.SystemProperties");
Method mtd = cla.getMethod("get", String.class);
String val = (String) mtd.invoke(null, KEY_VERSION_OPPO);
val = val.replaceAll("[vV]", "");
val = val.substring(0, 1);
int version = Integer.parseInt(val);
return version >= 3;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
设置字体颜色:
private void setOppoStausBar(){
//控制字体颜色,只有黑白两色
int SYSTEM_UI_FLAG_OP_STATUS_BAR_TINT = isDarkText ? 0x00000010 : 0x00190000;
Window window = activity.getWindow();
View decorView = window.getDecorView();
decorView.setSystemUiVisibility((isTranslate ? View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN : 0) | SYSTEM_UI_FLAG_OP_STATUS_BAR_TINT);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); //可有可无
window.setStatusBarColor(isTranslate ? Color.TRANSPARENT : ContextCompat.getColor(activity, bgColor));
}
可以看出来,OPPO的修改与Android6.0以上的修改唯一不同的就是setSystemUiVisibility中设置字符颜色的标识是自己手写的,不是使用系统的,官方给的是0x00000010,这个数值我随便输了几个,发现只有黑白两色。
开发者链接:https://open.oppomobile.com/wiki/doc#id=10161
魅族我没有机子,都是在官方的基础上归纳的:判断是否为Flyme4.0:
private static boolean isFlyme4Later() {
if ("MEIZU".equals(Build.BRAND.trim().toUpperCase())) {
return Build.FINGERPRINT.contains("Flyme_OS_4") || Build.VERSION.INCREMENTAL.contains("Flyme_OS_4") || Pattern.compile("Flyme_OS_[4|5]", Pattern.CASE_INSENSITIVE)
.matcher(Build.DISPLAY).find();
}
return false;
}
郑重说明,我没有机子,这个方法是网上找的,我本来一位可以使用判断小米和OPPO一样的方法,但是不行,没找到KEY_VERSION,没有机子也测不了。
设置字体颜色:
private static void darModeForFlyme4(Activity activity, boolean dark) {
boolean result = false;
try {
WindowManager.LayoutParams e = 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(e);
if (dark) {
value |= bit;
} else {
value &= ~bit;
}
meizuFlags.setInt(e, value);
activity.getWindow().setAttributes(e);
} catch (Exception var8) {
Log.e("StatusBar", "darkIcon: failed");
}
}
以上是个整合版本,官方给了一个工具类,链接:http://open-wiki.flyme.cn/doc-wiki/index#id?79
吐槽一句,魅族真麻烦,周围连个测试机都没有。
包括没找到文档的华为、VIVO,没找文档的三星、索尼、诺基亚、锤子、360等
设置全屏透明及背景色:
public static void setStatusTranslate(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
View decorView = activity.getWindow().getDecorView();
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
} else {
//感觉5.0以下(4.4及以下)可以放弃了
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
public static void setStatusBarColor(Activity activity, int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().setStatusBarColor(activity.getResources().getColor(color));
}
}
因为没有文档,所以没有找到设置状态栏反色的方法,如果是Android6.0及以上,以上品牌应该是都可以使用原生方法的。
本文旨在提供一些帮助,因为测试机有限,所以可能帮助的不多,你也可以再次基础上进行添加等修改,以符合项目要求。完整代码如下:
public class StatusBarUtil {
private static final String KEY_VERSION_OPPO = "ro.build.version.opporom"; //OPPO
private static final String KEY_VERSION_MIUI = "ro.miui.ui.version.name"; //小米
private static final String KEY_VERSION_EMUI = "ro.build.version.emui"; //华为
private static final String KEY_VERSION_SMARTISAN = "ro.smartisan.version"; //锤子
private static final String KEY_VERSION_VIVO = "ro.vivo.os.version"; //vivo
/**
* 设置状态栏透明度、背景颜色、文字颜色
* @param isTranslate 是否透明,若为true,则bgColor设置无效
* @param isDarkText 字体颜色,只有黑白两色,无论什么色值,都只会转为黑白两色
* @param bgColor 背景色,即状态栏颜色,isTranslate若为true则此值无效
*/
public static void setStatusColor(Activity activity, boolean isTranslate, boolean isDarkText, @ColorRes int bgColor) {
//如果系统为6.0及以上,就可以使用Google自带的方式
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Window window = activity.getWindow();
View decorView = window.getDecorView();
//可有可无
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
decorView.setSystemUiVisibility((isTranslate ? View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN : 0) | (isDarkText ? View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR : 0));
window.setStatusBarColor(isTranslate ? Color.TRANSPARENT : ContextCompat.getColor(activity, bgColor));
} else { //如果不是6.0及以上则分情况适配
if (isColorOS_3()) { //如果是OPPO Color3.0 & Android 5.1
//控制字体颜色,只有黑白两色
final int SYSTEM_UI_FLAG_OP_STATUS_BAR_TINT = isDarkText ? 0x00000010 : 0x00190000;
Window window = activity.getWindow();
View decorView = window.getDecorView();
decorView.setSystemUiVisibility((isTranslate ? View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN : 0) | SYSTEM_UI_FLAG_OP_STATUS_BAR_TINT);
//可有可无
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(isTranslate ? Color.TRANSPARENT : ContextCompat.getColor(activity, bgColor));
} else if (isMIUI6Later()) { //如果是Android 6.0以下,MIUI6及以上
setMIUI6Translate(activity, isTranslate);
setMIUI6StatusBarDarkMode(activity, isDarkText);
} else if (isFlyme4Later()) {
darModeForFlyme4(activity, isDarkText);
if (isTranslate) {
setStatusTranslate(activity);
}
} else {
//既不属于MIUI6,也不属于OPPO,还不属于魅族,极有可能是华为、三星、索尼、诺基亚、VIVO、锤子、360等
if (isTranslate) {
setStatusTranslate(activity);
}
setStatusBarColor(activity, isTranslate ? Color.TRANSPARENT : ContextCompat.getColor(activity, bgColor));
}
}
}
/**
* 是否是MIUI6及以后版本
* @return 是否是MIUI6
*/
private static boolean isMIUI6Later() {
try {
Class<?> cla = Class.forName("android.os.SystemProperties");
Method mtd = cla.getMethod("get", String.class);
String val = (String) mtd.invoke(null, KEY_VERSION_MIUI);
val = val.replaceAll("[vV]", "");
int version = Integer.parseInt(val);
return version >= 6;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 设置MIUI6及以上状态栏透明,字体为默认白色,Android6.0以上也可以
* @param on 是否为透明
*/
private static void setMIUI6Translate(Activity activity, boolean on) {
Window win = activity.getWindow();
WindowManager.LayoutParams winParams = win.getAttributes();
final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
if (on) {
winParams.flags |= bits;
} else {
winParams.flags &= ~bits;
}
win.setAttributes(winParams);
}
/**
* 设置MIUI6及以上,Android6.0以下版本状态栏黑色字符
* Android 6.0以上此方法无效
* 此方法是官方给的,应该没有错,我没有MIUI6的手机,无法测试
*/
private static void setMIUI6StatusBarDarkMode(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);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 是否是ColorOS_3.0系统 Android 5.1
* @return 是否是colorOS_3.0
*/
private static boolean isColorOS_3() {
try {
Class<?> cla = Class.forName("android.os.SystemProperties");
Method mtd = cla.getMethod("get", String.class);
String val = (String) mtd.invoke(null, KEY_VERSION_OPPO);
val = val.replaceAll("[vV]", "");
val = val.substring(0, 1);
int version = Integer.parseInt(val);
return version >= 3;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 判断是否是魅族Flyme4
* @return 是否是魅族Flyme4
*/
private static boolean isFlyme4Later() {
if ("MEIZU".equals(Build.BRAND.trim().toUpperCase())) {
return Build.FINGERPRINT.contains("Flyme_OS_4") || Build.VERSION.INCREMENTAL.contains("Flyme_OS_4") || Pattern.compile("Flyme_OS_[4|5]", Pattern.CASE_INSENSITIVE)
.matcher(Build.DISPLAY).find();
}
return false;
}
/**
* 设置魅族Flyme4以后 状态栏黑色字体
* 没有手机,从网上找的方法,应该没问题
* @param dark 是否黑色
*/
private static void darModeForFlyme4(Activity activity, boolean dark) {
boolean result = false;
try {
WindowManager.LayoutParams e = 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(e);
if (dark) {
value |= bit;
} else {
value &= ~bit;
}
meizuFlags.setInt(e, value);
activity.getWindow().setAttributes(e);
} catch (Exception var8) {
Log.e("StatusBar", "darkIcon: failed");
}
}
/**
* 设置状态栏透明,Android4.4、Android5.0以上方法不一
* 我感觉5.0以下的系统已经很少了吧,应该不需要适配了吧
* 如果只是设置状态栏透明,就使用这个就好了
*/
public static void setStatusTranslate(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
View decorView = activity.getWindow().getDecorView();
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
} else {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
/**
* 设置状态栏背景颜色,不适配5.0以下系统
* @param color 颜色值
*/
public static void setStatusBarColor(Activity activity, int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().setStatusBarColor(activity.getResources().getColor(color));
}
}
}