最近公司项目设计到了刘海适配问题,对于这刘海也是醉了,看了官网和别人的博客才有些了解!
现在对于刘海适配最全面的也就华为了,写的很详细,对于vivo和oppo鄙视一下,写了文档就给个判断是否为刘海的方法,其余就是些废话了。
项目主要每个页面都有一个title,类似首页、视频,正好这俩字被刘海遮住了,没办法,只能适配了。
华为:
/**
* 判断华为手机是否为刘海屏
*
* @param context
* @return true为刘海屏,false非刘海屏
*/
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) {
} catch (NoSuchMethodException e) {
} catch (Exception e) {
} finally {
return ret;
}
}
/**
* 获取华为手机参数
*
* @param context
* @return int[0]值为刘海宽度 int[1]值为刘海高度
*/
public static int[] getNotchSize(Context context) {
int[] ret = new int[]{0, 0};
try {
ClassLoader cl = context.getClassLoader();
Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
Method get = HwNotchSizeUtil.getMethod("getNotchSize");
ret = (int[]) get.invoke(HwNotchSizeUtil);
} catch (ClassNotFoundException e) {
} catch (NoSuchMethodException e) {
} catch (Exception e) {
} finally {
return ret;
}
}
/**
* 设置应用窗口在华为刘海屏手机使用刘海区
*
* @param window 应用页面window对象
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
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(WindowManager.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) {
} catch (Exception e) {
}
}
oppo ,就给了一个方法,也不知道刘海的高度是多少,只能根据个人判断了
/**
* oppo判断是否为刘海屏
*/
public static boolean hasNotchInOppo(Context context){
return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");
}
vivo, 和oppo一样差不多
/**
* vivo 判断是否为刘海屏
*/
public static final int NOTCH_IN_SCREEN_VOIO = 0x00000020;//是否有凹槽
public static final int ROUNDED_IN_SCREEN_VOIO = 0x00000008;//是否有圆角
public static boolean hasNotchInScreenAtVoio(Context context) {
boolean ret = false;
try {
ClassLoader cl = context.getClassLoader();
Class FtFeature = cl.loadClass("com.util.FtFeature");
Method get = FtFeature.getMethod("isFeatureSupport", int.class);
ret = (boolean) get.invoke(FtFeature, NOTCH_IN_SCREEN_VOIO);
} catch (ClassNotFoundException e) {
} catch (NoSuchMethodException e) {
} catch (Exception e) {
} finally {
return ret;
}
}
看他们各个文档,以及google文档,刘海高度<=状态栏高度的
因为项目每个页面都有title,所以我直接在基类里设置的,通过公用的一个include引用
BaseActivity设置
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
//禁止横屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//include里面的title
TextView empty= (TextView) findViewById(R.id.empty);
/**
* 华为下移title位置
*/
boolean isHWphone = PhoneStyle.hasNotchInScreen(this);
if (isHWphone) {//true为刘海屏,false则执行布局默认的布局了,
int[] notchSize = PhoneStyle.getNotchSize(this);
LinearLayout.LayoutParams layout = (LinearLayout.LayoutParams) empty.getLayoutParams();
//title下移到刘海下面,log输出notchSize[1]高度为90,但是感觉下移的太多了,所以减了50px
layout.setMargins(0, notchSize[1] - 50, 0, 0);
empty.setLayoutParams(layout);
}
/**
* oppo下移title位置
*/
boolean isOppOPhone = PhoneStyle.hasNotchInOppo(this);
if (isOppOPhone){
LinearLayout.LayoutParams layout = (LinearLayout.LayoutParams) empty.getLayoutParams();
//oppo 不知道下移多少合适,折中选择了40px
layout.setMargins(0, 40, 0, 0);
empty.setLayoutParams(layout);
}
/**
* vivo下移title位置
*/
boolean isViVoPhone = PhoneStyle.hasNotchInScreenAtVoio(this);
if (isViVoPhone){
LinearLayout.LayoutParams layout = (LinearLayout.LayoutParams) empty.getLayoutParams();
//vivo和oppo一样
layout.setMargins(0, 40, 0, 0);
empty.setLayoutParams(layout);
}
}
华为手机经过真机测试,位置下移的正好合适,vivo和oppo因为没有刘海手机,没有测试,感觉vivo和oppo都应把刘海的高度给出才好计算。
如果使用刘海手机可以自己在studio里下载模拟器
可参考:[https://developer.android.google.cn/preview/download]
但是使用studio中的模拟器还需要支持Android P,可以下载 Android Studio 3.2 Canary预览版,studio版本能够并行,但是操作同一个项目可能不支持,只能打开一个。
在安装并打开 Android Studio 3.2 之后,按照如下步骤安装 Android P Preview SDK:
点击 Tools > SDK Manager。
在 SDK Platforms 标签下,选择 Android P Preview。
在 SDK Tools 标签下,选择 Android SDK Build-Tools 28-rc1 (或更高版本)。
点击 OK,开始安装。
修改gradle
要全面测试应用的 Android P 兼容性并开始使用新 API,请打开您的模块级 build.gradle 文件并更新 compileSdkVersion 和 targetSdkVersion,如下所示:
android {
compileSdkVersion 'android-P'
defaultConfig {
targetSdkVersion 'P'
}
...
}
具体参考Android开发者官网 :[(https://developer.android.google.cn/preview/setup-sdk)]
google在Android P版本提供了刘海的适配方案,主要时提供了一个类 DisplayCutout
主要有三个模式:
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:仅仅当系统提供的bar完全包含了刘海区时才允许window扩展到刘海区,否则window不会和刘海区重叠
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:允许window扩展到刘海区(原文说的是短边的刘海区, 目前有刘海的手机都在短边,所以就不纠结了)
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:不允许window扩展到刘海区。
允许页面扩展到刘海区
DisplayCutout cutout = view.getRootWindowInsets().getDisplayCutout();
if(cutout != null){
WindowManager.LayoutParams lp =getWindow().getAttributes();
lp.layoutInDisplayCutoutMode=WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
getWindow().setAttributes(lp);
}
google提供的需要studio升到3.2,现在仅仅时预览版的,只能用于测试,如果测试华为、vivo、oppo手机那就不行了,只能找真机测试,而且我用预览版studio打包出出来的apk安装不了,这就尴尬了,你适配了没法打包,也就没法上线,鸡肋。
oppo官网刘海文档 :[(https://open.oppomobile.com/wiki/doc#id=10159)]
vivo官网刘海文档:[(https://dev.vivo.com.cn/doc/document/info?id=103)]
华为官网刘海文档:[(https://devcenter-test.huawei.com/consumer/cn/devservice/doc/50114)]