【Android】状态栏相关适配(判断MIUI,Flyme,状态栏图标颜色切换,获取状态栏高度,沉浸式状态栏相关等)

对于状态栏相关适配这个事情,真是让人头疼的一个模块。因为负责的项目主题色偏偏是白色,不但要去适配 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 keys() {
        return properties.keys();
    }

    public Set keySet() {
        return properties.keySet();
    }

    public int size() {
        return properties.size();
    }

    public Collection values() {
        return properties.values();
    }

    public static BuildProperties newInstance() throws IOException {
        return new BuildProperties();
    }
} 
  
OSHelper.java

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 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 进行设置,对应的 item 是 android:windowLightStatusBar ,true 则是黑色模式)。

然后 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

两个库现在都在用,如果有这个需求的可以参考使用。


你可能感兴趣的:([Android]解决方案)