沉浸式状态栏全面解析

前言

其实网上关于沉浸式状态栏的文章有很多,但是我发现基本上都有一个特点,就是先巴拉巴拉地讲一大堆概念,然后接着推出一个自己写的轮子。不是说这类文章不好,而是这类的文章往往读完了让人抓不住重点,只想着最后那个轮子,而我本人是非常不推荐使用别人写好的轮子,首先这类轮子面对很多不同场景的情况不能百分之百满足使用需求,其次过度地使用轮子往往会让开发者不了解代码到底是怎么实现沉浸式的。基于以上情况,就有了这篇文章。

准备知识

实现沉浸式状态栏主要跟以下四个Api相关:

  • View#setSystemUiVisibility()
  • Window#addFlags()
  • View#setFitsSystemWindows
  • Window#setStatusBarColor()
    下面我会详细地讲一下这四个方法

View#setSystemUiVisibility()及其各种Flags

首先setSystemUiVisibility()这个方法就是设置状态栏或者导航栏的各种属性的,哪都有些可以设置的属性呢?

  • View.SYSTEM_UI_FLAG_FULLSCREEN:
    视图全屏并隐藏状态栏,当用户交互时(如下滑状态栏)会恢复隐藏的状态栏(例子:电子书阅读)缺点:进入Activity会产生一个从非全屏到全屏的闪动效果


    getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN)
  • View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY:
    粘性沉浸模式,需要和SYSTEM_UI_FLAG_FULLSCREEN或者SYSTEM_UI_FLAG_HIDE_NAVIGATION联用,当使用View.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN|View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)联用时视图全屏,当用户产生交互时(如下滑状态栏)不会恢复状态栏,只会以半透明的方式覆盖在视图上面并在一定时间内自动消失


    getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN|View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
  • View.SYSTEM_UI_FLAG_IMMERSIVE:
    沉浸模式,只能和SYSTEM_UI_FLAG_FULLSCREEN联用,效果和View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY一样,目前已被后者替代

  • View.SYSTEM_UI_FLAG_LOW_PROFILE:
    低配模式,会隐藏一些不重要的状态栏和导航栏的图标

  • View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN:
    视图全屏且不会产生闪动,状态栏会覆盖在视图上面

  • View.SYSTEM_UI_FLAG_LAYOUT_STABLE:
    使视图稳定,当使用fitSystemWindows()(下面会单独介绍这个方法)需要视图稳定,一般和View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN联用

  • View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR:
    Android6.0系统以上增加的属性,设置了这个属性,状态栏会以与状态栏背景颜色兼容的模式绘制。什么意思呢?就是说如果当前的状态栏颜色是浅色,那么就有可能造成状态栏上的图标看不清了,但是如果你设置这个属性以后,状态栏的图标就会以深色绘制,这样就没有什么UI上的问题了。

Window.addFlags()及其各种Flags

WindowManager.LayoutParams相关属性:

  • FLAG_TRANSLUCENT_STATUS:
    Android4.4系统增加的属性,它会使状态栏透明透明并且自动执行View.SYSTEM_UI_FLAG_LAYOUT_STABLE和View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

  • FLAG_FULLSCREEN:
    视图全屏并隐藏状态栏,效果相当于View.SYSTEM_UI_FLAG_FULLSCREEN+View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY,并且视图稳定(不会因为系统控件的变化(如输入法),而重新布局)

  • FLAG_FORCE_NOT_FULLSCREEN:
    重写了FLAG_FULLSCREEN并强制显示状态栏(没有啥卵用)

  • FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS:
    Android5.0系统以上支持,如果设置了该属性,系统栏(状态栏和导航栏)将以透明背景绘制,并且该窗口中的相应区域将填充setStatusBar()和setNavigationBarColor()中设置的颜色

View#setFitsSystemWindows

此方法只有当设置SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN或者SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION才有有效,当窗口发生变化时,View需要调整自身内容以适应窗口的变化,你可以理解为当和SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN一起联用的时候,是给View加了个bottomTop属性,宽度填充视图,高度就是状态栏的高度;当和SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION一起联用的时候,是给View加了个bottomBottom属性,宽度填充视图,高度就是导航栏的高度,建议给布局的顶层ViewGroup使用

Window#setStatusBarColor()

Android5.0系统及以上开始支持,设置状态栏的颜色,为了使这个状态有效必须要设置FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS而且不能设置FLAG_TRANSLUCENT_STATUS

小结

上面的4个Api方法和其中的一些Flags非常重要,是实现沉浸式状态栏的关键,我都介绍得比较详细,只要你认真读下来,相信你已经对怎么实现沉浸式状态有了一定的思路了。

实现沉浸式状态栏的具体套路

实现沉浸式状态栏分为三个阶段,

  • Android4.4~Android5.0以下;
  • Android5.0~Android6.0以下;
  • Android6.0以上;
    下面我将分别详细介绍一下

Android4.4~Android5.0阶段以下:

