沉浸式状态栏方案

前言

沉浸式(透明)状态栏是Android4.4及以后的版本出现的,其可以通过改变状态栏的颜色/透明度实现与activity的联动效果,淘宝/京东/qq朋友圈等均实现了沉浸式状态栏的效果.

这里一定要提一下StatusBarUtil,此项目为朋友推荐用来参考的项目,本着不重复造轮子的原则,本想着拿来就用,结果:

  1. demo在三星note4上面直接就安装失败?API21的也会失败?
  2. 为了使用方便而牺牲了针对性,导致每个方面的使用都很不尽如人意.
  3. 项目有着5000+的star,本以为是个好项目,然而评论区大量的意见和BUG作者视而不见?这5000+是刷出来的?
  4. 最重要一点,该项目的状态栏一旦设置为透明,那么底部的虚拟按键也会是透明,这就造成了,类似于京东,QQ等底部有tab的页面直接无法使用.无法仅仅设置状态栏透明.这么重要的功能没有考虑到?
  5. 最让我气愤的一点:网上有关沉浸式状态栏的文章,要么特别简单,要么直接抄袭StatusBarUtil,国内抄袭成风?脑子呢?浪费时间

API 19 及以上

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

通用方法,直接设置Windows窗口半透明/透明/渐变式透明
透明效果随系统改变

经测试
在三星note4是渐变式透明
小米note3手机全透明
魅族MX4半透明

此时activity主视图居于状态栏下方,状态栏浮于视图上方.此时有Z轴效果

API 21及以上

设置颜色的API只有在21及以上才有.但是设置颜色和状态栏透明的FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS 不能共存

Step1

/**
  * Flag indicating that this Window is responsible for drawing the background for the
  * system bars. If set, the system bars are drawn with a transparent background and the
  * corresponding areas in this window are filled with the colors specified in
  * {@link Window#getStatusBarColor()} and {@link Window#getNavigationBarColor()}.
  */
public static final int FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000;

首先添加Flag,该flag表示可以对状态栏设置颜色,默认为全透明

Step2

/**
  * Window flag: request a translucent status bar with minimal system-provided
  * background protection.
  * <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;

移除此Flag,由文档可知此Flag用于设置状态栏透明,并且背景由系统提供.该FlagFLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS不能共存.

如果不再进行其他的设置,仅设置状态栏颜色的话,那么这就是伪沉浸式状态栏,此时视图未延伸至状态栏下方,未出现Z轴的层次效果.通过颜色的视觉效果营造沉浸式状态栏的感觉.

如果设置此Flag会默认设置系统UISYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,即主视图占据全屏,此时主视图会延伸至状态栏下方,后文再进行探讨.

Step3

网上一直流传一个说法(百度搜索到的'抄袭者'们):要么使用系统的默认透明,要么只能设置颜色,无法把视图延伸至状态栏下方.你们都是猪吗,没有解决方案的话淘宝,京东,QQ是怎么做的?

以下为解决方法1:
StatusBarUtil直接添加了一个Flag

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

虽然状态栏透明和设置状态栏颜色不能共存,那么可以设置虚拟键透明啊,这是个曲线救国的方法,比较有趣的是,该Flag不仅可以使虚拟键透明,同时可以使状态栏透明.该方法同时也是StatusBarUtil整个项目中的状态栏透明方法.
缺陷:无法单独设置状态栏透明,底部虚拟键不透明.对于部分布局不够友好

思路:
之前我也一直在纠结这个问题要怎么解决,一直在想,decor怎么就不能有个单独设置状态栏的方法呢,很遗憾,事实上没有这种方法,那么为了所有设备显示一致的效果,只能设置状态栏和全透明了.检测虚拟键是否启用,去预留下边距进行曲线救国

结果:对于小米全面屏,检测虚拟键一直返回true,并且由于虚拟键透明,那么开启虚拟按键时将会模糊不清,失败.

于是继续查找官方文档,有了完美解决方案

解决方案2:
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
没错,上文提到的SYSTEM_UI_FLAG_LAYOUT_FULLSCREENFlag.

之前一直被decorview的flag所局限,一直认为会有能解决的Flag可以使用.后面再阅读源码时,发现在设置Flag时都会自动去设置系统UI的属性,由此找到切入点.

SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN属性可以使activity所填充的视图可以延伸至statusbar下方,形成Z轴的重叠效果,并且不会延伸至虚拟键下方,完美解决问题.
当然同样也有属性可以使视图延伸至虚拟键下方,有兴趣的读者可以自行查阅文档

Step4

此时已经完成沉浸状态栏效果,但是大家会发现部分视图元素会与状态栏的图标重叠,网上很多的文章,包括StatusBarUtil的作者也是通过在状态栏下方添加登高的色块来避免重叠,当顶部视图是图片时就不用色块,让图片自然在状态栏下方显示,那么就会出现以下问题:

  1. 当顶部有多个视图重叠,同时需要延伸至顶部怎呢办
  2. 当顶部视图颜色会动态发生变化怎么办.
  3. 当顶部视图是图片时,同时不希望发生拉伸时怎么办
  4. 当顶部视图是图片背景,内部有大量的UI控件,无法用色块扩展怎么办

这时,色块的作用局限性就被放大了,我所采用的方法是,增大高度,添加padding,动态更改margin进行适配.以下为通用的方法步骤:

  1. 高度在原基础上增加50dp;
  2. 上边距在原基础上增加50dp;
  3. 测量状态栏高度.
  4. marginTop的值为负数,其绝对值为:50dp对应的px值减去状态栏的高度

注意:
高度增加50dp为较为稳妥的写法,在刘海屏出现之前,高度为固定25dp,在使用小米8后发现这个高度仅仅填充了大半,此时需要填充较为充足的预留高度.当然直接写死增大50dp的高度可能仍然会有些拉伸,大家可以动态去计算高度
marginTop设置为负数相信大家都有过类似操作的经历,比如下拉刷新,比如网页里面通过margin进行定位等等,我也是从这里得到的灵感.
高度需要设置为固定值,无论是在xml设置还是通过params设置,否则会导致部分机型出现过度拉伸

至此已完成沉浸式状态栏,至于其他的扩展功能需要大家发散思维.这里仅提供解决方案和思路

Last modification:November 11th, 2018 at 09:42 am
如果觉得我的文章对你有用,请我喝杯咖啡

Leave a Comment

2 comments

  1. jiangzz

    这个也是伪状态栏。有机会去看看我写的

    1. bt
      @jiangzz

      嗯嗯,留个地址哈,我去看看