Android 实现沉浸式全屏

fullscreen.png

前言

本文总结 Android 实现沉浸式全屏的实现方式。

实现沉浸式全屏

在一些需要全屏显示的场景下,比如玩游戏、看横屏视频的时候,内容全屏,占满窗口的体验会让用户更加沉浸到对内容的消费中,带来好的用户体验。

沉浸式显示具体来说就是如状态栏和导航栏部分的显示效果调整。当然,这里对于不同的产品形态会有不同的选择,状态栏文本的颜色、状态栏本身的背景色、导航栏的背景色以及是否显示,通过这些组合可以呈现出不同的用户体验。下面就从这两个组件的使用出发,看看实现沉浸式状态栏的方法。

fullscreen_landscape.gif
fullscreen_portrait.gif


状态栏

状态栏背景色

关于状态栏,首先是状态栏背景色, 这个根据需要设置就好了,一般情况下设置为透明比较好适配。

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 开始,我们甚至可以设置导航栏横线的颜色了。

潜在的坑

这里再补充一个最近遇到的关于设置状态栏和导航栏颜色的坑。如果当前 Activity 的 theme 属性中包含如下内容

    <style name="BugTheme" parent="AppTheme">
        <item name="android:windowTranslucentNavigation">true</item>
        <item name="android:windowTranslucentStatus">true</item>
    </style>

这里的坑就是由这个 Translucent 导致的。Translucent 的意思是半透明,Transparent 的意思才是透明。这两个单词从拼写到发音都有些相似,理论上说二者的效果也是类似,无非就是透明度的值不一样而已。但就是这细微的差别,容易导致问题。一旦给 Activity 的 theme 设置了如上的熟悉,那么后续通过 window.statusBarColorwindow.navigationBarColor 设置颜色就不在生效了。

导航栏显示与隐藏

导航栏显示与隐藏的方法,和状态栏显示隐藏的方法非常相似,改变一下参数即可。

controller.hide(WindowInsetsCompat.Type.navigationBars()) // 导航栏隐藏

// or

controller.show(WindowInsetsCompat.Type.navigationBars()) // 导航栏显示

沉浸式

WindowCompat.setDecorFitsSystemWindows(window, false) // 打开沉浸式
// or
WindowCompat.setDecorFitsSystemWindows(window, true) // 关闭沉浸式
沉浸式开 沉浸式关闭
full_off.png
full_on.png

可以看到,使用 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 里面。

关于 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
full_on.png
full_full.png

可以看到,LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 模式下,整个内容都撑开到刘海的区域了。屏占比更高了,看着也更舒服了。

总结

使用官方提供的 WindowInsetsControllerCompat 的系列 API,操作状态栏及导航栏,以及沉浸式的实现相对来说比较简单了,底层处理了各个版本之间的兼容性。

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容