android刘海屏适配方案

刘海屏屏幕适配

注:以下所述“刘海”指延伸至状态栏的屏幕区域。

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_FULLSCREENSYSTEM_UI_FLAG_HIDE_NAVIGATION的窗口中使用,以避免在设置或清除标志时执行窗口的其他布局。

若未作任何声明,则会按默认模式处理。

设置刘海屏显示模式

  • style中设置刘海屏显示模式,然后将其设置为Activity的Theme
    注意:API level 28及以上,若你的minSdkVersion < 28,则将以下样式写到 /values-v28/styles.xml中

在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_EDGESLAYOUT_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 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 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 模式
    android刘海屏适配方案_第1张图片
    android刘海屏适配方案_第2张图片
  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 模式
    android刘海屏适配方案_第3张图片
    android刘海屏适配方案_第4张图片
    横屏时,刘海区域留黑,内容布局不渲染到刘海区域。
    详情请查看刘海屏显示

2. 设备生产商各自的适配方案

google官方在Android P版本开始支持android刘海屏的显示,android设备生产厂商也各自提供了针对性的API为刘海屏的显示提供技术支持,包括在Android O版本的设备上支持刘海屏的显示。

下面列举几个android设备生产厂商各自的刘海屏适配方案,文档都写得很详细。

2.1 华为手机刘海屏适配

详见:华为手机刘海屏适配方案

2.2 小米手机刘海屏适配

详见:小米手机刘海屏适配方案

2.3 vivo手机刘海屏适配方案

详见:vivo手机屏幕适配指南

2.4 oppo手机刘海屏适配方案

详见:oppo凹型屏适配说明

你可能感兴趣的:(android,android屏幕适配)