对于状态栏相关适配这个事情,真是让人头疼的一个模块。因为负责的项目主题色偏偏是白色,不但要去适配 MIUI ,Flyme(因为这两个都可以实现沉浸式,并且图标可以切换成黑色),也要分别适配 Android 6.0 以下, Android 6.0 起两种不同情况(6.0 起原生提供了高亮状态栏模式,该模式下状态栏图标可以切换成黑色)。一大堆坑,坑到自己都怕......
现在就把一些相关实用的方法、注意点汇集起来,并推荐一些相关实用的库。
首先放代码:
BuildProperties.java
public class BuildProperties {
private final Properties properties;
private BuildProperties() throws IOException {
properties = new Properties();
properties.load(new FileInputStream(new File(Environment.getRootDirectory(), "build.prop")));
}
public boolean containsKey(final Object key) {
return properties.containsKey(key);
}
public boolean containsValue(final Object value) {
return properties.containsValue(value);
}
public Set> entrySet() {
return properties.entrySet();
}
public String getProperty(final String name) {
return properties.getProperty(name);
}
public String getProperty(final String name, final String defaultValue) {
return properties.getProperty(name, defaultValue);
}
public boolean isEmpty() {
return properties.isEmpty();
}
public Enumeration
public class OSHelper {
private static final String KEY_MIUI_VERSION_CODE = "ro.miui.ui.version.code";
private static final String KEY_MIUI_VERSION_NAME = "ro.miui.ui.version.name";
private static final String KEY_MIUI_INTERNAL_STORAGE = "ro.miui.internal.storage";
private static boolean isTranslucentStatusMiUi;
private static boolean isFlyMe;
static {
isTranslucentStatusMiUi = isTranslucentStatusMiUiVersion();
isFlyMe = isFlyMeOS();
}
//是否是支持沉浸式状态栏的 MiUi
public static boolean isTranslucentStatusMiUi() {
return isTranslucentStatusMiUi;
}
//判断是否是支持沉浸式状态栏(以及状态栏图标变黑色)的 MiUi 版本
private static boolean isTranslucentStatusMiUiVersion() {
try {
Class> sysClass = Class.forName("android.os.SystemProperties");
Method getStringMethod = sysClass.getDeclaredMethod("get", String.class);
boolean isMiUiV6 = "V6".equals(getStringMethod.invoke(sysClass, "ro.miui.ui.version.name"));
boolean isMiUiV7 = "V7".equals(getStringMethod.invoke(sysClass, "ro.miui.ui.version.name"));
boolean isMiUiV8 = "V8".equals(getStringMethod.invoke(sysClass, "ro.miui.ui.version.name"));
boolean isMiUiV9 = "V9".equals(getStringMethod.invoke(sysClass, "ro.miui.ui.version.name"));
return isMiUiV6|isMiUiV7|isMiUiV8|isMiUiV9;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
//判断是否是小米的 MiUi(所有版本)
public static boolean isMiUiOS() {
try {
final BuildProperties prop = BuildProperties.newInstance();
return prop.getProperty(KEY_MIUI_VERSION_CODE, null) != null
|| prop.getProperty(KEY_MIUI_VERSION_NAME, null) != null
|| prop.getProperty(KEY_MIUI_INTERNAL_STORAGE, null) != null;
} catch (final IOException e) {
return false;
}
}
/**
* set status bar darkMode
*
* MiUi 将修改MiUiWindowManager的部分LayoutParams
* FlyMe 将调用其对应的API
* Android 6.0 起将调用高亮状态栏模式
*
* @param darkMode 是否是黑色模式
* @param activity 所要设置的activity
*/
public static void setStatusBarDarkMode(boolean darkMode, Activity activity) {
if (isTranslucentStatusMiUi) {
Class extends Window> clazz = activity.getWindow().getClass();
try {
Class> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
int 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();
}
} else if(isFlyMe) {
if(darkMode) StatusBarProxy.setStatusBarDarkIcon(activity.getWindow(), true);
else StatusBarProxy.setStatusBarDarkIcon(activity.getWindow(), false);
}
//MiUi 和 FlyMe 均再进行高亮模式设置,为了避免系统版本在Android M起切换黑色模式无效的问题
setLightStatusBar(darkMode, activity);
}
/**
*
* Android 6.0起设置原生状态栏高亮模式(高亮模式下状态栏文字及图标将变成灰色)
*
* @param lightMode 是否是高亮模式
* @param activity 所要设置的activity
*/
private static void setLightStatusBar(boolean lightMode, Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int vis = activity.getWindow().getDecorView().getSystemUiVisibility();
if(lightMode) vis |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
else vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
activity.getWindow().getDecorView().setSystemUiVisibility(vis);
}
}
//判断是否是魅族的 FlyMe OS
private static boolean isFlyMeOS() {
/* 获取魅族系统操作版本标识*/
String meiZuFlyMeOSFlag = getSystemProperty("ro.build.display.id", "");
if (TextUtils.isEmpty(meiZuFlyMeOSFlag)){
return false;
} else if (meiZuFlyMeOSFlag.contains("flyme") || meiZuFlyMeOSFlag.toLowerCase().contains("flyme")){
return true;
} else {
return false;
}
}
//是否是魅族的 FlyMe
public static boolean isFlyMe() {
return isFlyMe;
}
/**
* 获取系统属性
* @param key ro.build.display.id
* @param defaultValue 默认值
* @return 系统操作版本标识
*/
private static String getSystemProperty(String key, String defaultValue) {
try {
Class> clz = Class.forName("android.os.SystemProperties");
Method get = clz.getMethod("get", String.class, String.class);
return (String)get.invoke(clz, key, defaultValue);
} catch (Exception e) {
return null;
}
}
//获取状态栏高度
public static int getStatusBarHeight(Activity activity) {
int result = 0;
int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = activity.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
/**
* 设置系统沉浸式状态栏属性(系统版本需高于等于 Android 4.4,如果是 FlyMe 并低于 Android 4.4 时调用其对应 API)
*
* @param activity 所要设置的activity
* @param on 是否开启沉浸式
*/
public static void setTranslucentStatus(Activity activity, boolean on) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
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);
} else {
if(isFlyMe) StatusBarProxy.setImmersedWindow(activity.getWindow(), on);
}
}
/**
* 设置状态栏颜色(该方法只作用于 Android 5.0 起的系统版本)
*
* @param activity 所要设置的activity
* @param color 要设置的颜色,非 R.color.xxx 形式
*/
public static void setStatusBarColor(Activity activity, int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
activity.getWindow().setStatusBarColor(color);
}
}
}
1、判断 MIUI 的方法,有两个,一个是判断全部版本的,另一个是判断是否支持沉浸式以及状态栏图标变黑的版本(因为 MIUI 是从 V6 开始才支持的,所以要进行判断,然后该方法需要定期更新,当前的代码只是判断到 V9 ),对外提供了直接获取判断结果的方法。
2、判断 Flyme 的方法,这个就直接进行判断了,没有什么特殊的版本,对外也提供了直接获取判断结果的方法。
3、接着是设置黑色模式状态栏的方法,其效果就是黑色模式下状态栏图标和文字会变成灰黑色,关闭该模式则恢复成白色。该方法作用于支持状态栏图标变黑的 MIUI 版本、Flyme、Android M起(没有修改过原生API的系统基本都可以)的机子。相关官方 API 说明如下,Flyme 需要下载相关 SDK:
MIUI:
MIUI 6 沉浸式状态栏调用方法
MIUI 6 沉浸式状态栏调用方法
(我也不知道哪个是最新的...)
Flyme:
Flyme系统API
(SDK也在该链接内下载)
Android M 起则调用原生 API(也可以直接在 styles.xml
然后 Android M 起无论是 MIUI 还是 Flyme 均再调用原生高亮模式的 API(因为好像发现 MIUI 自己的 API 在 M 版本起无法成功调起,所以避免这种情况就再次调用了原生 API)。
4、获取状态栏高度的方法。该方法可以获取系统默认的状态栏高度(MIUI 的 状态栏会细一点),注意一点,如果你发现调用 setHeight() 无法正常设置对的高度的话请调整为通过 setLayoutParams 方式。
5、设置原生状态栏沉浸式属性的方法。该方法在 Android 4.4 起可以设置/关闭 FLAG_TRANSLUCENT_STATUS 该属性(该属性也可以直接在 styles.xml 进行设置,对应的 item 是 android:windowTranslucentStatus),在 Android 4.4 以下版本会调用 Flyme 的 API(因为看了其源码,4.4 起是直接设置原生的沉浸式属性,4.4 以下有它自己的处理)。
然后还要提及一点:在 Android M 起,如果关闭该属性并在 styles.xml 设置了 colorPrimaryDark(状态栏颜色)可以实现跟 MIUI 和 Flyme 一样效果的沉浸式;而 4.4 起打开该属性的话沉浸式就是会加上一层透明层的那种效果,另外打开该属性 styles.xml 里面设置的 colorPrimaryDark 会无效(搞不懂为什么)。
6、设置状态栏颜色的方法。该方法只作用于 Android 5.0 起的版本,对应于第 5 点中提及的 styles.xml 中的 colorPrimaryDark,相关注意请参考第 5 点。
7、下面推荐沉浸式状态栏的两个库:
A、SystemBarTint
B、StatusBarUtil
两个库现在都在用,如果有这个需求的可以参考使用。