Android刘海屏、水滴屏全面屏适配小结
现在,市面上的屏幕尺寸和全面屏方案五花八门。这里我使用了小米的图来说明:
上述两种屏幕都可以统称为刘海屏,不过对于右侧较小的刘海,业界一般称为水滴屏或美人尖。为便于说明,后文提到的「刘海屏」「刘海区」都同时指代上图两种屏幕。
刘海屏、水滴屏全面屏适配细节
当我们在谈屏幕适配时,我们具体谈什么呢?
- 适应更长的屏幕
- 防止内容被刘海遮挡
其中第一点是所有应用都需要适配的,对应下文的声明最大长宽比,而第二点,如果应用本身不需要全屏显示或使用沉浸式状态栏,是不需要适配的。
针对需要适配第二点的应用,需要获取刘海的位置和宽高,然后将显示内容避开即可。
声明最大长宽比
以前的普通屏长宽比为16:9,全面屏手机的屏幕长宽比增大了很多,如果不适配的话就会类似下面这样:
适配方式
适配方式有两种:
- 将targetsdkversion版本设置到api 24及以上;
这个操作将会为<application>
标签隐式添加一个属性,android:resizeableactivity="true", 该属性的作用后面将详细说明。
在 <application> 标签中增加属性:android:resizeableactivity="false",同时在节点下增加一个meta-data标签:
<!-- render on full screen up to screen aspect ratio of 2.4 --> <!-- use a letterbox on screens larger than 2.4 --> <meta-data android:name="android.max_aspect" android:value="2.4" />
原理说明
在 android 7.0(api 级别 24)或更高版本的应用,android:resizeableactivity属性默认为true(对应适配方式1)。这个属性是控制多窗口显示的,决定当前的应用或者activity是否支持多窗口。
可以在清单的<activity>
或 <application>
节点中设置该属性,启用或禁用多窗口显示,配置如下:
android:resizeableactivity=["true" | "false"]
如果该属性设置为 true,activity 将能以分屏和*形状模式启动。 如果此属性设置为 false,activity 将不支持多窗口模式。 如果该值为 false,且用户尝试在多窗口模式下启动 activity,该 activity 将全屏显示。
适配方式2即为设置屏幕的最大长宽比,这是官方提供的设置方式。
如果设置了最大长宽比,必须android:resizeableactivity="false"。 否则最大长宽比没有任何作用。
适配刘海屏
android9.0适配
android p(9.0)开始,官方开始提供了官方的挖孔屏适配api,具体可以参考support display cutouts。
通过android p提供的 displaycutout 类,可以确定非功能区域的位置和形状,这些区域不应显示内容。 要确定这些凹口屏幕区域是否存在及其位置,请使用 getdisplaycutout() 函数。
全新的窗口布局属性 layoutindisplaycutoutmode 让您的应用可以为设备凹口屏幕周围的内容进行布局。 您可以将此属性设为下列值之一:
- layout_in_display_cutout_mode_default
- layout_in_display_cutout_mode_short_edges
- layout_in_display_cutout_mode_never
默认值是layout_in_display_cutout_mode_default,刘海区域不会显示内容,需要显示时可以将值设置为layout_in_display_cutout_mode_short_edges。
您可以按如下步骤在任何运行 android p 的设备或模拟器上模拟屏幕缺口:
- 启用开发者选项;
- 在 developer options 屏幕中,向下滚动至 drawing 部分并选择 simulate a display with a cutout。
适配参考示例:
// 延伸显示区域到刘海 windowmanager.layoutparams lp = window.getattributes(); lp.layoutindisplaycutoutmode = windowmanager.layoutparams.layout_in_display_cutout_mode_short_edges; window.setattributes(lp); // 设置页面全屏显示 final view decorview = window.getdecorview(); decorview.setsystemuivisibility(view.system_ui_flag_layout_fullscreen | view.system_ui_flag_layout_stable);
其中延伸显示区域到刘海的代码,也可以通过修改activity或应用的style实现,例如:
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="apptheme" parent="xxx"> <item name="android:windowlayoutindisplaycutoutmode">shortedges</item> </style> </resources>
android o 适配
因google官方的适配方案到android p才推出,因此在android o(8.0版本)设备上,各家厂商有自己的实现方案。
华为android o适配
方案一:
具体方式如下所示:
<meta-data android:name="android.notch_support" android:value="true"/>
对application生效,意味着该应用的所有页面,系统都不会做竖屏场景的特殊下移或者是横屏场景的右移特殊处理。例如:
<application android:allowbackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundicon="@mipmap/ic_launcher_round" android:testonly="false" android:supportsrtl="true" android:theme="@style/apptheme"> <meta-data android:name="android.notch_support" android:value="true"/> <activity android:name=".mainactivity"> <intent-filter> <action android:name="android.intent.action.main"/> <category android:name="android.intent.category.launcher"/> </intent-filter> </activity>
对activity生效,意味着可以针对单个页面进行刘海屏适配,设置了该属性的activity系统将不会做特殊处理。例如:
<application android:allowbackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundicon="@mipmap/ic_launcher_round" android:testonly="false" android:supportsrtl="true" android:theme="@style/apptheme"> <activity android:name=".mainactivity"> <intent-filter> <action android:name="android.intent.action.main"/> <category android:name="android.intent.category.launcher"/> </intent-filter> </activity> <activity android:name=".landscapefullscreenactivity" android:screenorientation="sensor"> </activity> <activity android:name=".fullscreenactivity"> <meta-data android:name="android.notch_support" android:value="true"/> </activity> </application>
方案二:
对application生效,意味着该应用的所有页面,系统都不会做竖屏场景的特殊下移或者是横屏场景的右移特殊处理。
1,设置应用窗口在华为刘海屏手机使用刘海区。
/*刘海屏全屏显示flag*/ public static final int flag_notch_support=0x00010000; /** * 设置应用窗口在华为刘海屏手机使用刘海区 * @param window 应用页面window对象 */ public static void setfullscreenwindowlayoutindisplaycutout(window window) { if (window == null) { return; } windowmanager.layoutparams layoutparams = window.getattributes(); try { class layoutparamsexcls = class.forname("com.huawei.android.view.layoutparamsex"); constructor con=layoutparamsexcls.getconstructor(layoutparams.class); object layoutparamsexobj=con.newinstance(layoutparams); method method=layoutparamsexcls.getmethod("addhwflags", int.class); method.invoke(layoutparamsexobj, flag_notch_support); } catch (classnotfoundexception | nosuchmethodexception | illegalaccessexception |instantiationexception | invocationtargetexception e) { log.e("test", "hw add notch screen flag api error"); } catch (exception e) { log.e("test", "other exception"); } }
2.清除添加的华为刘海屏flag,恢复应用不使用刘海区显示。
public static void setnotfullscreenwindowlayoutindisplaycutout (window window) { if (window == null) { return; } windowmanager.layoutparams layoutparams = window.getattributes(); try { class layoutparamsexcls = class.forname("com.huawei.android.view.layoutparamsex"); constructor con=layoutparamsexcls.getconstructor(layoutparams.class); object layoutparamsexobj=con.newinstance(layoutparams); method method=layoutparamsexcls.getmethod("clearhwflags", int.class); method.invoke(layoutparamsexobj, flag_notch_support); } catch (classnotfoundexception | nosuchmethodexception | illegalaccessexception |instantiationexception | invocationtargetexception e) { log.e("test", "hw clear notch screen flag api error"); } catch (exception e) { log.e("test", "other exception"); } }
小米android o适配
判断是否是刘海屏。
private static boolean isnotch() { try { method getint = class.forname("android.os.systemproperties").getmethod("getint", string.class, int.class); int notch = (int) getint.invoke(null, "ro.miui.notch", 0); return notch == 1; } catch (throwable ignore) { } return false; }
设置显示到刘海区域
@override public void setdisplayinnotch(activity activity) { int flag = 0x00000100 | 0x00000200 | 0x00000400; try { method method = window.class.getmethod("addextraflags", int.class); method.invoke(activity.getwindow(), flag); } catch (exception ignore) { } }
获取刘海宽高
public static int getnotchheight(context context) { int resourceid = context.getresources().getidentifier("notch_height", "dimen", "android"); if (resourceid > 0) { return context.getresources().getdimensionpixelsize(resourceid); } return 0; } public static int getnotchwidth(context context) { int resourceid = context.getresources().getidentifier("notch_width", "dimen", "android"); if (resourceid > 0) { return context.getresources().getdimensionpixelsize(resourceid); } return 0; }
oppo android o适配
判断是否是刘海屏
@override public boolean hasnotch(activity activity) { boolean ret = false; try { ret = activity.getpackagemanager().hassystemfeature("com.oppo.feature.screen.heteromorphism"); } catch (throwable ignore) { } return ret; }
获取刘海的左上角和右下角的坐标
private static string getscreenvalue() { string value = ""; class<?> cls; try { cls = class.forname("android.os.systemproperties"); method get = cls.getmethod("get", string.class); object object = cls.newinstance(); value = (string) get.invoke(object, "ro.oppo.screen.heteromorphism"); } catch (throwable ignore) { } return value; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。