沉浸式状态栏

96
chandarlee
2016.04.16 12:36* 字数 2208

何谓沉浸式状态栏##

说白了,沉浸式状态栏本质上就是给系统状态栏着色。当这个颜色和我们Activity中的ToolBar或者ActionBar所使用的背景颜色一致时就会有沉浸式的效果。

怎么给状态栏着色##

那么我们要怎么样才能给系统状态栏着色呢?谷歌后知后觉,终于在API 21Window类中添加了相应的方法,方法声明如下:

public abstract class Window {
  /**
  For this to take effect,the window must be drawing the system bar backgrounds with 
  android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS and 
  android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS must not be set
  **/
  public abstract void setStatusColor(int color);
}

具体的实现在PhoneWindow类中,这里不再深入。既然方法都有了,那么直接调用就行了。这里我们在Activity中将状态栏颜色设置为红色

  Window window = getWindow();
  window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
  window.setStatusBarColor(Color.parseColor("#FF0000"));

注意,上面的代码假设当前系统API Level >= 21,因为只有满足条件的SDK版本才能找到该方法;与此同时,在设置状态栏颜色的同时,API文档 告诉我们还需要同步设置WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS这个Window Flag,并且需要保证WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS这个Window Flag没有被设置。否则,不会生效

在调用任何一个不熟悉的方法时,请首先仔细阅读一下API 文档

上面,我们通过方法调用给系统状态栏着色;当然也可以通过指定Theme来完成;

    <style name="MaterialAppTheme" parent="android:Theme.Material.Light">
        <item name="android:colorPrimaryDark">#FF0000</item>
        <item name="android:statusBarColor">#00FF00</item>
    </style>

Material Theme中继承,覆写android:statusBarColor属性,指定具体颜色值即可。Material Themeandroid:statusBarColor属性的值默认使用android:colorPrimaryDark属性指定的值;所以我们也可以仅仅指定android:colorPrimaryDark属性;
  如果因为某种原因,不从Material Theme中继承,那么就只能老老实实地指定特定的属性不能偷懒,这些属性包括android:statusBarColorandroid:windowDrawsSystemBarBackgrounds

    <style name="CustomAppTheme" parent="android:Theme.Light">
        <item name="android:statusBarColor">#00FF00</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
    </style>

这里,我们只从Theme.Light中继承,那就不要指望它能像Material Theme那样帮我们做一些事;android:statusBarColor必须指定,因为它不再有默认的指向android:colorPrimaryDark;此外,android:windowDrawsSystemBarBackgroundstrue必不可少;就像上面使用setStatusColor方法时需要注意的那样,这个属性相当于添加了FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS(当然你也可以不在Theme中指定这个属性,使用如上代码那种方式添加Window Flag);而从Material Theme中继承时没有那样做,是因为Material Theme中它默认值为true

注意,上面Theme的声明,对应的资源文件应该在values-v21文件夹下。因为不管是相应的属性,还是对应的Material Theme都是至少API 21才能使用的

兼容低版本

OK,到此为止,我们所讨论的都是基于API 21以上的。如果低版本该怎么办?低版本的系统是不支持给状态栏着色的,但却可以通过 透明状态栏+透明背景颜色 来实现相同的效果;废话不多说,来看实现。

将系统状态栏设置为透明

这是第一步。可以通过代码方式

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

或者Theme Attribute的方式

  <item name="android:windowTranslucentStatus">true</item>
设置对应背景颜色

接下来,就是背景颜色的设置。首先需要将ActionBar或者ToolBar的背景颜色设置为我们需要的颜色,具体如何设置不再深入,请自行研究(这里,如果没有使用到ActionBar或者ToolBar,这一步可直接略过)。

其次,设置Activity根布局的背景颜色为一致的颜色;Just a piece of cake!

最后,我们还需要做一个调整。当设置了状态栏为透明后,Activity会相当于一个FullScreen的全屏设置,窗口会占满整个屏幕,整体的内容会往上移动一段状态栏高度的距离,这样就会导致状态栏覆盖到我们的内容。这时,我们需要在根布局上设置android:fitsSystemWindows="true",这样系统会帮我们重新调整窗口的位置避免出现覆盖的情况(无非就是给我们的窗口加上一个padding值);

注意,上面透明栏+背景色的方式只适用于API 19以上,因为这个版本以上的系统才支持透明化状态栏,所以,19以下的系统不支持沉浸式状态栏

其实系统的导航栏在API 19以上同状态栏一样也支持透明化和设置背景颜色,但这不是本文内容