Android真正可以实现沉浸式状态栏是从4.4开始的,因为4.4系统加入了一个重要的属性Window.LayoutParams.FLAG_TRANSLUCENT_STATUS,这个属性能干嘛,前面已经说过了,下面直接开搞。
首先,沉浸式状态栏一般有两种情况,一种是背景是一张图片,一种是颜色跟标题栏一致的

  • 背景是一张图片的情况:非常非常好实现,一行代码直接搞定
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

这里简单解释一下,FLAG_TRANSLUCENT_STATUS这个属性会让状态栏以白色绘制,同时还会执行SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN和SYSTEM_UI_FLAG_LAYOUT_STABLE这样就会让状态栏浮在图片的上面,这样就形成了沉浸式的效果,怎么样,是不是很简单?看一下效果


背景是一张图片的情况.jpg
  • 颜色跟标题栏一致的情况:这种情况在4.4~5.0以下这个阶段实现起来就稍微复杂了一点,因为这个阶段还有执行setStatusBarColor()这个方法,因此这个阶段实现的套路是,先制造一个假的View背景颜色跟标题栏的颜色一致,高度跟状态栏的高度一致,添加到顶层DecorView上面,然后让Android的最顶层的内容布局调用setFitsSystemWindows空出来状态栏的高度,最后调用FLAG_TRANSLUCENT_STATUS这个属性即可,让状态栏透明并浮在假View上
            View statusView = new View(activity);
            ViewGroup.LayoutParams statusViewLayoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));
            ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
            decorView.addView(statusView, statusViewLayoutParams);
            ViewGroup rootView = decorView.findViewById(Window.ID_ANDROID_CONTENT);
            if (rootView != null) {
                rootView.setFitsSystemWindows(true);
            }
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

看一下效果


颜色跟标题栏颜色一致.jpg

注意:从两张效果图的图片可以看出,沉浸式状态在这一阶段的表现还不是太好,上面会有一层黑色的半透明浮层,但是也基本可以实现了沉浸式。

Android5.0~Android6.0以下

这一阶段Android系统为我们增加了一个非常重要的API,Window#setStatusBarColor(),关于它的用法前面已经有所介绍,这里不再赘述,以及一个配合这个api使用的属性FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,直接开搞

  • 背景是一张图的情况
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            activity.getWindow()
                    .getDecorView()
                    .setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);

这里简单解释一下,我们把状态栏的颜色设置成透明,同时让状态栏浮在视图上面且保持稳定,这样图片就会顶到视图的顶部,因此就实现了沉浸式的效果,代码很简单,万一看不懂就翻翻上面对各种api的介绍,最后再来看一眼效果图


5.0~6.0 背景是一张图片.jpg
  • 颜色跟标题栏一致的情况
    有两种实现方式
    方式一:直接给状态栏设置对应的颜色
 activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            activity.getWindow().setStatusBarColor(color);

方式二:给状态栏设置透明色并让状态栏浮在视图顶层,配合setFitsSystemWindows()

 activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            activity.getWindow().setStatusBarColor(Color.TRANSPARENT);

toolBar.setFitsSystemWindows(true);//这里根据自己的布局情况

方式一和方式二都可以实现沉浸式状态栏,大家可以根据自己的业务情况去选择,这里我举个简单的例子,比如,我当前在进入页面的时候会加载一张骨架图,如果我采用方式一的沉浸式状态栏就有下面的问题,上图


骨架图的情况.jpg

这种情况的话,骨架图没有顶到视图的顶层,在加载过程中就会显得不是很好看,而如果用方式二的话就不存在这种情况,效果大家知道我就不上图了,举这个例子就是想告诉大家,要根据业务场景选择合适的实现方式。

Android6.0及以上

之所以又分了这一个阶段,是因为我们在5.0~6.0阶段发现了一个小问题,就是当我们要给状态栏设置的颜色是白色或者浅色的时候,因为默认的状态上图标的颜色是白色就会造成看不清的现象,如下图所示


浅色状态栏.jpg

为了避免这种情况,Android系统在6.0的时候增加了一个属性SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,此前已经介绍过这个属性,这里不再赘述,使用了它以后,状态栏上的图标文字就会默认使用黑色绘制。

activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            activity.getWindow().setStatusBarColor(color);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
            }

设置完以后的效果


设置了浅色主题的情况.jpg

这里再补充一个小知识:小米和魅族系统在Android5.0修改自家系统的源码,因此他们在5.0的时候就提供这个API,也就是说小米和魅族Android5.0系统的手机就支持了浅色主题模式。

为什么我不建议大家使用轮子

其实我看网上关于沉浸式的轮子,都是基于以上方法封装的一个工具类而已,我不推荐大家使用轮子,是因为当你自己的业务场景有特殊要求的时候(例如骨架图的情况),可能当前的轮子会不适应你的业务场景,所以掌握好核心知识才是最关键的,如果一定要用轮子,你掌握了这篇文章,自己造一个岂不是更好吗?

总结

本文首先介绍了关于实现沉浸式状态栏的4个核心api,并对其关键的属性做了详细的描述,然后又介绍了沉浸式状态栏发展的三个阶段,并对特殊情况的一些处理,相信你只要认真读了这篇文章,你一定已经掌握了沉浸式的核心知识。如果你觉得本文不错,请点个赞,谢谢!

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

推荐阅读更多精彩内容