小米开发平台 刘海屏、水滴屏、挖孔屏 Android P/Q 适配
1. 背景
- 小米 8 等刘海设备上市时运行的是 Android O 设备,但由于 Android O 没有标准接口,所以当时适配的规则和接口仅在 MIUI 系统生效。关于小米 Android O 的规则,详见https://dev.mi.com/console/doc/detail?pId=1293。
- 后来 Android P 中新增了刘海屏适配的API,为了与行业标准一致,MIUI 也决定在运行 Android P 的设备上完全采用 Android P 的接口。
- 但由于 Android P 的接口定义得比较晚,导致 MIUI 接口无法与其完全兼容,开发者需要针对 Android P 的小米设备重新适配。
该文档将结合小米的情况给大家简要介绍 Android P 的刘海屏适配规则及 API,更详细的内容可以直接查看官方文档 https://developer.android.com/guide/topics/display-cutout/。
2. 部分小米水滴屏/刘海屏/挖孔屏设备信息如下
机型 | model | device | 分辨率 | Notch高度 | Notch宽度 | DPI |
小米8 | MI 8 | dipper | 1080*2248 | 89 | 560 | 440 |
小米8 SE | MI 8 SE | sirius | 1080*2244 | 85 | 540 | 440 |
小米8 透明探索版 | MI 8 Explorer Edition | ursa | 1080*2248 | 89 | 560 | 440 |
小米8 屏幕指纹版 | MI 8 UD | equuleus | 1080*2248 | 89 | 560 | 440 |
小米8 青春版 | MI8Lite | platina | 1080*2280 | 82 | 296 | 440 |
小米POCO F1 | POCO F1 | beryllium | 1080*2246 | 86 | 588 | 440 |
红米6 Pro | Redmi 6 Pro | sakura | 1080*2280 | 89 | 352 | 440 |
红米Note 7 | Redmi Note 7 | lavender | 1080*2340 | 79 | 116 | 440 |
小米CC9 Pro | Mi CC9 Pro | tucana | 1080*2340 | 71 | 146 | 440 |
Redmi K30 | Redmi K30 | phoenix | 1080*2400 | 92 | 179 | 440 |
注意事项:
- 以上设备,由于MIUI调整了 DPI 值,因此DP值与像素值的转换关系是 1dp = 2.75 px ;
- 用原生api DisplayCutout就可以直接获取设备屏幕尺寸和异形的位置大小等信息,以上仅做参考,之后新机将不再罗列。
3. 概念说明
为了方便讨论,我们明确下以下概念:
上述两种屏幕都可以统称为刘海屏,不过对于右侧较小的刘海,业界一般称为水滴屏或美人尖。为便于说明,后文提到的「刘海屏」「刘海区」都同时指代上图两种屏幕。
4. Android P/Q 刘海屏水滴屏挖孔屏的适配规则
Android P 提供了 3 种显示模式供开发者选择,分别是:
- 默认模式(LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT)
- 刘海区绘制模式( LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)
- 刘海区不绘制模式(LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER)
如果开发者未作任何声明,则会按默认模式处理。以下将具体介绍这三种模式的表现。
4.1. 默认模式(LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT)
为了在不影响操作的情况下,尽可能利用刘海屏的显示区域,有以下表现:
非全屏(normal mode) | 全屏(fullscreen mode) | |
竖屏(portrait mode) | 使用耳朵区 | 禁用耳朵区 |
横屏(landscape mode) | 禁用耳朵区 | 禁用耳朵区 |
注:所谓全屏(fullscreen mode),是指隐藏状态栏(status bar),即通过 SYSTEM_UI_FLAG_FULLSCREEN 实现的效果。
默认模式的截图效果如下:
4.2. 刘海区绘制模式(LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)
如上所述,默认模式下某些场景会禁用耳朵区,那是因为这些场景下,系统无法判断开发者是否会把控件放置在耳朵区,所以只好默认禁用。如果开发者想要在那些场景下使用耳朵区,需要主动声明,即使用 LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 来主动声明。
由于各个厂商的刘海或者凹口形状、位置不一, 开发者可以通过 WindowInsets.getDisplayCutout() 来获得 DisplayCutout object,里面包含了几个有用的方法:
- getBoundingRects():获取刘海 / Cutout 所在的矩形区域的位置,多个刘海则返回多个区域(单位:像素)。
- getSafeInsetLeft() / getSafeInsetTop() / getSafeInsetRight() / getSafeInsetBottom() :返回安全区上下左右的偏移值(单位:像素)。
开发者根据业务内容,自行判断是否需要根据不同的刘海形状做不同的布局调整。以小米8(刘海高度89px)为例,当开发者选用 SHORT_EDGES 模式时,以上接口会返回以下值:
竖屏 | 横屏(刘海在左边) | |
getBoundingRects() | (201, 0 – 879, 90) | (0, 201 – 90, 879) |
getSafeInsetLeft() | 0 | 90 |
getSafeInsetTop() | 90 | 0 |
getSafeInsetRight() | 0 | 0 |
getSafeInsetBottom() | 0 | 0 |
上述接口的返回值代表:
- 小米8有一个刘海,竖屏时,这个刘海所在的矩形区域的左上角、右下角的坐标分别为 (201, 0) 和 (879, 90) —— 左上角为 (0, 0) 原点;横屏时,这个刘海所在的矩形区域的左上角、右下角的坐标分别为 (0, 201) 和 (90, 879) —— 左上角为 (0, 0) 原点。
- 对于小米8,如果开发者需要将内容避开刘海区域,竖屏时就需要从顶部向下偏移 90 px,左、右和下无需要偏移。
又以红米Note 7(水滴屏设备) 为例,当开发者选用 SHORT_EDGES 模式时,以上接口会返回以下值:
竖屏 | 横屏(水滴在左边) | |
getBoundingRects() | (450, 0 – 630, 80) | (0, 450 – 80, 630) |
getSafeInsetLeft() | 0 | 80 |
getSafeInsetTop() | 80 | 0 |
getSafeInsetRight() | 0 | 0 |
getSafeInsetBottom() | 0 | 0 |
上述接口的返回值代表:红米Note 7 有一个刘海(水滴),竖屏时,这个刘海所在的矩形区域的左上角、右下角的坐标分别为 (450, 0) 和 (630, 80) ;横屏时,这个刘海所在的矩形区域的左上角、右下角的坐标分别为 (0, 450) 和 (80, 630) 。
又以Redmi K30 (挖孔屏设备)为例子,当开发者选用 SHORT_EDGES 模式时,以上接口会返回以下值:
竖屏 | 横屏(摄像头在左边) | |
getBoundingRects() | (844, 0 – 1080, 95)] | (0, 0 – 95, 236) |
getSafeInsetLeft() | 0 | 95 |
getSafeInsetTop() | 95 | 0 |
getSafeInsetRight() | 0 | 0 |
getSafeInsetBottom() | 0 | 0 |
上述接口的返回值代表:
Redmi K30有刘海/水滴/挖孔,竖屏是,这个刘海在手机的左上角,右下角的坐标分别为 (844,0)和 (1080, 95);左上角为(0,0);横屏时,这个刘海对应的值为(0,0)和(95,236)。对于Redmi K30,如果开发者需要将内容避开挖孔区域,竖屏就需要从顶部向下偏移95px,左、右和下无需偏移。
4.3. 刘海区不绘制模式(LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER )
开发者选用这个模式后,意味着不绘制内容到耳朵区。如非必需,我们不建议采用这种模式,因为那样会浪费不少屏幕空间,用户体验不佳。
当开发者选用 NEVER 模式时, DisplayCutout object 的以下方法都会返回空值,因为 Google 认为既然开发者不使用耳朵区,就不需要关心刘海的大小了。
竖屏 | 横屏(刘海在左边) | |
getBoundingRects() | null | null |
getSafeInsetLeft() | null | null |
getSafeInsetTop() | null | null |
getSafeInsetRight() | null | null |
getSafeInsetBottom() | null | null |
5. 其他注意事项
5.1. 避免写死状态栏的值
由于 Notch 设备的状态栏高度与正常机器不一样,因此在需要使用状态栏高度时,不建议写死一个值,而应该改为读取系统的值。
以下是获取当前设备状态栏高度的方法:
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensi网站生成app软件onPixelSize(resourceId);
}
5.2. 处理好同一页面,进入与退出全屏模式(fullscreen mode)的过渡
因为在默认模式 / LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 下,系统针对全屏与非全屏的页面,耳朵区的显示逻辑不一样。如果开发者没有处理好,容易出现页面可用区域跳变的问题。针对这种页面,我们建议开发者主动声明是否使用耳朵区,以避免跳变。
6. 常见问题
6.1. 如何测试
有两种方法:
- 使用小米设备测试,如小米8系列(含标准版、探索版、屏幕指纹版),然后升级至 网址生成app工具Android P 的 MIUI 版本,下载地址为:https://www.miui.com/download-345.html。
- 使用运行原生 Android 9 的设备,然后前往「开发者选项 – 模拟“刘海屏”」,选择任一刘海选项。
若适配中遇到问题,可以发邮件给相关工程师张定昌 zhangdingchang@xiaomi.com、喻伟 yuwei@xiaomi.com 或工程组 miuishell@xiaomi.com。
6.2. 适配过小米 Android O 的刘海屏接口,在小米的 Android P 设备上是否需要重新适配
需要。如文章开头所说,Android P 的接口今年6月才公布,我们在接口设计上和他们有一些出入,所以没法兼容。开发者仍然需要再针对 Android P 做适配,但好消息是,各大手机厂商都支持 Android P 的接口,所以大家只要适配一次就可以了。
6.3. MIUI Android O 的老接口在 Android P/Q设备上是否生效?
P和Q大部份用的是原生Andoid的API,MIUI的接口保留了O里面的Application级别的控制接口
<meta-data
android:name="notch.config"
android:value="portrait|landscape"/>
app如果用这个meta-data声明了横竖屏都绘制到耳朵区,相当于每个页面都设成了LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES模式。这就需要app确认自己的所有页面会不会被遮挡,特别是横屏和全屏页面。如果想单独修改某个页面,可以单独修改该页面Window的layoutInDisplayCutoutMode属性
6.4. 原生 Android P 的规则和 MIUI Android O 的规则有什么区别
双方在默认模式下的表现是完全一致的,区别主要体现在:
- Android P 能通过 DisplayCutout object 获取刘海 / Notch / Cutout 的具体信息,但 MIUI Android O 只能获取刘海的高宽信息。
- Android P 不能控制仅竖屏(或横屏)使用耳朵区,但 MIUI Android O 可以分别配置横竖屏对耳朵区的使用策略。
编辑:yimen,如若转载,请注明出处:https://www.yimenapp.com/kb-yimen/12578/
部分内容来自网络投稿,如有侵权联系立删