美文网首页沉浸式状态栏程诺陪你学Android
全面解析android沉浸式状态栏

全面解析android沉浸式状态栏

作者: leoryzhu | 来源:发表于2019-05-16 23:12 被阅读76次

    一、沉浸式实现方式

    实现沉浸式状态样主要有两种方式,一种是通过设置theme来实现,另一种是代码中实现

    1.1通过设置theme来实现

    通过theme设置也有两种方法

    方法一:

    values/style.xml

    <style name="AppTheme" parent="AppTheme">
    <!--在Android 4.4之前的版本上运行,直接跟随系统主题-->
    
    </style>
    

    values-v19/style.xml

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="android:windowTranslucentStatus">true</item>
        <item name="android:windowTranslucentNavigation">true</item>
    </style>
    

    values-v21/style.xml

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="android:windowTranslucentStatus">false</item>
        <item name="android:windowTranslucentNavigation">true</item>
        <!--Android 5.x开始需要把颜色设置透明,否则导航栏会呈现系统默认的浅灰色-->
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>
    

    方法二:

    直接在values/style.xml中通过函数API进行区分

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
            <item name="android:windowTranslucentStatus" tools:targetApi="kitkat">true</item>
            <item name="android:windowTranslucentNavigation" tools:targetApi="kitkat">true</item>
            <item name="android:statusBarColor" tools:targetApi="lollipop">@android:color/transparent</item>
    </style>
    

    1.2 代码中设置

    /**
     * 通过设置全屏,设置状态栏透明
     *
     * @param activity
     */
    private void fullScreen(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                Window window = activity.getWindow();
                View decorView = window.getDecorView();
                //两个 flag 要结合使用,表示让应用的主体内容占用系统状态栏的空间
                int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
                decorView.setSystemUiVisibility(option);
                window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                //设置状态栏为透明,否则在部分手机上会呈现系统默认的浅灰色
                window.setStatusBarColor(Color.TRANSPARENT);
                //导航栏颜色也可以考虑设置为透明色
                //window.setNavigationBarColor(Color.TRANSPARENT);
            } else {
                Window window = activity.getWindow();
                WindowManager.LayoutParams attributes = window.getAttributes();
                int flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
                int flagTranslucentNavigation = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
                attributes.flags |= flagTranslucentStatus;
    //                attributes.flags |= flagTranslucentNavigation;
                window.setAttributes(attributes);
            }
        }
    }
    

    上面设置完之后,会出现页面挤到状态栏中去了,如下图,


    image.png

    针对这种情况又有2种解决办法,一种是设置android:fitsSystemWindows,另一种获取状态样高度后,设置页面的paddingTop值。

    方案一:

    values/style.xml中设置

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    
            <item name="android:windowTranslucentStatus" tools:targetApi="kitkat">true</item>
            <item name="android:windowTranslucentNavigation" tools:targetApi="kitkat">true</item>
            <item name="android:statusBarColor" tools:targetApi="lollipop">@android:color/transparent</item>
            <item name="android:fitsSystemWindows">true</item>
        </style>
    

    方案二:

    首先通过反射获取状态栏高度
    StatusBarCompat.class

     public static int getStatusBarHeight(Context context) {
            int result = 0;
            int resId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
            if (resId > 0) {
                result = context.getResources().getDimensionPixelOffset(resId);
            }
            return result;
        }
    

    再获取页面的根布局设置paddingTop

    final int statusHeight = StatusBarCompat.getStatusBarHeight(this);
    root.mToolBar.setPadding(0, statusHeight, 0, 0);
    

    修改后的效果:


    image.png

    二、状态栏颜色修改

    通过上面的设置可以实现状态栏的沉浸效果,状态栏颜色和字体颜色似乎不太近人意
    android 5.0之后提供了修改状态栏颜色的方法
    window.setStatusColor(@ColorInt int color)
    该方法的生效条件是给window添加LAG_DRAWS_SYSTEM_BAR_BACKGROUNDS并取消FLAG_TRANSLUCENT_STATUS属性,android 4.4 需要用到SystemBarTintManager 辅助类,在build.gradle中引用compile 'com.readystatesoftware.systembartint:systembartint:1.0.3'
    修改的状态栏颜色的代码如下:

     /**
         * 修改状态栏颜色,支持4.4以上版本
         * @param activity
         * @param colorId
         */
        public static void setStatusBarColor(Activity activity, int colorId) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                Window window = activity.getWindow();
                window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
                window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                window.setStatusBarColor(activity.getResources().getColor(colorId));
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                //使用SystemBarTint库使4.4版本状态栏变色,需要先将状态栏设置为透明
                transparencyBar(activity);
                SystemBarTintManager tintManager = new SystemBarTintManager(activity);
                tintManager.setStatusBarTintEnabled(true);
                tintManager.setStatusBarTintResource(colorId);
            }
        }
    

    三、状态栏文字颜色修改

    android 6.0以上才添加修改状态栏字体颜色,修改相对比较麻烦点,分为google原生、小米、魅族三种情况。

    google原生状态栏文字颜色修改

    这个方法比较简单,通过切换DecorView两个不同的标志位

    private static void setAndroidNativeLightStatusBar(Activity activity, boolean dark) {
        View decor = activity.getWindow().getDecorView();
        if (dark) {
            decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        } else {
            decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        }
    }
    

    小米系统状态栏文字颜色修改

    直接来代码

    public static boolean MIUISetStatusBarLightMode(Activity activity, boolean dark) {
        boolean result = false;
        Window window = activity.getWindow();
        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;
    
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && RomUtils.isMiUIV7OrAbove()) {
                    //开发版 7.7.13 及以后版本采用了系统API,旧方法无效但不会报错,所以两个方式都要加上
                    if (dark) {
                        activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
                    } else {
                        activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
                    }
                }
            } catch (Exception e) {
    
            }
        }
        return result;
    }
    

    魅族系统状态栏文字颜色修改

    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;
    }
    

    修改成功之后的效果图


    image.png

    四、使用建议

    android 6.0之前,不能够修改状态栏文字颜色,默认的文字颜色是白色,所以,在6.0以下要设置状态栏颜色不能设置为白色,为了防止文字和背景颜色相同。
    我的使用,6.0以上白色状态栏,黑色背景;6.0以下黑色状态栏,白色背景

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                StatusBarUtils.setLightStatusBar(this, true);//黑色文字
                StatusBarUtils.setStatusBarColor(this, R.color.white);//白色背景
            }else {
                //6.0以下默认白色文字
                StatusBarUtils.setStatusBarColor(this, R.color.black);//黑色背景
            }
    

    五、附加代码

    StatusBarUtils类

    /**
     * Describe : 状态栏设置工具
     */
    public class StatusBarUtils {
        @TargetApi(19)
        public static void transparencyBar(Activity activity) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                Window window = activity.getWindow();
                window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
                window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                window.setStatusBarColor(Color.TRANSPARENT);
                window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
    
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                Window window = activity.getWindow();
                window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
                        WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            }
        }
        /**
         * 修改状态栏颜色,支持4.4以上版本
         * @param activity
         * @param colorId
         */
        public static void setStatusBarColor(Activity activity, int colorId) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                Window window = activity.getWindow();
                window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
                window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                window.setStatusBarColor(activity.getResources().getColor(colorId));
            }
            else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                //使用SystemBarTint库使4.4版本状态栏变色,需要先将状态栏设置为透明
                transparencyBar(activity);
                SystemBarTintManager tintManager = new SystemBarTintManager(activity);
                tintManager.setStatusBarTintEnabled(true);
                tintManager.setStatusBarTintResource(colorId);
            }
        }
    
        /**
         *  修改状态栏文字颜色,这里小米,魅族区别对待。
         */
        public static void setLightStatusBar(final Activity activity, final boolean dark) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                switch (RomUtils.getLightStatusBarAvailableRomType()) {
                    case RomUtils.AvailableRomType.MIUI:
                        MIUISetStatusBarLightMode(activity, dark);
                        break;
    
                    case RomUtils.AvailableRomType.FLYME:
                        setFlymeLightStatusBar(activity, dark);
    
                        break;
    
                    case RomUtils.AvailableRomType.ANDROID_NATIVE:
                        setAndroidNativeLightStatusBar(activity, dark);
                        break;
    
                }
            }
        }
    
    
        public static boolean MIUISetStatusBarLightMode(Activity activity, boolean dark) {
            boolean result = false;
            Window window = activity.getWindow();
            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;
    
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && RomUtils.isMiUIV7OrAbove()) {
                        //开发版 7.7.13 及以后版本采用了系统API,旧方法无效但不会报错,所以两个方式都要加上
                        if (dark) {
                            activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
                        } else {
                            activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
                        }
                    }
                } catch (Exception e) {
    
                }
            }
            return result;
        }
    
        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_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
            } else {
                decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            }
        }
    }
    
    

    RomUtils类代码:

    /**
     * Describe : rom 工具
     */
    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 int getLightStatusBarAvailableRomType() {
            //开发版 7.7.13 及以后版本采用了系统API,旧方法无效但不会报错
            if (isMiUIV7OrAbove()) {
                return AvailableRomType.ANDROID_NATIVE;
            }
    
            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;
        }
    
        //Android Api 23以上
        private static boolean isAndroidMOrAbove() {
            return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
        }
    
        private static final String KEY_MIUI_VERSION_CODE = "ro.miui.ui.version.code";
    
        private static boolean isMiUIV6OrAbove() {
            try {
                final Properties properties = new Properties();
                properties.load(new FileInputStream(new File(Environment.getRootDirectory(), "build.prop")));
                String uiCode = properties.getProperty(KEY_MIUI_VERSION_CODE, null);
                if (uiCode != null) {
                    int code = Integer.parseInt(uiCode);
                    return code >= 4;
                } else {
                    return false;
                }
    
            } catch (final Exception e) {
                return false;
            }
    
        }
    
    

    相关文章

      网友评论

        本文标题:全面解析android沉浸式状态栏

        本文链接:https://www.haomeiwen.com/subject/mzzwaqtx.html