关于透明化状态栏

上面说到android:windowTranslucentStatus可用于API 19以上的版本透明化状态栏;但请注意,在19版本和19版本以上该属性生效时存在差异。具体表现如下(这里盗用stackoverflow上的一张图说明)

n0aYT.png

上图中,左边为19版本的显示效果,右边为21版本的效果;我们可以从图中看到比较明显的差异。在19版本中,系统给SystemBar添加了一个渐变,而21版本的则是一个透明的纯色。如果我们使用android:windowTranslucentStatus在21版本及以上来实现沉浸式的应用,则最终效果将不会太理想;那么是不是就不能实现了呢?No!

21版本以上的透明系统栏需要使用android:statusBarColor = "@android:color/transparent"来实现;这里android:windowTranslucentStatus肯定是为false的,因为这两个属性是不能同时生效的。但是由于android:windowTranslucentStatus属性的禁用,状态栏将不再会是浮在我们的window上。没关系,我们可以通过下面的方法达到一样的效果:

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);

其实设置android:windowTranslucentStatus属性时,正是系统帮我们设置了上面的Flag;上面我们在DecorView上调用这个方法,但其实可以在任何一个可见的View上进行调用,效果是一样的。下面再补充说明一下setSystemUiVisibility其他可用的标志:

View.SYSTEM_UI_FLAG_VISIBLE Level 14 默认标记

View.SYSTEM_UI_FLAG_LOW_PROFILE Level 14
低功耗模式, 会隐藏状态栏图标, 在4.0上可以实现全屏。status bar和navigation bar的相关图标会被弱化,比如navigation bar的几个虚拟键会弱化成很细微的小点。一旦你再次点击 status bar和navigation bar 的所在区域,他们就会再次完全显现。这种方式的好处是status bar和navigation bar并没有消失,仍然在界面上,但是它们的细节变暗了、模糊了。

View.SYSTEM_UI_FLAG_LAYOUT_STABLE Level 16
保持整个View稳定, 常跟bar 悬浮, 隐藏共用, 使View不会因为SystemUI的变化而做layout

View.SYSTEM_UI_FLAG_FULLSCREEN Level 16
状态栏隐藏(导航栏仍然显示)。跟WindowManager.LayoutParams.FLAG_FULLSCREEN有相同的效果(其实不同,因为该标志下statusbar的高度还是会存在,不算真正意义上的全屏),同时在使用ActionBar的FEATURE_ACTION_BAR_OVERLAY时,启用SYSTEM_UI_FLAG_FULLSCREEN 会将ActionBar隐藏;该标志一般适用于短期的全屏状态而不是长期。

View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN Level 16
状态栏上浮于Activity

View.SYSTEM_UI_FLAG_HIDE_NAVIGATION Level 14
暂时隐藏导航栏, 由于导航栏的重要性,当产生细微的用户交互后,比如单击屏幕,都可能会导致navigation bar重新出现,源于系统clear掉该标志与SYSTEM_UI_FLAG_FULLSCREEN 标志,同SYSTEM_UI_FLAG_IMMERSIVE 标志一起使用可避免被clear

View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION Level 16
导航栏上浮于Activity

View.SYSTEM_UI_FLAG_IMMERSIVE Level 19
Kitkat新加入的Flag, 沉浸模式, 跟SYSTEM_UI_FLAG_HIDE_NAVIGATION一起使用才有意义,可以避免系统在产生细微用户交互时系统clear掉SYSTEM_UI_FLAG_HIDE_NAVIGATION标志。如单独使用SYSTEM_UI_FLAG_HIDE_NAVIGATION标志时只需单击屏幕,导航栏就会重新出现;如果同时使用该标志,则不会出现,但用户在导航栏区域仍然可以主动呼出。呼出后,对应的标志会被清除。

View.SYSTEM_UI_FLAG_IMMERSIVE_STIKY Level 19
需要跟SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 或者 SYSTEM_UI_FLAG_FULLSCREEN 一起使用。当单独使用上面的标志时,任何用户交互会导致导航栏重新出现,从顶部向下滑动会重新呼出状态栏,这些操作会导致这些标志被clear掉。如果同时指定SYSTEM_UI_FLAG_IMMERSIVE_STIKY 标志,那么对应标志将不会被清除,且呼出隐藏的bar后会自动再隐藏掉

总结

只有Aos 4.4 API 19 KitKat以上版本才支持沉浸式状态栏!

Android
Web note ad 1