Android 刘海屏适配总结(推荐)
作者:developerHaoz 发布时间:2022-03-23 23:26:55
一、简介
随着 Apple 发布 iPhone X 之后,各大手机厂商也开始模仿这种刘海屏的设计,而且刘海屏手机的用户也是越来越大,前段时间将项目进行了所有主流厂商的刘海屏手机的适配,以便让刘海屏手机的用户也能有更好的体验。
二、刘海屏造成的 UI 显示问题
刘海屏手机因为比平常的手机多了一块顶部的遮挡性刘海,所以会造成顶部 Toolbar 以及搜索框的遮挡,而且有些厂商的手机(vivo、华为),默认是在「无状态栏」的界面将状态栏进行黑化显示,这时候会导致系统下移,从而导致底部的一些 UI 被截断。除此之外,一些控件的显示规则还会受到影响,如 PopupWindow 的显示高度会在「无状态栏」的界面中比普通手机低一个「刘海的高度」,从而遮挡住原先在 PopupWindow 周围的图标。
1、系统下移造成的底部 UI 截断
小说页码被截断
2、刘海挡住标题栏和搜索框
刘海挡住标题栏和搜索框
3、PopupWindow 显示异常
PopupWindow 显示异常
三、通用的适配方案
理论上来讲,通过 Android P 版本提供的刘海屏相关接口,判断手机是否为刘海屏手机,以及进行一些相应的处理是最合适的方式,但现在使用在国内使用 Android P 的接口是不现实的,所以只能通过各大厂商提供的技术文档来进行适配,但适配的流程基本是一致的。
刘海屏的适配流程
刘海屏的适配流程
其中需要着重处理的是:
1、应用是否已经适配刘海屏
2、页面是否显示状态栏
3.1 应用是否已经适配刘海屏
现在国内的主流机型(华为、vivo、OPPO、小米)在刘海屏的显示上分为两个阵营:
当不显示状态栏时,直接将界面进行显示,「状态栏原先的位置也用于显示界面」,例如:OPPO
当不显示状态栏时,直接「将状态栏原先的位置进行黑化,界面整体下移」,例如:华为、vivo
所以,我们在进行刘海屏适配的时候,首先需要通过一些手段,统一各大厂商的显示方案,让所有的刘海屏手机都利用状态栏的界面,「告知系统」我们已经适配了刘海屏,确保系统不会下移我们的应用,保留原生体验。
这里主要有两种方式:
1、设置屏幕高宽比例
因为刘海屏手机的「宽高比」比之前的手机大,如果不适配的话,Android 默认为最大的宽高比为 1.86,
小于刘海屏手机的宽高比,因此我们需要申明更高的宽高比来告诉系统,我们应用已经适配了刘海屏。
只要在 AndroidManifest.xml 中加入如下配置:
<meta-data android:name="android.max_aspect" android:value="2.1"/>
也可以在 Application 添加属性:
android:maxAspectRatio="ratio_float"
ps:这个属性需要 API 26 才支持
2、设置应用支持 resize
我们还可以通过设置应用支持 resizeable,来告诉系统我们适配了刘海屏,而且这也是 Google 官方推荐的方式。不过需要注意的是,使用这个属性之后,应用也会跟着支持分屏模式。只需要在 AndroidManifest.xml 中添加:
android:resizeableActivity="true"
3.2 页面是否显示状态栏
对于刘海屏适配,我们将界面分为两种:
对于有状态栏的界面,不会受到刘海屏的影响
全屏显示的界面(无状态栏),需要根据界面的显示进行一些控件的下移
因此,我们进行刘海屏适配,其实针对的就是没有状态栏的界面,而有状态栏的界面显示是正常的。对于没有状态栏的界面,主要是将对被刘海遮挡到的控件,设置对应刘海高度的 MarginTop,从而避免控件被遮挡。而对于底部可能被截断的界面,可以考虑将底部做成 ScrollView 的形式。
四、各厂商的适配方案
现在 Android P 的接口还没法用,但各手机厂商都制定了自己的 API,对此我们需要对各大机型进行特殊的适配,这里主要介绍 vivo、OPPO、华为 这三种主流手机的适配方案。
华为
华为作为国内的手机厂商大头,自己仿照 Android P 提供的 API,实现了一套几乎差不多的 API,所以我们如果想要告诉系统我们的应用适配了刘海屏,最好直接使用华为的 API,这样才是最保险的。
以下代码来自:华为刘海屏适配官方技术指导
1、应用页面设置使用刘海区显示
① 方案一:在 AndroidManifest.xml 中增加 meta-data 属性,此属性不仅可以针对 Application 生效,也可以对 Activity 配置生效:
<meta-data android:name="android.notch_support" android:value="true"/>
增加这个属性之后,系统就会对应用进行下移处理,从而保证原生体验。
② 方案二:通过添加窗口 FLAG 的方式设置界面使用刘海区:
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、判断该华为手机是否刘海屏
public static boolean hasNotchInHuawei(Context context) {
boolean hasNotch = false;
try {
ClassLoader cl = context.getClassLoader();
Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
Method hasNotchInScreen = HwNotchSizeUtil.getMethod("hasNotchInScreen");
if(hasNotchInScreen != null) {
hasNotch = (boolean) hasNotchInScreen.invoke(HwNotchSizeUtil);
}
} catch (Exception e) {
e.printStackTrace();
}
return hasNotch;
}
3、获取刘海的高度
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) {
Log.e("test", "getNotchSize ClassNotFoundException");
} catch (NoSuchMethodException e) {
Log.e("test", "getNotchSize NoSuchMethodException");
} catch (Exception e) {
Log.e("test", "getNotchSize Exception");
} finally {
return ret;
}
OPPO
OPPO 是主流厂商中的一股清流,学 iPhoneX 是最像的,OPPO 手机对于不显示状态栏的界面,采取的是「状态栏原先的位置也用于显示界面」的方案,所以我们只要进行相关控件的位置移动就可以了。
以下代码来自:OPPO 凹形屏适配说明
1、判断该 OPPO 手机是否为刘海屏手机
public static boolean hasNotchInOppo(Context context) {
return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");
}
2、获取刘海屏的高度
对于 OPPO 刘海屏手机的刘海高度,OPPO 官方的文档没有提供相关的 API,但官方文档表示 OPPO 手机的刘海高度和状态栏的高度是一致的,而且我也对此进行了验证,确实如此。所以我们可以直接获取状态栏的高度,作为 OPPO 手机的刘海高度。
public static int getStatusBarHeight(Context context) {
int statusBarHeight = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
}
return statusBarHeight ;
}
vivo
vivo 提供的技术文档对于开发者来说是最不友好的,只提供了一个 API 来进行刘海屏的判断,并没有提供刘海高度的获取方式,我们只能通过获取状态栏高度来当做刘海的高度,但在某些机型可能会有些偏差。
官方文档: vivo 手机适配指南
判断该 vivo 手机是否为刘海屏手机
public static boolean hasNotchInVivo(Context context) {
boolean hasNotch = false;
try {
ClassLoader cl = context.getClassLoader();
Class ftFeature = cl.loadClass("android.util.FtFeature");
Method[] methods = ftFeature.getDeclaredMethods();
if (methods != null) {
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if(method != null) {
if (method.getName().equalsIgnoreCase("isFeatureSupport")) {
hasNotch = (boolean) method.invoke(ftFeature, 0x00000020);
break;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
hasNotch = false;
}
return hasNotch;
}
五、总结
以上便是在之前在进行 Android 刘海屏适配的时候,所积累的一些经验和心得。将其记录下来,以便自己以后进行回顾,同时也希望这篇文章能对进行刘海屏适配的同学一些帮助。也希望大家多多支持脚本之家。
来源:https://www.jianshu.com/p/f93683dcb8b6
猜你喜欢
- java缓冲流本身不具IO功能,只是在别的流上加上缓冲提高效率,像是为别的流装上一种包装。当对文件或其他目标频繁读写或操作效率低,效能差。这
- 这篇效果和上一篇:https://www.jb51.net/article/100638.htm的效果是一样的,但是不再在OnTouchEv
- 需求说明实际操作过程中,从D盘根目录下的ak.txt读取文件写入D盘根目录下的hello.txt文件内实现思路写两个方法,一个用于读取目标文
- 1、原理事务的概念想必大家都很清楚,其ACID特性在开发过程中占有重要的地位。同时在并发过程中会出现一些一致性问题,为了解决一致性问题,也出
- 一、前言之前介绍了JMeter engine启动原理,但是里面涉及到HashTree这个类结构没有给大家详细介绍,这边文章就详细介绍JMet
- 第一种查看防火墙是否打开6379端口 查看防火墙状态systemctl status firewalld如果防火墙没有启动的话。可以选择直接
- 原文地址:http://www.javayihao.top/detail/84一:概述由于springboot项目,不管是java工程还是w
- 目录安装 BenchmarkDotNet什么是基准测试创建基准测试代码运行 benchmarkBenchmarkDotNet 是一个轻量级,
- 对象是使用new创建的,但是并没有与之相对应的delete操作来回收对象占用的内存。当我们完成对某个对象的使用时,只需停止对该对象的引用:将
- 最近在开发的过程中,一个列表的查询,涉及到了多表的关联查询,由于持久层使用的是mongodb,对这个非关系型数据使用的不是很多,所以在实现此
- 本文实例为大家分享了java通过PDF模板填写PDF表单的具体代码,包括图片,供大家参考,具体内容如下需要用到的java包: it
- 设计算法,计算两给定基因序列的相似程度。人类基因由4种核苷酸,分别用字母ACTG表示。要求编写一个程序,按以下规则比较两个基因序列并确定它们
- 一、项目结构二、pom.xml<?xml version="1.0" encoding="UTF-8&q
- Spring 框架是一个流行的Java应用程序框架,它提供了许多强大的功能,如依赖注入和面向切面编程。然而在使用 Spring 框架时,我们
- 创建一个简单的项目:<?xml version="1.0" encoding="UTF-8"?
- 这篇文章主要介绍了Java多态中动态绑定原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以
- 简单工厂模式介绍:概要:简单工厂模式,又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模
- 工厂顾名思义就是创建产品,根据产品是具体产品还是具体工厂可分为简单工厂模式和工厂方法模式,根据工厂的抽象程度可分为工厂方法模式和抽象工厂模式
- 目录规则(来自百度百科,康威生命游戏词条)控制台实现的关键接口代码实现规则(来自百度百科,康威生命游戏词条)游戏开始时,每个细胞随机地设定为
- 1.用法介绍方式一:DatatypeConverter说明:使用jdk自带的DatatypeConverter.java类实现,但是jdk版