Android 沉浸式状态栏原理

首先: 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基类的封装

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,448评论 25 707
  • 一、ActionBar(标题栏) android.app.ActionBar是 Android 3.0(API 1...
    SharpChen阅读 1,498评论 0 10
  • 一提到沉浸式状态栏,第一个浮现在脑海里的词就是“碎片化”。碎片化是让 Android 开发者很头疼的问题,相信没有...
    扬州慢_阅读 176,094评论 30 300
  • 前言 网上已经有很多有关于系统状态栏的解决方案,这篇文章也不会有什么新奇的解决方案,都是本人经过自己试验,统计提炼...
    btman阅读 127,797评论 33 211
  • 秋风异能者,能落叶,能杀生。能把有形之物灭于无形。 病殃殃的气息,掩盖了秋日繁华。有一种信息,因它而变...
    10086好阅读 188评论 0 0