彻底搞定Android Kitkat+沉浸式状态栏效果

首先,我要实现的最终效果是这样的,即在Android4.4及以上版本系统上,统一显示为如下效果:


最终效果
最终效果

所谓“沉浸式”状态栏

这里所说的沉浸式状态栏,就是指上面的效果,状态栏和Toolbar的颜色保持一致,融为一体的效果。

版本差异及解决方法

本文所用的示例使用的style风格是NoActionBar的,标题栏使用的是Toolbar控件,请知悉。

Android4.4

Android4.4以前的版本,状态栏的颜色都是黑色的,而且无法修改;但一般APP的Toolbar都不会设置为黑色,于是,两者
有十分明显的颜色区分,各自占有不同的区域,填充不同的颜色。简单的说,Android4.4以前的版本是无法做到
沉浸式的效果的(做系统开发的除外),所以如果想要统一风格的话,可以设置APP最小支持的版本为4.4,如果
不行,就没办法了,只能4.4以前一个样式,4.4及以后一个样式。

Android4.4开始,新增了设置状态栏背景色透明的属性,新增的属性是这两个:

WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS 
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION

这两个属性分别设置状态栏和导航栏背景色为全透明。(这里只讨论状态栏,只设置上面一个属性就行,导航栏的类似)
实现方法很简单,在Activity初始化时,调用以下代码:

// Translucent status bar
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    Window window = getWindow();
    window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}

由于是代码设置,所以无法通过xml预览到想要的结果,一定要在4.4真机上运行才能看到效果。

到这里可能存在两个问题:

1. Toolbar把状态栏的空间占用了,挤到一起。

2. 状态栏和Toolbar并没有完全融为一体,而是从上到下,由黑色渐变到Toolbar的颜色。

第一个现象是必现的,解决方法是在Toolbar的布局文件里加上一个属性:

android:fitsSystemWindows="true"

这个属性必须加在Toolbar或者Toolbar的父控件上,也就是,如果Toolbar直接写在Activity的布局文件里,则在Toolbar上
加这个属性,如果Toolbar是include到Activity的布局文件里,则可以加到Toolbar的父控件里;

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    tools:context="com.chengsy.immersive.MainActivity">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorPrimary"
        android:fitsSystemWindows="true"
        app:title="@string/app_name"
        app:titleTextColor="#FFFFFF">

    </android.support.v7.widget.Toolbar>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Hello World!" />

</LinearLayout>

上面这种情况,如果写到linearlayout中,就会出现问题,StatusBar的背景色跟整个窗口的背景色一样(或者跟桌面颜色一样),而不是和Toolbar的
颜色一样。效果如下:

属性设置错误效果
属性设置错误效果

第二个现象根据不同的系统厂商,效果不一样,也就是不同的手机厂商做的不一样,没办法,但是不是太影响沉浸式的效果。
效果如下:

渐变的StatusBar
渐变的StatusBar

总结一下,4.4版本,需要设置两个地方来实现沉浸式效果:

  1. 设置状态栏透明
  2. 设置Toolbar的fitsSystemWindows属性为true

Android5.0

先看看通过上面的设置,程序运行在Android5.0的设备上是什么效果:
可能的效果也有两个,一个是我们想要的效果,沉浸式,不再配图了,另一个是这样的:

在Android5.0部分机器上的效果
在Android5.0部分机器上的效果

这种效果是状态栏的颜色上面覆盖了一层半透明的颜色,不是全透明。当然,原因你懂得,不同厂商系统设计师的idea是不一样的,
但是这里说一下,谷歌官方的Mertial Design的设计规范是如上图所示,并不是沉浸式的效果,但国内的APP现在普遍比较
喜欢沉浸式的效果。

那么如果要统一5.0的效果跟4.4的效果保持一致,全部都是沉浸式该怎么办???
这么办---
首先,Android5.0开始,系统又新增了设置状态栏颜色值的属性和接口:

WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS

通过这个属性来设置设置状态栏的背景色,来实现两者融为一体的效果,代码实现如下:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        getWindow().setStatusBarColor(Color.TRANSPARENT);// SDK21
}

原理很简单,首先要清空之前设置的FLAG_TRANSLUCENT_STATUS属性,然后添加修改状态栏背景色的属性FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
,最后给状态栏设置为全透明。

这里有坑,有的5.x的设备,如果调用这句代码
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
状态栏会变黑色,这时候需要去掉这句。但是去掉这句代码,在6.0+系统上半透明的状态栏又出现了。所以处理方法是判断如果是某种特殊的系统,
不调用上述代码。

Android6.0

对于Android6.0及以后的系统,对这方面的支持就已经很完善和统一了,通过4.4设置的FLAG_TRANSLUCENT_STATUS属性和
fitsSystemWindows属性就可以达到想要的效果了,不需要做特殊处理,版本内暂时不存在机型之间的差异。

但是6.0也不是完全没新的东西(指的是状态栏这一块内容),6.0新增了设置状态栏里内容色调的属性和接口,通过设置如下属性,可以把状态栏的文字
色调由亮色改为暗色,亮色是白色,暗色是黑色:

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

