随着去年iPhone X的发布,今年android厂商也紧随其后,发布了各种异形屏手机(国内习惯称为刘海屏),作为Android的东家,google也是大力支持这种方案,然后就决定在系统级予以支持,所以从Android P开始,就提供了相应的接口,适配刘海屏,并且华为和小米已经明确说明了,以后的新系统会兼容google官方的解决方案,所以接下来,我就介绍一下官方的解决方案。
谷歌称刘海屏为凹口屏以及屏幕缺口支持, 下面的内容摘自: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 的设备或模拟器测试凹口屏幕周围的内容显示。
(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区域。 |
(1)设置使用刘海区显示代码:
//使内容出现在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)获取刘海屏安全显示区域和刘海尺寸信息:
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 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;
}
});
见上边代码
如果是刘海屏,调整布局避开刘海区。
布局原则:保证重要的文字、图片、视频信息、可点击的控件和图标,应用弹窗等,建议显示在状态栏区域以下(安全区域)。如果内容不重要或者不会遮挡,布局可以延伸到状态栏区域(危险区域)。
建议按照如下布局原则修改:
经过试验研究,小米和华为的获取的高度就是刘海的高度,并且在不同状态下这个值会有所变化
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;
}