美文网首页
Android 沉浸式状态栏原理

Android 沉浸式状态栏原理

作者: 耳_总 | 来源:发表于2017-11-20 10:04 被阅读157次

    首先: android 的透明状态栏和沉浸式是两个不同的东西,但是又相互交错,不要混淆。透明状态栏是指将状态栏设置为透明或者半透明,沉浸式是指,将页面的布局“沉浸”在状态栏下面,如果这时候将状态栏设置为透明,那么状态栏和手机页面看以来是一个整体,增大了屏幕的利用空间。如果设置了沉浸式状态栏不透明,毫无疑问布局将会被状态栏遮挡。

    在之前先讲讲Android的主题设置,和将状态栏设置透明带来的变化。
    对于现在开发项目我们一般使用的是 Theme.AppCompat.xxx兼容包里面的主题

    <style name="AppTheme1" parent="Theme.AppCompat.Light.DarkActionBar">
            <!-- Customize your theme here. -->
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorPrimary</item>
        </style>
    

    为了兼容不同版本,会建立多个版本兼容的文件夹


    image.png

    比如:

            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorPrimary</item>
            <item name="android:windowTranslucentStatus">true</item>
            <item name="android:windowTranslucentNavigation">true</item>
            <item name="android:statusBarColor">@android:color/transparent</item>
    

    前三个属性分别表示表示的颜色用一张经典的图来对应表示:

    image.png
    分别代表toolbar的颜色、状态栏的颜色、输入法和radiobuttion等的颜色,但是需要注意的是:
    在5.0以下的版本,状态栏的颜色通过主题设置是没有用的,用以上的主题属性来设置状态栏颜色,在5.x以下是黑色,自己可以试试。

    后三个属性是来设置透明状态栏的,android:windowTranslucentStatus、android:windowTranslucentNavigation是4.4以后才有的属性,android:statusBarColor是5.0以后才有的属性,所以才要分为几个value文件夹来存放不同的主题,好在as现在非常智能,如果你在兼容低版本的情况下,用了高版本的api会发出警告。

    设置状态栏透明

    那么设置状态栏透明又会是什么样子呢?(分析4.4以上的版本)
    有两种方式设置状态栏透明,第一种是上面的通过改变主题的方式,这里贴出value-v19、v21的主题参数

    v19
    <style name="AppTheme1" parent="Theme.AppCompat.Light.NoActionBar">
            <!-- Customize your theme here. -->
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorPrimary</item>
            <item name="android:windowTranslucentStatus">true</item>
            <item name="android:windowTranslucentNavigation">true</item>
        </style>
    ========
    v21
    <style name="AppTheme1" parent="Theme.AppCompat.Light.DarkActionBar">
            <!-- Customize your theme here. -->
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorPrimary</item>
            <item name="android:windowTranslucentStatus">true</item>
            <item name="android:windowTranslucentNavigation">true</item>
            <item name="android:statusBarColor">@android:color/transparent</item>
        </style>
    

    主要区别只一个用了actionbar主题,一个没用,这里是为了尝试看到更多变化的效果,对这里透明状态栏没影响。


    image.png

    正对以上运行效果。说明下:

    • 4.4版本和5.x版本设置状态栏是有差异的,4.4的状态栏和底部导航栏都变半透明,但是颜色是渐变的,而5.x以上是变灰色
    • windowTranslucentStatus属性改变状态栏成半透明,并且布局网上顶,充满全屏
    • windowTranslucentNavigation属性改变底部导航栏颜色为半透明
    • statusBarColor 5.x才有设置状态栏颜色,4.x不是不能设置颜色的。

    这里对上面扩展下:
    为什么setContentView的布局会顶上去呢?再来一张窗口的层级的经典图片


    image.png

    statubar状态栏是系统窗口,DecorView是视图的顶级窗口,继承自FramLayout,他的子View一个是actionbar,和一个id为content的FramLyout,这个content就是setContentView的那个布局,通过AS的工具也可以看出来。

    image.png

    至于为什么设置了透明主题属性就会把contentView顶上去呢?这里没有去探究源码,暂且就这么认为吧——如果设置了透明状态栏,decorview和content之间的margin为0,这时候content布局的大小就是整个屏幕的大小;如果没有设置,则margin为状态栏的高度加上actionbar的高度(如果有)。但是actionbar没有顶上去,但是有什么方法不让顶上去呢?有两种方法。
    1、将跟布局添加:android:fitsSystemWindows="true|false"

    <android.support.v7.widget.LinearLayoutCompat
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:fitsSystemWindows="true"
        android:gravity="start"
        android:background="#ffffff"
        tools:context="com.xc.test.MainActivity">
    
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:scaleType="fitStart"
            android:src="@drawable/aaa"/>
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"/>
    
    </android.support.v7.widget.LinearLayoutCompat>
    

    2、代码设置

    window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
    

    注意:android:fitsSystemWindows这个属性只有在主题中设置了[windowTranslucentStatus|windowTranslucentNavigation]属性或者代码中调用window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);才会生效。(我目前的验证是这样)

    利用代码设置状态栏透明
    // 设置状态栏全透明
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
                //设置根布局顶上去
                window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
                window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                window.setStatusBarColor(Color.TRANSPARENT);
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            }
    
    image.png

    在这里看到6.x的模拟器状态栏为什么不是灰色呢?最后找到了答案,原来通过代码设置的透明和通过主题设置是有区别的


    image.png
    状态栏着色
    • 5.x以上的版本
     window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
     window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
     window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
     window.setStatusBarColor(setStatusBarColor());
    
    image.png

    window.setStatusBarColor(setStatusBarColor());这个方法设置状态栏的颜色5.x以后才有的api,4.4版本需要做特殊处理。

    大家发现没有,我一直在6.x的模拟器上设置了一个actionBar,然而这个actionbar没有沉浸到状态栏下面,正是如此,我们只能舍弃actionbar使用toolbar了,在页面的布局中加入toolbar

    <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@color/colorPrimaryDark"
            app:title="toobar"
            >
    
        </android.support.v7.widget.Toolbar>
    
    image.png

    toolbar随着布局顶到状态栏下面,其实toolbar有两种用法:
    一种是设置没有actionbar的主题(你也可以设置,但是这样会重叠),将toolbar像上面一样嵌入布局中,toolbar就是一个普通的布局,实质上toolbar是继承的ViewGroup,这样没毛病。
    另一种是setSupportActionBar(toolbar),让toolbar替代actionbar,如果是这样的话,主题必须是noActionBar的,不然抛异常,这样子toolbar就会继承actionbar的一些特性,比如设置透明状态栏之后不会将布局顶上去,而且还为你把toolbar填充了高度。当然前提是不能调用window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);如果调用了这个方法,照样会顶上去的,也可以用fitsSystemWindows来配合使用,多尝试。


    image.png

    就这样,当状态栏为透明,toolbar又顶上去了,这样就显得整个主题风格一致,这就是沉浸式。
    还有就是app最上端为图片的时候希望这样设计:


    image.png

    这叫透明状态栏,反正也没个规范的说法,我就这么叫的。

    4.4状态栏着色

    4.4版本唯一一个和5.x版本不一样的地方就是4.4没有改变状态栏颜色的api,只有改变透明度的api,那怎么解决这个问题呢?
    我们都知道,整个app展现在我们眼前的最外层的视图为DecorView,实质上是一个FramLayout,状态栏是系统的窗口,覆盖在DecorView之上的,DecorView里面有我们的content,既然是这样,我们可以在DecorView里面加一个布局,覆盖在content之上,通过设置状态栏透明,改变这个View的颜色,这样就造成了改变状态栏颜色的假象。这些事情早就有人遭了轮子,在这里就不重复了。
    StatusBarUtil

    总结:

    • 4.4以上才有沉浸式状态栏
    • 设置状态栏透明或者任意颜色(可以通过主题和代码方法)使得布局顶到状态栏下面
    • toolbar替换actionbar使得沉浸式得以实现

    下一篇写toolbar的使用和沉浸式BaseActivity基类的封装

    相关文章

      网友评论

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

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