Android沉浸式状态栏

作者: filimoo | 来源:发表于2019-04-22 16:22 被阅读68次

    Android沉浸式状态栏

    Android状态栏默认是固定的黑底白字,这肯定是不被伟大的设计师所喜爱的,更有甚者,某些时候设计希望内容能够延时到状态栏底部(例如头部是大图的情况)。所幸的是随着Android版本的迭代,开发者对状态栏等控件有了更多的控制。Android一直在尝试引入新的Api来满足开发者的需求,但Api却一直不够完美,接口添加了很多,却都不够简单或者说完美,算上第三方厂商的特色行为,怎一个“乱”字了得

    Android完美的沉浸式需要多个接口配合使用才能完成,我们需要去了解android各个版本引入的Api的功能和局限性,因此这篇文章首先会介绍系统的一些接口,然后展示如何封装一些接口用于实现沉浸式。

    • SystemUI
    • StatusBar颜色更改
    • fitSystemWindows
    • 一个完整的封装

    SystemUI

    在Android2.3以前,对StatusBar的操作有两个:StatusBar的显示与隐藏、Activiy内容延伸到StatusBar下方(全局布局)。

    // 全屏布局且隐藏状态栏:
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
    
    // 全屏布局,不隐藏状态栏:
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 
    
    

    | WindowManager.LayoutParams.FLAGLAYOUTNO_LIMITS);

    在Android3.0中,View添加了一个重要的方法:setSystemUiVisibility(int),用于控制一些窗口装饰元素的显示,并添加了View.STATUS_BAR_VISIBLEView.STATUS_BAR_HIDDEN两个Flag用于控制Status Bar的显示与隐藏。

    在Android4.0中,View.STATUS_BAR_VISIBLE改为View.SYSTEM_UI_FLAG_VISIBLEView.STATUS_BAR_HIDDEN更名为View.SYSTEM_UI_FLAG_LOW_PROFILE。由于引进了NavigationBar,因此也添加了一个flag:SYSTEM_UI_FLAG_HIDE_NAVIGATION

    • View.SYSTEM_UI_FLAG_LOW_PROFILE: 同时影响StatusBar和NavigationBar,但并不会使得SystemUI消失,而只会使得背景很浅,并且去掉SystemUI的一些图标或文字。
    • View.SYSTEM_UI_FLAG_HIDE_NAVIGATION: 会隐藏NavigationBar,但是由于NavigationBar是非常重要的,因此只要有用户交互,系统就会清除这个flag使NavigationBar就会再次出现。

    在Android4.1中,又引入了以下几个flag:

    • View.SYSTEM_UI_FLAG_FULLSCREEN: 这个标志与WindowManager.LayoutParams.FLAG_FULLSCREEN作用相同,但是如果你从屏幕下滑或者一些其它操作,会使得StatusBar重新显示。
    • View.SYSTEM_UI_FLAG_LAYOUT_STABLE: 与其它flag配合使用,防止系统栏隐藏时内容区域发生变化。
    • View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN: Activity全屏显示,但状态栏不会被隐藏覆盖,状态栏依然可见,Activity顶端布局部分会被状态遮住
    • View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION: 使内容布局到NavigationBar之下,可以配合SYSTEM_UI_FLAG_HIDE_NAVIGATION使用防止跳动

    在Android4.4(API 19)又增加了两个flag:

    • View.SYSTEM_UI_FLAG_IMMERSIVE
    • View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY

    这两个flag主要是对SYSTEM_UI_FLAG_FULLSCREENSYSTEM_UI_FLAG_HIDE_NAVIGATION的修补。前文已经说过,在使用这两个flag后,用户的某些行为会使得系统强制清除这些flag。这并不是用户想要的,因此配合View.SYSTEM_UI_FLAG_IMMERSIVEView.SYSTEM_UI_FLAG_IMMERSIVE_STICKY就可以阻止系统的强制清除行为。

    View.SYSTEM_UI_FLAG_IMMERSIVE只作用与SYSTEM_UI_FLAG_FULLSCREEN,而View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY同时作用于两个

    综上,我们可以给出全屏布局和隐藏状态栏的新方案

    //仅仅只是全屏布局:
    //getWindow().getDecorView().setSystemUiVisibility(
        View.SYSTEM_UI_FLAG_LAYOUT_STABLE
        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    
    //全屏布局并且隐藏状态栏与导航栏
    getWindow().getDecorView().setSystemUiVisibility(
        View.SYSTEM_UI_FLAG_FULLSCREEN
        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    
    

    在Android4.4还为WindowManager.LayoutParams添加了两个flag:

    • FLAG_TRANSLUCENT_STATUS: 当使用这个flag时SYSTEM_UI_FLAG_LAYOUT_STABLESYSTEM_UI_FLAG_LAYOUT_FULLSCREEN会被自动添加
    • FLAG_TRANSLUCENT_NAVIGATION:当使用这这个个flag时SYSTEM_UI_FLAG_LAYOUT_STABLESYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION会被自动添加。

    StatusBar颜色更改

    StatusBar的颜色更改分为两部分,一个是背景颜色的修改,一个是字体颜色的修改。

    首先先说说背景颜色的修改,在Android 5.0之前,状态栏颜色并不可定制,5.0之后才可定制。首先,我们可以在主题里通过colorPrimaryDark来指定背景色,其次,我们可以调用 window.setStatusBarColor(@ColorInt int color) 来修改状态栏颜色,但是让这个方法生效有一个前提条件:

    你必须给window添加FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS并且取消FLAG_TRANSLUCENT_STATUS

    此外,设置FLAG_TRANSLUCENT_STATUS也会影响到StatusBar的背景色,但并没有固定的表现:

    • 对于6.0以上的机型,设置此flage会使得StatusBar完全透明
    • 对于5.x的机型,大部分是使背景色半透明,小米和魅族以及其它少数机型会全透明
    • 对于4.4的机型,小米和魅族是透明色,而其它系统上就只是黑色到透明色的渐变。

    我们知道了改背景色后,我们再来看看字体和图标颜色的更改。默认字体和图标是白色,如果在浅色背景上就会看不到状态栏信息了,因此体验会很糟糕。但可惜的是android6.0才官方支持更改字体和图标的颜色。

    在Android6以后,我们只要给SystemUI加上SYSTEM_UI_FLAG_LIGHT_STATUS_BAR这个flag,就可以让字体和图标变为黑色。虽然官方已经支持了,但国内有些机型的版本号确实是6.0,但并不能更改字体和图标颜色,例如联想的ZUK Z1机型

    当然,国内的魅族和小米走在前沿,从Android4.4开始就已经更改字体和图标颜色了,但并没有直接的接口用,必须通过反射的方式去更改字体颜色

    针对小米的方案:

    /**
     * 设置状态栏字体图标为深色,需要 MIUIV6 以上
     *
     * @param window 需要设置的窗口
     * @param dark   是否把状态栏字体及图标颜色设置为深色
     * @return boolean 成功执行返回 true
     */
    public static boolean MIUISetStatusBarLightMode(Window window, boolean dark) {
        boolean result = false;
        if (window != null) {
            Class clazz = window.getClass();
            try {
                int darkModeFlag;
                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;
            } catch (Exception e) {
    
            }
        }
        return result;
    }
    
    

    针对魅族的方案:

     /**
     * 设置状态栏图标为深色和魅族特定的文字风格
     * 可以用来判断是否为 Flyme 用户
     *
     * @param window 需要设置的窗口
     * @param dark   是否把状态栏字体及图标颜色设置为深色
     * @return boolean 成功执行返回true
     */
    public static boolean FlymeSetStatusBarLightMode(Window window, boolean dark) {
        boolean result = false;
        if (window != null) {
            try {
                WindowManager.LayoutParams lp = window.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);
                window.setAttributes(lp);
                result = true;
            } catch (Exception e) {
    
            }
        }
        return result;
    }
    
    

    对于小米魅族除外的Android5.x的机器,不能改字体和图标颜色,如果app是浅色皮肤,那么我们就只能给StatusBar设置半透明的背景了,并且FLAG_TRANSLUCENT_STATUS并不可靠(前文已说,表现不一定是半透明背景)

    fitSystemWindows

    我们首先探讨了内容布局是否全屏以及状态栏的显示与隐藏,其次我们探讨了状态栏颜色的修改问题。那如果我们全屏布局并且显示透明状态栏的时候会怎样?

    状态栏与内容会重叠。这既是我们想要的效果,也是我们不想要的内容。如果APP顶部时高斯模糊的图片,与状态栏重叠是设计师希望看到的效果;但是,如果ActionBar和状态栏重叠了,那可就不好看了。 所以重叠与不重叠完全看业务,而库的封装者则需要告诉业务方,如何才能不重叠。

    这个时候就是fitSystemWindows出场的时候了。

    我们可以给view设置fitSystemWindows属性,其是一个bool值。其既可以在xml里直接设置android:fitsSystemWindows="true",也可以通过View#setFitsSystemWindows(boolean fitSystemWindows)在java代码中设置。不过这一步也仅仅只是设置了一个flag。

    Android系统组件例如状态栏、NavBar、键盘所占据的空间称为界面的WindowInsets,Android系统会在特定的时机从根View派发WindowInsets,如果View的fitSystemWindows标志位被设为true的话,WindowInsets会传递给下列几个方法:

    1. fitSystemWindows(Rect insets): 这个是老版本提供的接口,现在已经被弃用,仅用于API 19
    2. onApplyWindowInsets(WindowInsets insets): 这应该是标准的方式了,然而在魅蓝M1上竟然会出现找不到WindowInsets这个类的crash
    3. 使用ViewCompat.setOnApplyWindowInsetsListener添加的Listener: 这种setListener的方式比较灵活,并且传值是WindowInsetsCompat类型,在魅蓝M1等机型都可以跑通,是上乘之选。

    此外有几个关键点需要重点关注:

    1. 一旦有一个View消耗了WindowInsets,那么WindowInsets的dispatch就结束了。所以一般只在Activity的最外层View调用setFitsSystemWindows(true)
    2. 系统处理WindowInsets的手段本质是设置padding,因此这会让你View原本的padding失效
    3. 一般而言,只有一个View消耗WindowInsets,但这是系统行为,我们可以在onApplyWindowInsets里主动调用dispatchApplyWindowInsets使得其可以继续传递。

    第三点的意义在于,如果我们需要多个View受WindowInsets影响时,我们可以自己去传递WindowInsets,一般封装者也会提供一个WindowInsetsLayout, 让直接子元素的fitSystemWindows都生效。@XiNGRZ在Mantou Earth有一个很好的实现(点我查看)。使用这个Layout可以满足大部分需求,但也存在一个小漏洞:使用onApplyWindowInsets在魅蓝M1上会crash(前文已经指出原因)

    业务上可能会对fitSystemWindows有更复杂的应用,很多时候是由于历史业务的原因导致大大小小的坑,这个时候就需要我们很好的把握fitSystemWindows,随机应变,自由适配WindowInsets了。

    一个完整的封装

    基于上述的种种讨论,我认为一个良好的封装应该提供三个方面的接口:全屏布局+ 状态栏透明(5.x半透明)、 更改状态栏颜色、 一个WindowInsetsLayout。

    下面看一下QMUI(内部Android UI库)的实现:

    /**
     * 沉浸式状态栏
     * 支持 4.4 以上版本的 MIUI 和 Flyme,以及 5.0 以上版本的其他 Android
     *
     * @param activity
     */
    @TargetApi(19)
    public static void translucent(Activity activity, @ColorInt int colorOn5x) {
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT){
            // 版本小于4.4,绝对不考虑沉浸式
            return;
        }
        // 小米和魅族4.4 以上版本支持沉浸式
        if (QMUIDeviceHelper.isMeizu() || QMUIDeviceHelper.isMIUI()) {
            Window window = activity.getWindow();
            window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            return;
        }
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = activity.getWindow();
            window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && supportTransclentStatusBar6()) {
                // android 6以后可以改状态栏字体颜色,因此可以自行设置为透明
                // ZUK Z1是个另类,自家应用可以实现字体颜色变色,但没开放接口
                window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
                window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                window.setStatusBarColor(Color.TRANSPARENT);
            } else {
                // android 5不能修改状态栏字体颜色,因此直接用FLAG_TRANSLUCENT_STATUS,nexus表现为半透明
                // update: 部分手机运用FLAG_TRANSLUCENT_STATUS时背景不是半透明而是没有背景了。。。。。
                // window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    
                // 采取setStatusBarColor的方式,部分机型不支持,那就纯黑了,保证状态栏图标可见
                window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
                window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                window.setStatusBarColor(colorOn5x);
            }
    //        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    //            // android4.4的默认是从上到下黑到透明,我们的背景是白色,很难看,因此只做魅族和小米的
    //        } else if(Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1){
    //            // 如果app 为白色,需要更改状态栏颜色,因此不能让19一下支持透明状态栏
    //            Window window = activity.getWindow();
    //            Integer transparentValue = getStatusBarAPITransparentValue(activity);
    //            if(transparentValue != null) {
    //                window.getDecorView().setSystemUiVisibility(transparentValue);
    //            }
        }
    }
    
    

    然后是更改状态栏的颜色:

    /**
     * 设置状态栏黑色字体图标,
     * 支持 4.4 以上版本 MIUI 和 Flyme,以及 6.0 以上版本的其他 Android
     *
     * @param activity 需要被处理的 Activity
     */
    public static void setStatusBarLightMode(Activity activity) {
        if (mStatuBarType != STATUSBAR_TYPE_DEFAULT) {
            setStatusBarLightMode(activity, mStatuBarType);
            return;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            if (MIUISetStatusBarLightMode(activity.getWindow(), true)) {
                mStatuBarType = 1;
            } else if (FlymeSetStatusBarLightMode(activity.getWindow(), true)) {
                mStatuBarType = 2;
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                Window window = activity.getWindow();
                View decorView = window.getDecorView();
                int systemUi = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
                systemUi = changeStatusBarModeRetainFlag(window, systemUi);
                decorView.setSystemUiVisibility(systemUi);
                mStatuBarType = 3;
            }
        }
    }
    
    /**
     * 已知系统类型时,设置状态栏黑色字体图标。
     * 支持 4.4 以上版本 MIUI 和 Flyme,以及 6.0 以上版本的其他 Android
     *
     * @param activity 需要被处理的 Activity
     * @param type     StatusBar 类型,对应不同的系统
     */
    private static void setStatusBarLightMode(Activity activity, @StatusBarType int type) {
        if (type == STATUSBAR_TYPE_MIUI) {
            MIUISetStatusBarLightMode(activity.getWindow(), true);
        } else if (type == STATUSBAR_TYPE_FLYME) {
            FlymeSetStatusBarLightMode(activity.getWindow(), true);
        } else if (type == STATUSBAR_TYPE_ANDROID6) {
            Window window = activity.getWindow();
            View decorView = window.getDecorView();
            int systemUi = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            systemUi = changeStatusBarModeRetainFlag(window, systemUi);
            decorView.setSystemUiVisibility(systemUi);
        }
    
    }
    
    /**
     * 设置状态栏白色字体图标
     * 支持 4.4 以上版本 MIUI 和 Flyme,以及 6.0 以上版本的其他 Android
     */
    public static void setStatusBarDarkMode(Activity activity) {
        if (mStatuBarType == STATUSBAR_TYPE_DEFAULT) {
            // 默认状态,不需要处理
            return;
        }
        if (mStatuBarType == STATUSBAR_TYPE_MIUI) {
            MIUISetStatusBarLightMode(activity.getWindow(), false);
        } else if (mStatuBarType == STATUSBAR_TYPE_FLYME) {
            FlymeSetStatusBarLightMode(activity.getWindow(), false);
        } else if (mStatuBarType == STATUSBAR_TYPE_ANDROID6) {
            Window window = activity.getWindow();
            View decorView = window.getDecorView();
            int systemUi = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
            systemUi = changeStatusBarModeRetainFlag(window, systemUi);
            decorView.setSystemUiVisibility(systemUi);
        }
    }
    
    /**
     * 每次设置SystemUiVisibility要保证其它必须的flag不能丢
     */
    private static int changeStatusBarModeRetainFlag(Window window, int out) {
        out = retainSystemUiFlag(window, out, View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
        out = retainSystemUiFlag(window, out, View.SYSTEM_UI_FLAG_FULLSCREEN);
        out = retainSystemUiFlag(window, out, View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
        out = retainSystemUiFlag(window, out, View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
        out = retainSystemUiFlag(window, out, View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
        out = retainSystemUiFlag(window, out, View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
        return out;
    }
    
    public static int retainSystemUiFlag(Window window, int out, int type) {
        int now = window.getDecorView().getSystemUiVisibility();
        if ((now & type) == type) {
            out |= type;
        }
        return out;
    }
    
    

    最后是QMUIWindowInsetLayout,这个只是对XiNGRZ的代码作了一些小改动:

    public class QMUIWindowInsetLayout extends FrameLayout {
    
        public QMUIWindowInsetLayout(Context context) {
            this(context, null);
        }
    
        public QMUIWindowInsetLayout(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public QMUIWindowInsetLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            ViewCompat.setOnApplyWindowInsetsListener(this,
                    new android.support.v4.view.OnApplyWindowInsetsListener() {
                        @Override
                        public WindowInsetsCompat onApplyWindowInsets(View v,
                                                                      WindowInsetsCompat insets) {
                            return setWindowInsets(insets);
                        }
                    });
        }
    
        private WindowInsetsCompat setWindowInsets(WindowInsetsCompat insets) {
            if (Build.VERSION.SDK_INT >= 21 && insets.hasSystemWindowInsets()) {
                if (applySystemWindowInsets21(insets)) {
                    return insets.consumeSystemWindowInsets();
                }
            }
            return insets;
        }
    
        @SuppressWarnings("deprecation")
        @Override
        protected boolean fitSystemWindows(Rect insets) {
            if (Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 21) {
                return applySystemWindowInsets19(insets);
            }
            return super.fitSystemWindows(insets);
        }
    
        @SuppressWarnings("deprecation")
        @TargetApi(19)
        private boolean applySystemWindowInsets19(Rect insets) {
            boolean consumed = false;
    
            for (int i = 0; i < getChildCount(); i++) {
                View child = getChildAt(i);
                if (!child.getFitsSystemWindows()) {
                    continue;
                }
    
                Rect childInsets = new Rect(insets);
                computeInsetsWithGravity(child, childInsets);
    
                child.setPadding(childInsets.left, childInsets.top, childInsets.right, childInsets.bottom);
    
                consumed = true;
            }
    
            return consumed;
        }
    
        @TargetApi(21)
        private boolean applySystemWindowInsets21(WindowInsetsCompat insets) {
            boolean consumed = false;
    
            for (int i = 0; i < getChildCount(); i++) {
                View child = getChildAt(i);
    
                if (!child.getFitsSystemWindows()) {
                    continue;
                }
    
                Rect childInsets = new Rect(
                        insets.getSystemWindowInsetLeft(),
                        insets.getSystemWindowInsetTop(),
                        insets.getSystemWindowInsetRight(),
                        insets.getSystemWindowInsetBottom());
    
                computeInsetsWithGravity(child, childInsets);
                ViewCompat.dispatchApplyWindowInsets(child, insets.replaceSystemWindowInsets(childInsets));
    
                consumed = true;
            }
    
            return consumed;
        }
    
        @SuppressLint("RtlHardcoded")
        private void computeInsetsWithGravity(View view, Rect insets) {
            LayoutParams lp = (LayoutParams) view.getLayoutParams();
    
            int gravity = lp.gravity;
    
            /**
             * 因为该方法执行时机早于 FrameLayout.layoutChildren,
             * 而在 {FrameLayout#layoutChildren} 中当 gravity == -1 时会设置默认值为 Gravity.TOP | Gravity.LEFT,
             * 所以这里也要同样设置
             */
            if (gravity == -1) {
                gravity = Gravity.TOP | Gravity.LEFT;
            }
    
            if (lp.width != LayoutParams.MATCH_PARENT) {
                int horizontalGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
                switch (horizontalGravity) {
                    case Gravity.LEFT:
                        insets.right = 0;
                        break;
                    case Gravity.RIGHT:
                        insets.left = 0;
                        break;
                }
            }
    
            if (lp.height != LayoutParams.MATCH_PARENT) {
                int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
                switch (verticalGravity) {
                    case Gravity.TOP:
                        insets.bottom = 0;
                        break;
                    case Gravity.BOTTOM:
                        insets.top = 0;
                        break;
                }
            }
        }
    
    }
    
    

    目前这套方案用于微信读书,应该是相当稳定的方案了,使用较为灵活。
    转载自:http://blog.cgsdream.org/2017/03/16/android-translcent-statusbar/

    相关文章

      网友评论

        本文标题:Android沉浸式状态栏

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