文章

刘海屏适配

刘海屏适配

刘海屏 (异形屏、凹口屏幕) 适配

刘海屏都是在 Android O 及以上的的。

  1. Android O(API27),官方还没有公布适配 API,各大厂商有自己的一套适配方案,适配起来比较麻烦,主要适配华为、OPPO、Vivo、小米、锤子。
  2. Android P(API28),官方公布适配 API,各大厂商的 Android O 方案可能不适用了

适配场景:
使用全屏或者沉浸这种设置。一般使用到全屏沉浸的应用像地图、视频、广告页、列表还是需要适配的

AndroidP(API28) 刘海屏 Google 方案适配

官方 API

  • 模拟刘海屏
    在 Developer options(开发者选项) 屏幕中,向下滚动至 Drawing(绘图) 部分并选择 Simulate a display with a cutout(模拟具有凹口的显示屏)。

Android P 提供提供的刘海屏适配方案

  1. 对于有状态栏的页面,不会受到刘海屏特性的影响,因为刘海屏包含在状态栏中了;
  2. 全屏显示的页面,系统刘海屏方案会对应用界面做下移处理,避开刘海区显示,这时会看到刘海区域变成一条黑边,完全看不到刘海了;
  3. 已经适配 Android P 应用的全屏页面可以通过谷歌提供的适配方案使用刘海区,真正做到全屏显示。

刘海屏布局及安全区域说明

Android P 中凹口屏幕相关接口

判断是否有刘海屏

1
2
3
4
5
6
7
8
9
10
@RequiresApi(api = Build.VERSION_CODES.P)
override fun isNotchScreen(window: Window): Boolean {
    val decorView = window.decorView
    val windowInsets = decorView.rootWindowInsets ?: return false
    val dct = windowInsets.displayCutout
    return dct != null && (dct.safeInsetTop != 0
            || dct.safeInsetBottom != 0
            || dct.safeInsetLeft != 0
            || dct.safeInsetRight != 0)
}

DisplayCutout 类接口

主要用于获取凹口位置和安全区域的位置

方法接口说明
getBoundingRects()返回刘海屏区域 Rects 的列表,每个 Rects 都是显示屏上非功能区域的边界矩形。
getSafeInsetLeft ()返回安全区域距离屏幕左边的距离,单位是 px。
getSafeInsetRight ()返回安全区域距离屏幕右边的距离,单位是 px。
getSafeInsetTop ()返回安全区域距离屏幕顶部的距离,单位是 px。
getSafeInsetBottom()返回安全区域距离屏幕底部的距离,单位是 px。

设置凹口屏幕显示模式

模式模式说明
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT只有当 DisplayCutout 完全包含在系统栏中时,才允许窗口延伸到 DisplayCutout 区域。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER该窗口决不允许与 DisplayCutout 区域重叠。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES该窗口始终允许延伸到屏幕短边上的 DisplayCutout 区域。
  1. LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
    始终不会让屏幕到延申刘海区域中,会留出一片黑色区域。
  2. LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
    在全屏下,刘海区留出黑色一片;在 Translucent SystemBar 和 Immersive Fullscreen 模式下,会占用刘海区
  3. LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
    全屏模式下,LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 和 LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER 的表现一样,刘海区留出一片黑色区域;Translucent SystemBar 模式下,LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 表现和 LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 一样,会占用刘海区域;Immersive Fullscreen 模式下,留出黑色一片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 全屏模式
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)

// immersive mode
window.decorView.systemUiVisibility =
    View.SYSTEM_UI_FLAG_FULLSCREEN or
    View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
    View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
    View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
actionBar?.hide()

// transparent systembar
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)

Android 8.1.0(API27) 刘海屏适配

华为 刘海屏

https://developer.huawei.com/consumer/cn/devservice/doc/50114

  1. 可关闭刘海屏
  2. Android P 上,Android O 方案和 Google Android P 方案可以共存

OPPO 凹形屏

https://open.oppomobile.com/wiki/doc#id=10159

  1. 可关闭凹形屏
  2. Android P 上,Android O 方案和 Google Android P 方案可以共存

AndroidO 之 VIVO 异形屏适配

https://dev.vivo.com.cn/documentCenter/doc/103

1
2
3
4
5
6
vivo手机没对刘海做出适配方案
* 在设置里的 显示与亮度→第三方应用显示比例 中,有两种显示模式:
* 1、安全区域显示
* 2、全屏显示
* 这两种模式目前没有方法可供开发者判断,所以在适配时会有差异
* 尤其是在安全区域显示时的全屏占用刘海的情况下。

如下图所示:

vivo 没有提供 api 来处理,只能根据沉浸式来处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
override fun setPortraitRenderIntoNotchScreen(window: Window) {
    super.setPortraitRenderIntoNotchScreen(window)
    window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
    window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
    var systemUiVisibility = window.decorView.systemUiVisibility
    systemUiVisibility = systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    systemUiVisibility = systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
    window.decorView.systemUiVisibility = systemUiVisibility
}

override fun clearPortraitRenderIntoNotchScreen(window: Window) {
    super.clearPortraitRenderIntoNotchScreen(window)
    window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
    window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
    var systemUiVisibility = window.decorView.systemUiVisibility
    systemUiVisibility = systemUiVisibility and View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN.inv()
    systemUiVisibility = systemUiVisibility and View.SYSTEM_UI_FLAG_LAYOUT_STABLE.inv()
    window.decorView.systemUiVisibility = systemUiVisibility
}

