刘海屏屏幕适配
注:以下所述“刘海”指延伸至状态栏的屏幕区域。
1. google 官方对Android P刘海屏的适配方案
google官方已经在**Android 9(API level 28)**开始支持刘海屏的显示(显示屏缺口支持)。当然设备制造商也可以选择在Android 8.1或更低版本的设备上支持刘海屏显示。
google官方对刘海屏设计的约束
- 不能有两个以上的刘海,即刘海区域 <= 2个;单边最多只能有一个刘海
- 支持应用界面延伸到短边的刘海区域内。不支持延伸到长边的刘海区域内,长边刘海区留黑;
- 竖屏模式下,在没有设置任何特殊标志的情况时,状态栏必须至少延伸至刘海屏的高度,即刘海屏高度 <= 状态栏高度;
- 默认情况下,全屏模式(指隐藏状态栏status bar,即通过 SYSTEM_UI_FLAG_FULLSCREEN 实现的效果)或横屏模式时,刘海区域是闭封的,即禁用刘海区域,刘海区域留黑。
刘海屏的API
控制布局内容在刘海区域的显示方式的API属性: layoutInDisplayCutoutMode,其值可设置为以下几种:
-
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT - 默认模式:竖屏模式时,内容布局将渲染到刘海区域;全屏模式或横屏模式下内容布局不绘制到刘海区域。
-
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES - 刘海区绘制模式:横竖屏模式下,不论状态栏是否显示,内容布局都将渲染到刘海区域。因此,在使用这种模式时,确保应用的布局内容在安全区域内绘制,以免被遮挡。
需通过View.setSystemUiVisibility(int)
方法设置以下几个flags值:
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
SYSTEM_UI_FLAG_LAYOUT_STABLE -
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER - 刘海区不绘制模式:任何模式下,内容布局都不渲染到刘海区域。
此模式应在临时设置了SYSTEM_UI_FLAG_FULLSCREEN
或SYSTEM_UI_FLAG_HIDE_NAVIGATION
的窗口中使用,以避免在设置或清除标志时执行窗口的其他布局。
若未作任何声明,则会按默认模式处理。
设置刘海屏显示模式
- 在
style
中设置刘海屏显示模式,然后将其设置为Activity的Theme
注意:API level 28及以上,若你的minSdkVersion < 28,则将以下样式写到 /values-v28/styles.xml中
<style name="FullscreenTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<!-- 未方便观察,将状态栏颜色设置为透明:API level 21及以上 -->
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
在Activity.onCreate()方法中
getWindow().getDecorView()
.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
- 使用代码设置刘海屏显示模式
Activity.onCreate()中:
getWindow().getDecorView()
.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(lp);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setStatusBarColor(Color.TRANSPARENT);
}
设置布局内容的安全区域
当layoutInDisplayCutoutMode
设置为LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
或LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
时,未避免布局内容被遮挡,使用以下代码设置内容布局的安全区域:
contentView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
int statusBarHeight = getDeviceStatusBarHeight(FullscreenActivity.this);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowInsets windowInsets = getWindow().getDecorView().getRootWindowInsets();
if (windowInsets != null) {
DisplayCutout cutout = windowInsets.getDisplayCutout();
if (cutout != null) {
List<Rect> rects = cutout.getBoundingRects();
if (rects != null && rects.size() > 0) {
contentView.setPadding(
cutout.getSafeInsetLeft(),
Math.max(statusBarHeight, cutout.getSafeInsetTop()),
cutout.getSafeInsetRight(),
cutout.getSafeInsetBottom());
}
}
}
}
}
@Override
public void onViewDetachedFromWindow(View v) {
}
});
或
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
contentView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
@Override
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
int statusBarHeight = ScreenAdapterUtil.getDeviceStatusBarHeight(FullscreenActivity.this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
DisplayCutout cutout = insets.getDisplayCutout();
if (cutout != null) {
List<Rect> rects = cutout.getBoundingRects();
if (rects != null && rects.size() > 0) {
contentView.setPadding(cutout.getSafeInsetLeft(),
Math.max(statusBarHeight, cutout.getSafeInsetTop()),
cutout.getSafeInsetRight(),
cutout.getSafeInsetBottom());
}
}
}
return insets;
}
});
}
获取状态栏高度
public int getDeviceStatusBarHeight(Context context) {
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0)
return context.getResources().getDimensionPixelSize(resourceId);
return 0;
}
效果图
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 模式
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 模式
横屏时,刘海区域留黑,内容布局不渲染到刘海区域。
详情请查看刘海屏显示
2. 设备生产商各自的适配方案
google官方在Android P版本开始支持android刘海屏的显示,android设备生产厂商也各自提供了针对性的API为刘海屏的显示提供技术支持,包括在Android O版本的设备上支持刘海屏的显示。
下面列举几个android设备生产厂商各自的刘海屏适配方案,文档都写得很详细。
2.1 华为手机刘海屏适配
详见:华为手机刘海屏适配方案
2.2 小米手机刘海屏适配
详见:小米手机刘海屏适配方案
2.3 vivo手机刘海屏适配方案
详见:vivo手机屏幕适配指南
2.4 oppo手机刘海屏适配方案
详见:oppo凹型屏适配说明