何谓沉浸式状态栏##
说白了,沉浸式状态栏本质上就是给系统状态栏着色。当这个颜色和我们Activity
中的ToolBar
或者ActionBar
所使用的背景颜色一致时就会有沉浸式的效果。
怎么给状态栏着色##
那么我们要怎么样才能给系统状态栏着色呢?谷歌后知后觉,终于在API 21
的Window
类中添加了相应的方法,方法声明如下:
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 Theme
中android:statusBarColor
属性的值默认使用android:colorPrimaryDark
属性指定的值;所以我们也可以仅仅指定android:colorPrimaryDark
属性;
如果因为某种原因,不从Material Theme
中继承,那么就只能老老实实地指定特定的属性不能偷懒,这些属性包括android:statusBarColor
,android: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:windowDrawsSystemBarBackgrounds
为true
必不可少;就像上面使用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
上的一张图说明)
上图中,左边为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
以上版本才支持沉浸式状态栏!