沉浸式状态栏技术方案及其中的一些坑

沉浸式状态栏

沉浸式状态栏是android 4.4开始支持的一个feature,在软件打开的时候通知栏和软件顶部颜色融为一体,这样不仅可以使软件和系统本身更加融为一体,而且让用户注意力更加集中在内容上。

在ios上这是一个很早就支持的功能,但在android上则一直只能看到黑色的状态栏。google mail在抽屉上成功展示了沉浸式状态栏,可见想要把内容显示在状态栏肯定是能做的。

Gmail effect

技术方案

虽然google官方文档和网上那些技术文章中,都轻描淡写地用

1
<item name="android:windowTranslucentStatus">true</item>

或者

1
2
3
4
5
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window window = getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}

但设置后,则会把状态栏当做应用可用区域的一部分,而导致应用标题栏的图标/文字显示到状态栏上,和原有时间等区域重合。而设置android:fitsSystemWindows=”true”则只是把整个window的背景色延展到了状态栏,实则是伪沉浸式。
qzone effect

所以这里要做的是如何在做好沉浸式的同时不让状态栏本身内容被影响。

大致实现

  • 升级API LEVEL到19(不需要修改targetSdkVersion,只需要编译使用新的sdk)
  • 对应用和activity的theme在values-v19的style.xml里重写,设置android:windowTranslucentStatus为true
  • 对title bar的xml做一些简单修改,比如min height都做到dimens.xml里面,root layout上加一个paddingTop,分别在values/dimens和values-v19/dimens设置为0dp和25dp(android默认的状态栏高度)。
  • 把所有对标题栏相关的不规范padding/margin引用都纠正为使用dimens内的。
  • 自定义标题栏组件,避免在layout里面的冗余导致以后修改困难,以及在所有activity重复初始化标题栏。

遇到和解决的一些坑

  • 很多rom把渐变阴影给去了(CM、miui、Flyme OS等等)
    大部分应用了沉浸式状态栏的应用都没有考虑到这点,如腾讯地图,导致在那些rom上打开应用后状态栏一片白乎乎的看不清。
    尽管google的原生4.4 rom中,在设置了沉浸式状态栏后,会对状态栏区域加上一条渐变的背景,来防止亮色导致状态栏图标/字看不清,但实际应用中发现其实很多rom把渐变阴影给去了,所以在状态栏组件中,加上了绘制阴影的选项(包括5.0半透明黑条和4.4渐变阴影两种选项),会在4.4机器开启了沉浸式状态栏时,绘制阴影。

  • 状态栏高度不一致
    部分rom(如miui和Flyme等)修改了状态栏高度,miui改高了,而meizu上的flyme则改矮了,所以不能直接写作25dp。在CustomTitleBar组件中通过重写getPaddingTop方法来兼容所有状态栏高度。

  • 插件和没有标题栏的activity
    插件和一些没有标题栏的activity的layout都因为沉浸式状态栏而乱了。如果不需要标题栏的话,可以直接通过在layout root上加上android:fitsSystemWindows=”true”来自适应,否则同样需要做重写style样式(因为目前插件还不能直接引用主工程资源)

  • 既知的页面resize bug
    一个已知的在设置沉浸式状态栏后的bug:https://code.google.com/p/android/issues/detail?id=63777。导致在软键盘弹出后页面没有resize,内容被键盘遮住,adjustPan不起作用,如写说说、写日志这些界面。直接在需要relayout的子view上添加fitsSystemWindows属性。

待完善点

  • 目前统一在4.4上添加了渐变阴影,而在5.0则维持系统原样(会有一条半透明黑色bar),后期可以看看是否通过白名单在部分没有修改原生状态栏行为(保留了渐变阴影)的rom上不进行重复的阴影绘制,需要有很多机器可以确认。
  • 小米的miui上,部分原生应用有把状态栏字体图标改变颜色的行为,研究了一下是rom原生带有普通模式和dark模式两种的状态栏字体颜色,而不是标准的android api,可以通过反射修改:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public void setStatusBarDarkMode(boolean darkmode, Activity activity) {
    Class<? extends Window> clazz = activity.getWindow().getClass();
    try {
    Class<?> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
    Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
    int darkModeFlag = field.getInt(layoutParams);
    Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
    extraFlagField.invoke(activity.getWindow(), darkmode ? darkModeFlag : 0, darkModeFlag);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
Mark Zhai (翟一帆) wechat
欢迎您扫一扫上面的微信公众号,订阅我们的公众号!
坚持原创技术分享,您的支持将鼓励我继续创作!