由于刘海屏设备上市时运行的是 Android O 设备,而 Android O 没有标准接口来适配刘海屏设备,因此各大厂商针对Android O刘海屏适配方案也各有差异,具体如下:
华为刘海屏手机安卓O版本适配指导
小米刘海屏 Android O 适配
OPPO凹形屏适配说明
VIVO全面屏应用适配指南
后来 Android P 中新增了刘海屏适配的API,因此开发者需要针对 Android P 的设备重新适配,使用android标准接口来适配刘海屏设备,文档如下:
Android P 官方刘海屏适配规则及 API
Android P刘海屏适配规则(小米)
Android O 8.0刘海屏适配
1.顶部内容会被刘海部分遮挡
2.如何处理耳朵区的显示区域
1.status bar 的高度必须大于等于刘海的高度;对于应用来说,相当于一个更高的 status bar.
2.当应用显示 status bar 时(如微信首页),允许应用使用耳朵区(因为 status bar 区域本身不可交互,且会显示信号、电池等信息,因此我们假定应用不会在该区域放置重要的内容和可交互的控件)
3.当应用不显示 status bar 时(如全屏游戏),不允许应用使用耳朵区,系统默认填黑
4.横屏时,默认均不允许使用耳朵区,系统默认填黑
5.不允许应用180度倒转显示
1.检查设备是否为Notch设备
2.确定页面是否显示状态栏
3.尽量避免同一个APP不同页面显示和隐藏状态并存(会出现页面跳变的情况(应用的可用高度变了)
4.确定是否需要横屏显示Notch区域(若显示,需兼顾notch区域在左边/右边的情况)
5.检查是否写死了状态栏的高度值。Notch机器状态栏的值是变化的,建议改为读取系统的值
6.检查普通屏幕和刘海屏幕上的显示是否正常.
小米 Android O 刘海屏适配
系统增加了 property ro.miui.notch,值为1时则是 Notch 屏手机
SystemProperties.getInt("ro.miui.notch", 0) == 1;
调用示例:
public static int getInt(String key,Activity activity) {
int result = 0;
if (isXiaomi()){
try {
ClassLoader classLoader = activity.getClassLoader();
@SuppressWarnings("rawtypes")
Class SystemProperties = classLoader.loadClass("android.os.SystemProperties");
//参数类型
@SuppressWarnings("rawtypes")
Class[] paramTypes = new Class[2];
paramTypes[0] = String.class;
paramTypes[1] = int.class;
Method getInt = SystemProperties.getMethod("getInt", paramTypes);
//参数
Object[] params = new Object[2];
params[0] = new String(key);
params[1] = new Integer(0);
result = (Integer) getInt.invoke(SystemProperties, params);
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
// 是否是小米手机
public static boolean isXiaomi() {
return "Xiaomi".equals(Build.MANUFACTURER);
}
其中,value 的取值可以是以下4种:
"none" 横竖屏都不绘制耳朵区(默认值,系统自动将应用页面下移挖孔高度的距离,保证应用布局显示在挖空区域以下)
"portrait" 竖屏绘制到耳朵区
"landscape" 横屏绘制到耳朵区
"portrait|landscape" 横竖屏都绘制到耳朵区
如果开发者希望对特定 Window 作处理,可以使用该接口。 在 WindowManager.LayoutParams 增加 extraFlags 成员变量,用以声明该 window 是否使用耳朵区。
其中,extraFlags 有以下变量:
0x00000100 开启配置
0x00000200 竖屏配置
0x00000400 横屏配置
组合后表示 Window 的配置,如:
0x00000100 | 0x00000200 竖屏绘制到耳朵区
0x00000100 | 0x00000400 横屏绘制到耳朵区
0x00000100 | 0x00000200 | 0x00000400 横竖屏都绘制到耳朵区
控制 extraFlags 时注意只控制这几位,不要影响其他位。可以用 Window 的 addExtraFlags 和 clearExtraFlags 来修改, 这两个方法是 MIUI 增加的方法,需要反射调用。
int flag = 0x00000100 | 0x00000200 | 0x00000400;
try {
Method method = Window.class.getMethod("addExtraFlags",
int.class);
method.invoke(getWindow(), flag);
} catch (Exception e) {
Log.i(TAG, "addExtraFlags not found.");
}
华为 Android O 刘海屏适配
public static boolean hasNotchInScreen(Context context) {
boolean ret = false;
try {
ClassLoader cl = context.getClassLoader();
Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
Method get = HwNotchSizeUtil.getMethod("hasNotchInScreen");
ret = (boolean) get.invoke(HwNotchSizeUtil);
} catch (ClassNotFoundException e) {
Log.e("test", "hasNotchInScreen ClassNotFoundException");
} catch (NoSuchMethodException e) {
Log.e("test", "hasNotchInScreen NoSuchMethodException");
} catch (Exception e) {
Log.e("test", "hasNotchInScreen Exception");
} finally {
return ret;
}
}
使用新增的Meta-data属性android.notch_support,在应用的AndroidManifest.xml中增加meta-data属性,此属性不仅可以针对Application生效,也可以对Activity配置生效。
类文件 | 接口 | 接口说明 |
---|---|---|
com.huawei.android.view.LayoutParamsEx | public void addHwFlags(int hwFlags) | 通过添加窗口FLAG的方式设置页面使用刘海区显示:public static final int FLAG_NOTCH_SUPPORT=0x00010000; |
调用范例参考:
对Application生效,意味着该应用的所有页面,系统都不会做竖屏场景的特殊下移或者是横屏场景的右移特殊处理:
/*刘海屏全屏显示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");
}
}
类文件 | 接口 | 接口说明 |
---|---|---|
com.huawei.android.view.LayoutParamsEx | public void clearHwFlags (int hwFlags) | 通过去除窗口FLAG的方式设置页面不使用刘海区显示:public static final int FLAG_NOTCH_SUPPORT=0x00010000; |
调用范例参考:
/*刘海屏全屏显示FLAG*/
public static final int FLAG_NOTCH_SUPPORT=0x00010000;
/**
* 设置应用窗口在华为刘海屏手机使用刘海区
* @param window 应用页面window对象
*/
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");
}
}
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(isAdd) {//add flag
isAdd = false;
NotchSizeUtil.setFullScreenWindowLayoutInDisplayCutout(getWindow());
getWindowManager().updateViewLayout(getWindow().getDecorView(),getWindow().getDecorView().getLayoutParams());
} else{//clear flag
isAdd = true;
NotchSizeUtil.setNotFullScreenWindowLayoutInDisplayCutout(getWindow());
getWindowManager().updateViewLayout(getWindow().getDecorView(),getWindow().getDecorView().getLayoutParams());
}
}
});
VIVO刘海屏判断
public static final int VIVO_NOTCH = 0x00000020;//是否有刘海
public static final int VIVO_FILLET = 0x00000008;//是否有圆角
public static boolean hasNotchAtVivo(Context context) {
boolean ret = false;
try {
ClassLoader classLoader = context.getClassLoader();
Class FtFeature = classLoader.loadClass("android.util.FtFeature");
Method method = FtFeature.getMethod("isFeatureSupport", int.class);
ret = (boolean) method.invoke(FtFeature, VIVO_NOTCH);
} catch (ClassNotFoundException e) {
LogUtil.e( "Vivo","hasNotchAtVivo ClassNotFoundException");
} catch (NoSuchMethodException e) {
LogUtil.e( "Vivo","hasNotchAtVivo NoSuchMethodException");
} catch (Exception e) {
LogUtil.e( "Vivo","hasNotchAtVivo Exception");
} finally {
return ret;
}
}
OPPO刘海屏判断
public static boolean hasNotchAtOPPO(Context context) {
return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");
}