简介:

随着去年iPhone X的发布,今年android厂商也紧随其后,发布了各种异形屏手机(国内习惯称为刘海屏),作为Android的东家,google也是大力支持这种方案,然后就决定在系统级予以支持,所以从Android P开始,就提供了相应的接口,适配刘海屏,并且华为和小米已经明确说明了,以后的新系统会兼容google官方的解决方案,所以接下来,我就介绍一下官方的解决方案。

适配方案:

1.官方特性介绍

谷歌称刘海屏为凹口屏以及屏幕缺口支持, 下面的内容摘自:https://developer.android.com/preview/features#cutout

Android P 支持最新的全面屏以及为摄像头和扬声器预留空间的凹口屏幕。 通过全新的 DisplayCutout 类,可以确定非功能区域的位置和形状,这些区域不应显示内容。 要确定这些凹口屏幕区域是否存在及其位置,请使用 getDisplayCutout() 函数。

全新的窗口布局属性 layoutInDisplayCutoutMode 让您的应用可以为设备凹口屏幕周围的内容进行布局。 您可以将此属性设为下列值之一:

(1)LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT

(2)LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES

(3)LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER

您可以按如下方法在任何运行 Android P 的设备或模拟器上模拟屏幕缺口:

(1)启用开发者选项。

(2)在 Developer options 屏幕中,向下滚动至 Drawing 部分并选择 Simulate a display with a cutout。

(3)选择凹口屏幕的大小。

注:我们建议您通过使用运行 Android P 的设备或模拟器测试凹口屏幕周围的内容显示。

2.相关接口介绍

(1)获取刘海尺寸相关接口:

https://developer.android.com/reference/android/view/DisplayCutout

方法 接口说明
List getBoundingRects() 返回Rects的列表,每个Rects都是显示屏上非功能区域的边界矩形。设备的每个短边最多只有一个非功能区域,而长边上则没有。
int getSafeInsetBottom() 返回安全区域距离屏幕底部的距离,单位是px。
int getSafeInsetLeft () 返回安全区域距离屏幕左边的距离,单位是px。
int getSafeInsetRight () 返回安全区域距离屏幕右边的距离,单位是px。
int getSafeInsetTop () 返回安全区域距离屏幕顶部的距离,单位是px。

(2)设置是否延伸到刘海区显示接口:
https://developer.android.com/reference/android/view/WindowManager.LayoutParams#layoutInDisplayCutoutMode

方法 接口说明
int layoutInDisplayCutoutMode 默认值:
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
其他可能取值:
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER

https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES

常量 常量说明
int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 只有当DisplayCutout完全包含在系统状态栏中时,才允许窗口延伸到DisplayCutout区域显示。
int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER 该窗口决不允许与DisplayCutout区域重叠。
int LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 该窗口始终允许延伸到屏幕短边上的DisplayCutout区域。

3.代码实现

(1)设置使用刘海区显示代码:

1
2
3
4
5
6
7
8
//使内容出现在status bar后边,如果要使用全屏的话再加上View.SYSTEM_UI_FLAG_FULLSCREEN
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
//设置页面全屏显示
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.layoutInDisplayCutoutMode =
windowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
//设置页面延伸到刘海区显示
getWindow().setAttributes(lp);

(2)获取刘海屏安全显示区域和刘海尺寸信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
getWindow().getDecorView().setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
@Override
public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
DisplayCutout cutout = windowInsets.getDisplayCutout();
if (cutout == null) {
Log.e(TAG, "cutout==null, is not notch screen");//通过cutout是否为null判断是否刘海屏手机
} else {
List<Rect> rects = cutout.getBoundingRects();
if (rects == null || rects.size() == 0) {
Log.e(TAG, "rects==null || rects.size()==0, is not notch screen");
} else {
Log.e(TAG, "rect size:" + rects.size());//注意:刘海的数量可以是多个
for (Rect rect : rects) {
Log.e(TAG, "cutout.getSafeInsetTop():" + cutout.getSafeInsetTop()
+ ", cutout.getSafeInsetBottom():" + cutout.getSafeInsetBottom()
+ ", cutout.getSafeInsetLeft():" + cutout.getSafeInsetLeft()
+ ", cutout.getSafeInsetRight():" + cutout.getSafeInsetRight()
+ ", cutout.rects:" + rect
);
}
}
}
return windowInsets;
}
});

UI适配

1.判断是否为刘海屏

见上边代码

2.调整布局

如果是刘海屏,调整布局避开刘海区。

布局原则:保证重要的文字、图片、视频信息、可点击的控件和图标,应用弹窗等,建议显示在状态栏区域以下(安全区域)。如果内容不重要或者不会遮挡,布局可以延伸到状态栏区域(危险区域)。

建议按照如下布局原则修改:

3.获取状态栏高度

经过试验研究,小米和华为的获取的高度就是刘海的高度,并且在不同状态下这个值会有所变化

1
2
3
4
5
6
7
8
public static int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}