小米 刘海屏

  1. 可关闭刘海屏
  2. Android P 上,Android O 方案不可用,采用 Google Android P 方案

小米叫 Notch 设备

注意

  1. 适配了刘海屏,要注意横向切换,耳朵区在右边时,小米 8 左边是圆弧,有部分页面会被遮挡
  2. 小米有个「隐藏屏幕刘海」设置,开启后,屏幕刘海区域显示黑色(竖屏才是黑色,横屏貌似没效果)

Notch 特点

  1. 含 Notch 往往都是全面屏手机,即屏幕比例可能是 18:9、18.7:9 等不同的值
  2. 只对 AndroidO 适配,AndroidP 采用官方 API

适配

系统级适配规则

Notch 机型在界面上会带来两个问题:

  1. 顶部内容会被 Notch 遮挡
  2. 如何处理耳朵区的显示区域

为了保证绝大部分应用都能正常显示,同时尽可能利用屏幕的显示区域。MIUI System UI 制定了以下全局规则:

  1. status bar 略高于 Notch 高度,对于应用来说,相当于一个更高的 status bar。
  2. 当应用显示 status bar 时(如微信首页),允许应用使用耳朵区(背后的逻辑是:因为 status bar 区域本身不可交互,且会显示信号、电池等信息,因此我们假定应用不会在该区域放置重要的内容和可交互的控件)。
  3. 当应用不显示 status bar 时(如全屏游戏),不允许应用使用耳朵区,系统默认填黑。
  4. 横屏时,默认均不允许使用耳朵区,系统默认填黑。
  5. 不允许应用 180 度倒转显示。

注:上述规则的模拟效果对比图,可以参见文末的附录 “Notch 屏系统默认规则介绍 “。

开发者适配

系统规则只能解决最基础的可用性问题,在系统规则下,开发者仍需要检查以下内容:

  1. 检查系统默认规则是否有可用性问题,考虑是否做针对性优化。
  2. 检查 status bar 的显示策略。重新考虑是否隐藏 status bar
  3. 尽量避免某些页面显示 status bar,某些页面又隐藏,否则会出现页面跳变的情况(应用的可用高度变了)。
  4. 检查横屏的情况,确定是否需要利用横屏的 Notch,若使用,需兼顾 Notch 出现在左边/右边的情况。
  5. 检查是否写死了状态栏的高度值。Notch 机器状态栏的值是变化的,建议改为读取系统的值(后有相关方法说明)。
  6. 检查开启「隐藏屏幕刘海」后,应用是否显示异常(详见后文)。
  7. 检查普通屏幕的显示,保证应用在普通屏幕和 Notch 屏幕下都能正常显示 。
适配接口说明

https://dev.mi.com/console/doc/detail?pId=1293

锤子 异形屏

https://resource.smartisan.com/resource/61263ed9599961d1191cc4381943b47a.pdf

危险区域和安全区域

应用全屏显示时屏幕顶部 “ 两边圆角 “ 及 “ 前置摄像头 “ 可能会遮挡应用内容

安全区域:

需要适配点

  • 判断当前设备是否为异形屏
1
2
3
4
5
6
7
包名:smartisanos.api
类名:DisplayUtilsSmt
接口:public static boolean isFeatureSupport(int mask)
异形屏判断参数:0 x 00000001
返回值:true 表示此设备为异形屏,false 表示此设备不是异形屏

* 备注:目前仅在 Smartian OS 6.0及以上提供该接口,为兼容低版本请使用反射调用
  • 应用在屏幕顶部左右两边圆角区域信息显示不全
    解决:应用全屏显示时,可以将内容布局到状态栏区域,因为屏幕顶部左右有圆角,此区域的信息可能显示
    在圆角外,建议在屏幕顶部圆角区域(左右顶部宽度 = 45px)内不放关键信息(按钮、文字等),只
    用背景填充
  • 屏幕顶部不规则开孔遮挡应用信息
    竖屏时,建议应用关键信息(按钮、文字等)避开前置摄像头区域(中间顶部,高 82px × 宽 104px),
    只用背景填充
  • 横屏时,考虑到用户可能顺时针旋转手机也可能逆时针旋转手机,建议应用两侧中间关键信息(按钮、文
    字等)避开前置摄像头区域,或只在安全区内显示操作项,不规则区域只用背景填充
  • 应用顶部信息离状态栏很近
    此问题的一般原因是应用写死了状态栏高度为 60px,而状态栏黑色背景高度为 82px,所以会有 22p 的
    内容被状态栏遮挡,建议应用调用系统接口获取实际的状态栏高度,保证应用内容正常显示

三星?

刘海屏适配资源

https://github.com/KilleTom/BangScreenToolsMaster

https://github.com/ChristianFF/NotchCompat

https://github.com/zhangzhun132/NotchTools

android 兼容所有刘海屏的方案大全
https://blog.csdn.net/DJY1992/article/details/80689632

Android P 刘海屏适配全攻略
https://juejin.im/post/5b1930835188257d7541ba33

本文由作者按照 CC BY 4.0 进行授权