如下图所示:
4.2.适配刘海屏
在刘海屏调试打开之后,浏览应用的所有页面,测试所有遮挡问题,或者是下移导致的问题,对有问题的页面进行布局适配。适配方案如下:
Google 提供的适配方案,可以设置是否在全屏模式下,使用刘海屏的区域。
// 谷歌官方提供的默认适配刘海屏
val attrib = window.attributes
attrib.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
新的布局属性layoutInDisplayCutoutMode包含三种可选的模式,分别为:
// 窗口声明使用刘海区域
public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS = 1;
// 默认情况下,全屏窗口不会使用到刘海区域,非全屏窗口可正常使用刘海区域
public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0;
// 声明不使用刘海区域
public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 2;
4.3.刘海屏的高度
在全屏模式下,我们的应用页面背景充满整个屏幕显示,控件和文字等关键信息布局在状态栏以外的区域,以保证关键信息不会出现遮挡(谷歌要求:凹槽高度和刘海高度要保持一致)。我们需要有办法获取到刘海屏凹槽的高度,才可以做到设计和布局的时候,留出安全距离。
4.3.1. 获取刘海尺寸信息接口
Android P已经预留出了标准的测量刘海屏凹槽的Api:DisplayCutout。
刘海屏的凹槽,就在屏幕的中间,所以只有**getSafeInsetTop()方法返回的结果,是我们需要的,而其他的getSafeInsetXXX()**方法,直接返回的是0。代码如下所示:
btn_always.postDelayed(Runnable {
val displayCutout = btn_always.rootWindowInsets.displayCutout
if (null == displayCutout) {
Log.e(TAG, “displayCutout is empty”)
return@Runnable
}
Log.i(TAG, “SafeInsetBottom:” + displayCutout.safeInsetBottom);
Log.i(TAG, “SafeInsetLeft:” + displayCutout.safeInsetLeft);
Log.i(TAG, “SafeInsetRight:” + displayCutout.safeInsetRight);
Log.i(TAG, “SafeInsetTop:” + displayCutout.safeInsetTop);
}, 100)
输出结果为:
SafeInsetBottom:0
SafeInsetLeft:0
SafeInsetRight:0
SafeInsetTop:84
4.3.2. 获取系统状态栏高度接口
获取刘海屏的高度之后,我们还要获取系统状态栏的高度,代码如下:
fun getStatusBarHeight(context: Context): Int {
var result = 0
val resourceId = context.resources.getIdentifier(“status_bar_height”, “dimen”, “android”)
if (0 < resourceId) {
result = context.resources.getDimensionPixelOffset(resourceId)
}
return result
}
5. 其他厂商刘海屏适配
像华为、Oppo和Vivo这样的厂商,实现刘海屏的方式,也并不是按照 Android P的标准做的,它完全是自己修改了刘海屏的实现方式。不过他们是会提供完备的适配文档,这就需要我们直接阅读他们提供的开发文档来进行适配。各个厂商的刘海屏适配参
考如下:
厂商 | 介绍 |
---|---|
华为 | https://mini.eastday.com/bdmip/180411011257629.html |
Oppo | https://open.oppomobile.com/wiki/doc#id=10159 |
Vivo | https://dev.vivo.com.cn/doc/document/info?id=103 |
5.1.华为
华为提供了刘海屏Api,可以通过反射的方式调用。
5.1.1. 判断是否刘海屏接口
代码如下:
/**
- 判断是否是华为刘海屏
- @param context 上下文对象
- @return true:是刘海屏;false:非刘海屏
*/
fun hasNotchInScreen(context: Context): Boolean {
var ret = false
try {
val cl = context.getClassLoader()
val HwNotchSizeUtil = cl.loadClass(“com.huawei.android.util.HwNotchSizeUtil”)
val method = HwNotchSizeUtil.getMethod(“hasNotchInScreen”)
ret = method.invoke(HwNotchSizeUtil) as Boolean
} catch (e: ClassNotFoundException) {
Log.e(TAG, “hasNotchInScreen ClassNotFoundException”)
} catch (e: NoSuchMethodException) {
Log.e(TAG, “hasNotchInScreen NoSuchMethodException”)
} catch (e: Exception) {
Log.e(TAG, “hasNotchInScreen Exception”)
} finally {
return ret
}
}
5.1.2. 获取刘海尺寸信息接口
代码如下:
/**
- 获取华为刘海的高宽
- @param context 上下文对象
- @return [0]值为刘海宽度int;[1]值为刘海高度
*/
fun getNotchSize(context: Context): IntArray {
var ret = intArrayOf(0, 0)
try {
val cl = context.classLoader
val HwNotchSizeUtil = cl.loadClass(“com.huawei.android.util.HwNotchSizeUtil”)
val method = HwNotchSizeUtil.getMethod(“getNotchSize”)
ret = method.invoke(HwNotchSizeUtil) as IntArray
} catch (e: ClassNotFoundException) {
Log.e(TAG, “getNotchSize ClassNotFoundException”)
} catch (e: NoSuchMethodException) {
Log.e(TAG, “getNotchSize NoSuchMethodException”)
} catch (e: Exception) {
Log.e(TAG, “getNotchSize Exception”)
} finally {
return ret
}
}
5.1.3. 应用页面设置使用刘海区显示
给window添加华为新增的FLAG_NOTCH_SUPPORT方式,代码如下所示:
/**
- 设置应用窗口在华为刘海屏手机使用挖孔区
- @param window 应用页面window对象
*/
fun setFullScreenWindowLayoutInDisplayCutout(window: Window?) {
if (null == window) {
return
}
val layoutParams: WindowManager.LayoutParams = window.attributes
try {
val layoutParamsExCls = Class.forName(“com.huawei.android.view.LayoutParamsEx”)
val con = layoutParamsExCls.getConstructor(WindowManager.LayoutParams::class.java)
val layoutParamsExObj = con.newInstance(layoutParams)
val method = layoutParamsExCls.getMethod(“addHwFlags”, Int::class.javaPrimitiveType)
method.invoke(layoutParamsExObj, FLAG_NOTCH_SUPPORT)
} catch (e: ClassNotFoundException) {
Log.e(TAG, “hw notch screen flag api error”)
} catch (e: NoSuchMethodException) {
Log.e(TAG, “hw notch screen flag api error”)
} catch (e: IllegalAccessException) {
Log.e(TAG, “hw notch screen flag api error”)
} catch (e: InstantiationException) {
Log.e(TAG, “hw notch screen flag api error”)
} catch (e: InvocationTargetException) {
Log.e(TAG, “hw notch screen flag api error”)
} catch (e: Exception) {
Log.e(TAG, “other Exception”)
}
}
5.2.Oppo
对于Oppo而言,它刘海的高度是固定的,就是80px。
判断当前设备是否是刘海屏,也提供了对应的 Api,可以用以下方法获取。代码如下所示:
/**
- 判断是否是Oppo刘海屏
- @param context 上下文对象
- @return true:是刘海屏;false:非刘海屏
*/
fun hasNotchInScreenAtOppo(context: Context): Boolean {
return context.packageManager!!.hasSystemFeature(“com.oppo.feature.screen.heteromorphism”)
}
返回 true 为刘海屏,但是这种方法只能识别Oppo品牌所支持的刘海屏。
5.3.Vivo
在Vivo系统中,增加了一个接口来判断此设备是否具有凹槽,我们可以使用发射的方式调用。代码如下所示:
/**
- 判断Voio是否有凹槽
- @param context 上下文对象
- @return true表示具备此特征,false表示没有此特征
*/
fun hasNotchInScreenAtVoio(context: Context): Boolean {
var ret = false
try {
val cl = context.classLoader
val FtFeature = cl.loadClass(“android.util.FtFeature”)
val method = FtFeature.getMethod(“isFeatureSupport”, Int::class.javaPrimitiveType)
ret = method.invoke(FtFeature, NOTCH_IN_SCREEN_VOIO) as Boolean
} catch (e: ClassNotFoundException) {
Log.e(TAG, “hasNotchInScreen ClassNotFoundException”)
} catch (e: NoSuchMethodException) {
Log.e(TAG, “hasNotchInScreen NoSuchMethodException”)
} catch (e: Exception) {
Log.e(TAG, “hasNotchInScreen Exception”)
} finally {
return ret
}
}
6. 结语
======
看完本篇文章,我想你对Android P的刘海屏也有一定的认识了,现在还不确定不同厂商会不会对其微调,所以你要是碰到什么问题,欢迎一起研究学习,不妨在留言区留言讨论。
最后
小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
资料⬅专栏获取
,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助**。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
[外链图片转存中…(img-NB6F1YCJ-1719099768272)]一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
资料⬅专栏获取