android开发杂识Android开发干货集中营UI

Android关于沉浸式状态栏的一些总结

2017-08-08  本文已影响2633人  依然范特稀西
封面.png

一、前言

其实我是不打算写这篇文章的,为什么呢?因为关于沉浸式状态栏的文章太多了,随便google一下就能出来几十上百篇文章,当然这其中有写的好的,也有滥竽充数的。前面在公众号推出了Material Design 的系列文章,就有读者留言,希望出一篇关于沉浸式的文章。因此这篇文章就整理总结一下各个版本的实现原理,顺便为大家推荐一个我觉得很方便的一个库。

二、沉浸式的一般套路

在介绍这个方便的轮子之前,我们先一起来回顾一下实现沉浸式状态栏的一般套路。在Android上,关于对StatusBar(状态栏)的操作,一直都在不断改善,并且表现越来越好,在Android4.4 以下,我们可以对StatusBar和 NavigationBar进行显示和隐藏操作。但是直到Android4.4,我们才能真正意义上的实现沉浸式状态栏。从Android4.4 到现在(Android 7.1),关于沉浸式大概可以分成三个阶段:

大概就是这个三个阶段,那么接下来我们就看一下这个三个阶段分别是如何来实现的。

2.1 Android4.4(API 19) - Android 5.0(API 21)实现沉浸式的方式

Android 4.4 为什么能够实现沉浸式的效果呢?因为在Android 4.4 新增了一个重要的属性:FLAG_TRANSLUCENT_STATUS

 /**
         * Window flag: request a translucent status bar with minimal system-provided
         * background protection.
         *
         * <p>This flag can be controlled in your theme through the
         * {@link android.R.attr#windowTranslucentStatus} attribute; this attribute
         * is automatically set for you in the standard translucent decor themes
         * such as
         * {@link android.R.style#Theme_Holo_NoActionBar_TranslucentDecor},
         * {@link android.R.style#Theme_Holo_Light_NoActionBar_TranslucentDecor},
         * {@link android.R.style#Theme_DeviceDefault_NoActionBar_TranslucentDecor}, and
         * {@link android.R.style#Theme_DeviceDefault_Light_NoActionBar_TranslucentDecor}.</p>
         *
         * <p>When this flag is enabled for a window, it automatically sets
         * the system UI visibility flags {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
         * {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.</p>
         */
        public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;

解释:设置状态栏透明,并且变为全屏模式。上面的解释已经说得很清楚了,当window的这个属性有效的时候,会自动设置 system ui visibility的标志SYSTEM_UI_FLAG_LAYOUT_STABLESYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

有两种方式实现这个属性:

可以在代码中设置,如下:

activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

当然也可以在theme 中设置属性windowTranslucentStatus,如下:

android:windowTranslucentStatus

效果如下:

效果如上图,可以看出,沉浸式的效果是出来了,但是也有一个问题,我们的标题栏和状态栏重叠了,相当于整个布局上移了StatusBar 的高度。

为了让标题栏回到原来的位置,我们在标题栏的上方添加一个大小和StatusBar大小一样的View,View 的BackgroundColor 为标题栏一样的颜色,这个View起到一个占位的作用。这个时候,标题栏就会下移StatusBar的高度,回到正常的位置。

添加如下代码:

       //获取windowphone下的decorView
        ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
        int       count     = decorView.getChildCount();
        //判断是否已经添加了statusBarView
        if (count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView) {
            decorView.getChildAt(count - 1).setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
        } else {
            //新建一个和状态栏高宽的view
            StatusBarView statusView = createStatusBarView(activity, color, statusBarAlpha);
            decorView.addView(statusView);
        }
        ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
        //rootview不会为状态栏留出状态栏空间
        ViewCompat.setFitsSystemWindows(rootView,true);
        rootView.setClipToPadding(true);

创建和status bar 一样大小的View的代码如下:

 private static StatusBarView createStatusBarView(Activity activity, int color, int alpha) {
        // 绘制一个和状态栏一样高的矩形
        StatusBarView statusBarView = new StatusBarView(activity);
        LinearLayout.LayoutParams params =
                new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));
        statusBarView.setLayoutParams(params);
        statusBarView.setBackgroundColor(calculateStatusColor(color, alpha));
        return statusBarView;
    }

其中StatusBarView 就是一个普通的View。

添加上述代码后,效果如下:


通过以上就可以实现Android 4.4 上的沉浸式状态栏。

另外,如果是一张图片延伸到状态栏的话,直接设置FLAG_TRANSLUCENT_STATUS就可以了,如下:

小结:Android4.4上实现沉浸式状态栏的套路是:为window添加FLAG_TRANSLUCENT_STATUS Flag,然后添加一个和status bar 一样大小的View 站位,从而让让标题栏不会与status bar 重叠。而图片延伸到状态栏只需要设置FLAG_TRANSLUCENT_STATUS就OK。

前面说过,沉浸式在Android4.4 - Android5.0 之间的版本表现得不是很好,从上面贴的几张图就可以看出,状态栏的顶部有一个渐变,会显示出黑色的阴影(底部的导航栏也是一样的效果),在Android 5.0 版本已经被修复了。

2.2 Android 5.0(API 21)以上实现沉浸式的方式

