一、关于刘海屏:
Android 9 支持最新的全面屏,其中包含为摄像头和扬声器预留空间的屏幕缺口。 通过 DisplayCutout
类可确定非功能区域的位置和形状,这些区域不应显示内容。 要确定这些屏幕缺口区域是否存在及其位置,请使用 getDisplayCutout()
函数全新的窗口布局属性 layoutInDisplayCutoutMode
让您的应用可以为设备屏幕缺口周围的内容进行布局。 您可以将此属性设为下列值
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
二、主流国产机型(华为、小米、vivo、oppo)适配方案
/**
*华为手机判断是否有刘海屏
*/
private boolean hasNotchAtHuaWei(Context context) {
boolean ret = false;
try {
ClassLoader classLoader = context.getClassLoader();
Class cl = classLoader.loadClass("com.huawei.android.util.HwNotchSizeUtil");
Method get = cl.getMethod("hasNotchInScreen");
ret = (boolean) get.invoke(cl);
} catch (ClassNotFoundException e) {
Log.e("MyLog", "hasNotchAtHuawei ClassNotFoundException");
} catch (NoSuchMethodException e) {
Log.e("MyLog", "hasNotchAtHuawei NoSuchMethodException");
} catch (Exception e) {
Log.e("MyLog", "hasNotchAtHuawei Exception");
}
return ret;
}
/**
* 获取华为手机刘海的尺寸
* @return 宽高 px
*/
private int[] getNotchSizeAtHuawei(Context context) {
int[] ret = new int[]{0, 0};
try {
ClassLoader cl = context.getClassLoader();
Class sizeUtils = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
Method get = sizeUtils.getMethod("getNotchSize");
ret = (int[]) get.invoke(sizeUtils);
} catch (ClassNotFoundException e) {
Log.i("MyLog", "getNotchSizeAtHuawei ClassNotFoundException");
} catch (NoSuchMethodException e) {
Log.i("MyLog", "getNotchSizeAtHuawei NoSuchMethodException");
} catch (Exception e) {
Log.i("MyLog", "getNotchSizeAtHuawei Exception");
}
return ret;
}
/**
* vivo手机 判断是否有刘海屏
* vivo不提供接口获取刘海尺寸,目前vivo的刘海宽为100dp,高为27dp。(100dp x 27dp)
*/
private static boolean hasNotchAtVivo(Context context) {
boolean ret = false;
try {
ClassLoader classLoader = context.getClassLoader();
Class feature = classLoader.loadClass("android.util.FtFeature");
Method method = feature.getMethod("isFeatureSupport", int.class);
ret = (boolean) method.invoke(feature, VIVO_NOTCH);
} catch (ClassNotFoundException e) {
Log.e("MyLog", "hasNotchAtVivo ClassNotFoundException");
} catch (NoSuchMethodException e) {
Log.e("MyLog", "hasNotchAtVivo NoSuchMethodException");
} catch (Exception e) {
Log.e("MyLog", "hasNotchAtVivo Exception");
}
return ret;
}
/**
* OPPO 是否有刘海屏
* 显示屏宽度为1080px,高度为2280px。刘海区域则都是宽度为324px, 高度为80px。
*/
private boolean hasNotchAtOPPO(Context context) {
return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");
}
/**
* 小米手机获取刘海高度
*/
private int getXiaoMiCutH(Context context) {
int resourceId = context.getResources().getIdentifier("notch_height", "dimen", "android");
int result = 0;
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
/**
* 获取刘海高度
*/
public int adaptation() {
int cutHeight = 0;
if (Constant.FinalVar.HUAI_WEI.equalsIgnoreCase(Constant.Variable.carrier)) {
if (hasNotchAtHuaWei(mContext)) {
cutHeight=getNotchSizeAtHuawei(mContext)[1];
}
} else if (Constant.FinalVar.XIAO_MI.equalsIgnoreCase(Constant.Variable.carrier)) {if (SystemPropertiesUtils.getInt("ro.miui.notch", 0) == 1) {
cutHeight = getXiaoMiCutH(mContext);
}
} else if (Constant.FinalVar.VIVO.equalsIgnoreCase(Constant.Variable.carrier)) {
if (hasNotchAtVivo(mContext)) {
cutHeight = DpPxUtils.dip2px(mContext,27);
}
} else if (Constant.FinalVar.OPPO.equalsIgnoreCase(Constant.Variable.carrier)) {
if (hasNotchAtOPPO(mContext)) {
cutHeight =80;
}
}
return cutHeight;
}
/**
* 设置状态栏透明
*/
public void initStateBar() {
Window window = ((Activity) mContext).getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
int cutHeight=adaptation();
if (cutHeight>0){
window.setStatusBarColor(Color.BLACK);
}else {
window.setStatusBarColor(Color.TRANSPARENT);
window.setNavigationBarColor(Color.TRANSPARENT);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
if (cutHeight==0){
try {
@SuppressLint("PrivateApi")
Class decorViewClazz = Class.forName("com.android.internal.policy.DecorView");
Field field = decorViewClazz.getDeclaredField("mSemiTransparentStatusBarColor");
field.setAccessible(true);
field.setInt(window.getDecorView(), Color.TRANSPARENT);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
在BaseActivity通用设置,给跟布局设置一个刘海高度的paddingtop
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
int cutHeight=densityUtil.adaptation();
if (cutHeight > 0) {
ViewGroup vg = ((Activity) mContext).findViewById(android.R.id.content);
vg.setPadding(0, cutHeight, 0, 0);
}
}
小米手机判断是否有刘海缺口的工具类
public class SystemPropertiesUtils {
public static int getInt(String s, int i) {
int res = 0;
try {
Class<?> aClass = Class.forName("android.os.SystemProperties");
Method[] declaredMethods = aClass.getDeclaredMethods();
Method temp = null;
for(Method m:declaredMethods){
if ("getInt".equals(m.getName())){
temp=m;
break;
}
}
if (temp!=null){
res = (int) temp.invoke(null,s, i);
}
/// Method method = aClass.getMethod("getInt", String.class, Integer.class);
} catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
Log.i("MyLog",">>>>>>>>>>>SystemPropertiesUtils="+e.toString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
Log.i("MyLog",">>>>>>>>>>>SystemPropertiesUtils="+e.toString());
}
return res;
}
}