看名字感觉跟我的描素不一样啊,其实,这个属性是把状态栏标记为亮色调,从而,系统会自动把状态栏的文字内容变为
暗色调,如果你的APP的Toolbar颜色是亮色的,再配上亮色的内容就会不明显,上面的代码可以解决这个问题,自动将
状态栏的内容改为暗色调。

其他问题

  1. 输入框的兼容问题

    如果想要页面底部的输入框可以被键盘顶起来,并且不影响页面的沉浸式效果,需要做两点:

     - AndroidManifest.xml文件配置键盘属性:android:windowSoftInputMode="adjustResize"
     - Activity页面根布局设置 android:fitsSystemWindows="true" 属性
    

    布局代码如下:

     <?xml version="1.0" encoding="utf-8"?>
     <RelativeLayout 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:fitsSystemWindows="true"
         android:orientation="vertical"
         tools:context="com.chengsy.immersive.MainActivity">
     
         <android.support.v7.widget.Toolbar
             android:id="@+id/toolbar"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:background="@color/colorPrimary"
             android:fitsSystemWindows="true"
             app:title="@string/app_name"
             app:titleTextColor="#FFFFFF">
     
         </android.support.v7.widget.Toolbar>
     
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_below="@+id/toolbar"
             android:gravity="bottom">
     
             <EditText
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:text="Hello World!" />
         </LinearLayout>
     
     </RelativeLayout>
    

    通过上面的处理,状态栏的颜色不对了,变成了白色或者桌面的背景色,问题又来了。解决方法的原理就是给状态栏填充上颜色,
    但是,给状态栏修改颜色的属性和接口在5.0才出现,[4.4--5.0)版本的怎么办?用SystemBarTintManager,Github上的开源库,
    解决眼前的问题。使用很简单,引用这个库:

     compile 'com.readystatesoftware.systembartint:systembartint:1.0.3'
    

    代码中设置:

     private SystemBarTintManager tintManager;
     
     tintManager = new SystemBarTintManager(this);
     tintManager.setStatusBarTintColor(getResources().getColor(R.color.colorPrimary));
     tintManager.setStatusBarTintEnabled(true);
    
  2. 其他第三方控件的兼容,如ActionMode等

    有用到这个的自己去查、去尝试吧,这个控件好久不用了,也不知道什么效果,可以参考这篇文章里的解决方法:
    沉浸式状态栏实现及遇到的坑

  3. 系统厂商的兼容,如MIUI系统等

    这里也介绍了MIUI系统的适配问题,供参考:
    沉浸式状态栏实现及遇到的坑

总结

由于Android系统的开放性,以及系统厂商的不统一,导致Android系统的适配成为了另程序员头疼的一大问题,沉浸式的效果同样
不好做到完全统一样式,只能尽力而为之。通过上面的操作,基本可以保证大多数机型和系统的沉浸式效果,但仍然有个别无法适配的
系统或机型,这里也无法一一列举所有的情形,需要程序员们有针对性的设计代码,解决问题。

这里给出自己总结的一个方法,可以在BaseActivity中调用:

首先,Toolbar要设置fitsSystemWindows属性,如果页面包含EditText,需同时在页面根布局添加fitsSystemWindows属性;
其次,引用SystemBarTintManager库提供支持;
最后,调用如下代码:

private SystemBarTintManager tintManager;
protected void initWindow() {
    // 4.4及以上版本设置状态栏透明
    Window window = getWindow();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        // Translucent status bar
        window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    }

    // 解决4.4-5.0版本之间,页面包含EditText无法适配的问题
    {
        // create our manager instance after the content view is set
        mTintManager = new SystemBarTintManager(this);
        // enable status bar tint
        mTintManager.setStatusBarTintEnabled(true);
        // enable navigation bar tint
        mTintManager.setNavigationBarTintEnabled(true);

        // 自定义状态栏的颜色
        mTintManager.setStatusBarTintColor(getResources().getColor(R.color.colorPrimary));
    }

    // 解决[5.0-5.1.1]版本状态栏没有全透明的系统适配问题
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        // 解决部分5.x系统使用状态栏透明属性后状态栏变黑色,不使用这句代码,在6.0设备上又出现半透明状态栏
        // 需要特殊处理
        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);
    }
    
    // 把状态栏标记为浅色,然后状态栏的字体颜色自动转换为深色。
    // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    //     getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
    // }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,506评论 25 707
  • 前言 原文:http://blog.csdn.net/mybeta/article/details/5076032...
    naturs阅读 23,081评论 8 70
  • 背景 上篇文章一个千万量级的APP使用的一些第三方库中,在说到一个使用很广泛的滑动退出库SwipeBackLayo...
    Ziv_xiao阅读 16,655评论 22 127
  • 前言 首先请大家看几张图: 以上的效果,一般我们统称为沉浸式状态栏。其实,这种叫法不是很准确,而且也没有沉浸式状态...
    宇是我阅读 3,799评论 2 28
  • 交朋友这件小事,很像狗熊掰玉米。 从幼时到现在,掰一路扔一路。 末了驻足一看——咦,没捞着几个嘛?! 幼时和同院的...
    石长生之阅读 770评论 3 2