前言
本文总结 Android 实现沉浸式全屏的实现方式。
实现沉浸式全屏
在一些需要全屏显示的场景下,比如玩游戏、看横屏视频的时候,内容全屏占满窗口的体验会让用户更加沉浸到对内容的消费中,带来更好的用户体验。
沉浸式显示具体来说就是如状态栏和导航栏部分的显示效果调整。当然,这里对于不同的产品形态会有不同的选择。
状态栏文本、icon 的颜色、状态栏本身的背景色、导航栏的背景色以及是否显示,通过这些组合可以呈现出不同的用户体验。下面就从这两个组件的使用出发,看看如何实现沉浸式的效果。
![](https://img.haomeiwen.com/i27208369/f2d2689c44b4d416.png)
![](https://img.haomeiwen.com/i27208369/ed004359eebd7ded.png)
状态栏
状态栏背景色
关于状态栏,首先是状态栏背景色, 这个根据需要设置就好了,一般情况下设置为透明比较好适配。
window.statusBarColor = Color.TRANSPARENT
状态栏文字颜色
关于状态栏、导航栏的其他操作,我们可以使用系统的 WindowInsetsControllerCompat
这个类,从名字 Compat
就可以看到,这是一个兼容的类。关于沉浸式状态栏的实现,由于 Android 在国内变成了「安卓」,因此早期关于状态各种属性的适配可以说是群魔乱舞,各式各样的 StatusBarUtils 大行其道。现在好了,Android 官方终于一统天下,亲自下场来搞了,这下关于沉浸式的实现就比较简单了。
WindowInsetsControllerCompat
的使用也很简单,创建一个他的实例即可。
val controller = WindowInsetsControllerCompat(window, window.decorView)
后面的一切使用这个实例就可以了,API 很简单,命名一目了然。
比如更改状态颜色这个功能。关于状态栏文字的颜色,Android 官方只允许设置黑色或者白色
controller.isAppearanceLightStatusBars = true // 黑色状态栏
// or
controller.isAppearanceLightStatusBars = false // 白色状态栏
注意、注意、注意 ,这里的注释没有写错,这个方法就是这么奇怪,自己一开始使用也是被绕晕了。但就是这样。还有一点需要注意的是,Android 6.0 也就是 Android SDK 23 开始,才可以使用这个 feature 。
状态栏显示与隐藏
controller.hide(WindowInsetsCompat.Type.statusBars()) // 状态栏隐藏
// or
controller.show(WindowInsetsCompat.Type.statusBars()) // 状态栏显示
这个就很简单了。
导航栏
说完了状态栏,在来看导航栏。相比状态栏,导航栏上不会有文字,一般情况下就是一条底部的横线。因此,我们只需要关心导航的背景色和可见性即可。
导航栏背景色
window.navigationBarColor = Color.TRANSPARENT
导航栏横线的颜色
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.navigationBarDividerColor = Color.TRANSPARENT
}
从 Android P (28) 也就是 Android 9.0 开始,我们甚至可以设置导航栏横线的颜色了。
导航栏显示与隐藏
导航栏显示与隐藏的方法,和状态栏显示隐藏的方法非常相似,改变一下参数即可。
controller.hide(WindowInsetsCompat.Type.navigationBars()) // 导航栏隐藏
// or
controller.show(WindowInsetsCompat.Type.navigationBars()) // 导航栏显示
沉浸式
WindowCompat.setDecorFitsSystemWindows(window, false) // 打开沉浸式
// or
WindowCompat.setDecorFitsSystemWindows(window, true) // 关闭沉浸式
沉浸式开 | 沉浸式关闭 |
---|---|
![]() |
![]() |
可以看到,使用 WindowInsetsControllerCompat
,沉浸式就是这么简单。
适配全面屏
从上面沉浸式的图,可以看到其实还是有点问题,就是横屏之后,屏幕左边并没有完全铺开,而是有一段黑边,看着非常难受了。这其实是关于异形屏的适配问题。
其实这段黑边就是刘海屏的区域,Android 官方叫做 DisplayCutout area
这个区域也是有专门的参数进行适配。
val params = window.attributes
params.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
window.attributes = params
大于等于 Android 9.0 (SDK 28 P)的设备都支持。关于 layoutInDisplayCutoutMode
参数有三种类型。
关于这三个参数,还要考虑当前屏幕是横屏还是竖屏。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
这个是默认参数。
竖屏状态
如果没有设置全屏显示的属性,那么内容是延伸到 DisplayCutout area
的。这种情况下,如果进行全屏和非全屏的切换操作,会发现内容在整体上下跳动。比如在这种情况下,调用 controller.hide(WindowInsetsCompat.Type.statusBars())
进行状态栏的显示隐藏的操作,就会发现整个内容在上下跳动,在上面的动图里很明显了。
横屏状态
横屏状态下,顶部的状态栏就变成左边或者右边(这里看屏幕是怎么旋转的,可能是旋转了 -90 度,也可能是 270 度)的黑边了。内容不会延伸到左右两边的 DisplayCutout area
里。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
这种情况下,内容是默认延伸到 DisplayCutout area
的。因此,全屏和非全屏操作的时候,就不会有内容上下跳动或者黑边的问题了。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
这种情况,内容永远不会延伸到 DisplayCutout area
里面。
关于这个三个参数和沉浸式状态的设置,可以进行自由组合,会有不同的效果。具体要怎样,就看实际业务需求了。 比如 Android 官方的相册应用,当我们点击一张图片进行预览的时候,顶部状态栏是全屏之后才慢慢隐藏的。
关于 LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 和 LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 的区别,我们用两张图比较一下就大概明白了。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT | LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES |
---|---|
![]() |
![]() |
可以看到,LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 模式下,整个内容都撑开到刘海的区域了。屏占比更高了,看着也更舒服了。
需要注意的是,刘海屏适配还牵扯到屏幕内容高度(竖屏)或者内容宽度的计算问题。实际的分辨率、displayMetrics.widthPixels
、displayMetrics.heightPixels
这些值是不变的。但是随着刘海区域的变化,实际内容可展示的区域是有变化的,因此还需要做好适配相关的问题。
总结
使用官方提供的 WindowInsetsControllerCompat
的系列 API,操作状态栏及导航栏,以及沉浸式的实现相对来说比较简单了,底层处理了各个版本之间的兼容性。关于文中提到的相关类和方法的使用,更多细节可以查看参考文档中的链接。
参考
- blog.csdn.net/StjunF/arti…
- blog.csdn.net/jingzz1/art…
- guolin.blog.csdn.net/article/det…
- juejin.cn/post/690978…
作者:IAM四十二
链接:https://juejin.cn/post/7139495545206210590