Android 5.0 是一个里程碑式的版本,从Android 5.0开始,Google 推出了全新的设计规范 Material Design,并且原生控件就可以实现一些炫酷的UI动效。从这个版本开始,google 加入了一个比较重要的方法setStatusBarColor (对应属性:android:statusBarColor),通过这个方法,可以很轻松地实现沉浸式状态栏。方法如下:

 /**
     * Sets the color of the status bar to {@code color}.
     *
     * For this to take effect,
     * the window must be drawing the system bar backgrounds with
     * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} and
     * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS} must not be set.
     *
     * If {@code color} is not opaque, consider setting
     * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
     * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.
     * <p>
     * The transitionName for the view background will be "android:status:background".
     * </p>
     */
    public abstract void setStatusBarColor(@ColorInt int color);

注意看这个方法的注释,想要这个方法生效,必须还要配合一个Flag一起使用,必须设置FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS ,并且不能设置FLAG_TRANSLUCENT_STATUS(Android 4.4才用这个)

我们来看一下FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS这个flag:

可以看到,这个flag 也是在Android 5.0添加的,它的作用是什么呢?

解释:设置了FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,表明会Window负责系统bar的background 绘制,绘制透明背景的系统bar(状态栏和导航栏),然后用getStatusBarColor()getNavigationBarColor()的颜色填充相应的区域。这就是Android 5.0 以上实现沉浸式导航栏的原理。

实现沉浸式添加如下代码:

getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//注意要清除 FLAG_TRANSLUCENT_STATUS flag
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().setStatusBarColor(getResources().getColor(android.R.color.holo_red_light));

效果如下:


当然也可以直接在Theme中使用,在values-v21文件夹下添加如下主题:


<style name="MDTheme" parent="Theme.Design.Light.NoActionBar">
        <item name="android:windowTranslucentStatus">false</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <item name="android:statusBarColor">@android:color/holo_red_light</item>
    </style>

效果和上面代码中添加的效果一样,这里就不贴效果图了。

图片延伸到状态栏

在Android 5.0 使图片延伸到状态栏,只需设置windowTranslucentStatus,将 statusBarColor 设置为透明即可:

<style name="ImageTranslucentTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
        <item name="android:windowTranslucentNavigation">true</item>
        <item name="android:windowTranslucentStatus">true</item>
        <!-- 设置statusBarColor 为透明-->
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>

效果如下:


代码中通过版本号的判断兼容 Android5.0以下和Android 5.0以上:

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            activity.getWindow().setStatusBarColor(calculateStatusColor(color, statusBarAlpha));
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
            int count = decorView.getChildCount();
            if (count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView) {
                decorView.getChildAt(count - 1).setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
            } else {
                StatusBarView statusView = createStatusBarView(activity, color, statusBarAlpha);
                decorView.addView(statusView);
            }

            ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
            rootView.setFitsSystemWindows(true);
            rootView.setClipToPadding(true);
            setRootView(activity);
}

2.3 Android 6.0 + 实现状态栏字色和图标浅黑色

使用沉浸式的时候会遇到一个问题,那就是Android 系统状态栏的字色和图标颜色为白色,当我的主题色或者图片接近白色或者为浅色的时候,状态栏上的内容就看不清了。 ,这个问题在Android 6.0的时候得到了解决。Android 6.0 新添加了一个属性SYSTEM_UI_FLAG_LIGHT_STATUS_BAR

解释:为setSystemUiVisibility(int)方法添加的Flag,请求status bar 绘制模式,它可以兼容亮色背景的status bar 。要在设置了FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDSflag ,同时清除了FLAG_TRANSLUCENT_STATUSflag 才会生效。

添加如下代码:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            getWindow().getDecorView().setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}

效果如下:


除了在代码中添加以外,还可以直接在主题中使用属性:

 <style name="MDTheme" parent="Theme.Design.Light.NoActionBar">
        <item name="android:windowTranslucentStatus">false</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <item name="android:statusBarColor">@android:color/holo_red_light</item>
        <!-- Android 6.0以上 状态栏字色和图标为浅黑色-->
        <item name="android:windowLightStatusBar">true</item>
    </style>

注意:主题要放在values-v23文件夹下:

三、轮子StatusBarUtil

通过上面的介绍,其实将各个版本实现沉浸式的方式和原理都讲完了。但是或许当你真正去实践沉浸式状态栏的时候,你会感觉到无从下手,因此,我给大家推荐一个轮子StatusBarUtil,Github:https://github.com/laobie/StatusBarUtil

为什么会推荐这个库呢?因为这个库就只有一个类StatusBarUtil,使用起来很方便,就像一个工具类一样使用。里面封装了很多静态方法,直接使用就好。自己添加也很方便。介绍一下使用的一些场景:

需要在setContentView()之后调用:

setContentView(R.layout.main_activity);
...
StatusBarUtil.setColor(MainActivity.this, mColor);
StatusBarUtil.setColor(Activity activity, int color)
StatusBarUtil.setTranslucent(Activity activity, int statusBarAlpha)
StatusBarUtil.setTransparent(Activity activity)
StatusBarUtil.setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, int color)

StatusBarUtil.setTranslucentForImageView(Activity activity, int statusBarAlpha, View needOffsetView)
status_uti

四、最后

以上就是对于沉浸式状态栏的一些总结,希望可以给还没有使用沉浸式的同学一些帮助。如果你已经使用过沉浸式状态栏,也不仿看一下,可以对各个版本实现的原理有一个更深的了解。最后,推荐了一个不错的库,更确切的说,应该是一个不错的工具类。如有问题,欢迎交流。

上一篇 下一篇

猜你喜欢

热点阅读