刘海屏适配

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

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

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

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

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

官方 API

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

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

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

Android P 中凹口屏幕相关接口

判断是否有刘海屏

@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 模式下,留出黑色一片
// 全屏模式
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

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

如下图所示:

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

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

危险区域和安全区域

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

安全区域:

需要适配点

包名:smartisanos.api
类名:DisplayUtilsSmt
接口:public static boolean isFeatureSupport(int mask)
异形屏判断参数:0 x 00000001
返回值:true 表示此设备为异形屏,false 表示此设备不是异形屏

* 备注:目前仅在 Smartian OS 6.0及以上提供该接口,为兼容低版本请使用反射调用

三星?

刘海屏适配资源

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