API处理Android安全距离详情
作者:? 发布时间:2023-12-24 05:16:19
前言
在Android屏幕的空间中,大部分的区域我们都是可以随意绘制,只有一部分区域是显示的固定内容:
状态栏
标题栏(ActionBar)
页面内容(Content)
导航栏
其中标题栏是可选的,除了Material风格的应用应用的并不多,页面内容就是android.R.id.content
是Activity的主要内容。
而我们主要需要讨论的就是 状态栏和导航栏,因为这两个区域在不同设备类型,不同的Android版本和不同的厂商下大小和效果是不同的,等等。这些差异无疑增加了我们做页面适配的复杂程度,也更容易出现兼容问题。
在2017年下半年iPhone X的发布,引入了刘海屏设备,导致了蓝绿大厂争相效仿,同时又自成一派,颇有一番百家争鸣之象。 这也导致了一个新的问题 刘海区域适配 ,那时候Android才8.1,并没有API来支持这屏幕上这多出来的一块区域,不过好在大部分设备在定制时刘海和状态栏高度是一致的。
终于在2018年发布的Android 9中Google正式支持了刘海屏,定制了规范约束了设备厂商,减轻了刘海屏适配的差异问题,但是根源问题并没有解决。因为刘海区域的存在,可能会出现页面内容被遮挡,比如:启用页广告跳过按钮被遮挡的问题,导致被应用商店拒掉的风险。
不过好在Android 9中要求刘海设备必须有以下行为:
一条边缘最多只能包含一个刘海。
一台设备不能有两个以上的刘海。
设备的两条较长边缘上不能有刘海。
在未设置特殊标志的竖屏模式下,状态栏的高度必须与刘海的高度持平。
默认情况下,在全屏模式或横屏模式下,整个刘海区域必须显示黑边。
刘海高度默认是和状态栏高度一致依旧没有变,所以问题又回到了状态栏区域的处理。
描述
所以肯定有同学说了:直接获取状态栏高度不就可以了适配刘海屏了。像这样:
val top = context.getStatusBarHeight()
titleBar.setPadding(0, top, 0, 0)
这么说也没有错,大部分情况下是没有问题的。但是既然官方已经适配刘海屏了,也为我们提供了新的API为什么不用呢:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.decorView.post {
val top = window.decorView.rootWindowInsets?.displayCutout?.safeInsetTop ?: 0
// val bottom = window.decorView.rootWindowInsets?.displayCutout?.safeInsetBottom ?: 0
titleBar.setPadding(0, top, 0, 0)
}
}
上面的方案实际上可以获取上下左右四个方向的安全距离,但大部分情况我们只需要处理顶部就可以了。实际上这已经可以解决我们的问题了,但是还有更好的解决方案方案:
添加依赖:
implementation 'androidx.core:core:1.7.0'
// 老版本也可以,但是getInsets() API 还没添加
// implementation 'androidx.core:core:1.3.0'
使用ViewCompat工具:
ViewCompat.setOnApplyWindowInsetsListener(titleBar) { view: View, insets: WindowInsetsCompat ->
//val top = insets.systemWindowInsetTop // 高版本已经过时,可以用下面的api替换
val stableInsets = insets.getInsets(
WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout())
titleBar.setPadding(0, stableInsets.top, 0, 0)
return@setOnApplyWindowInsetsListener insets
}
实际上屏幕安全距离,基本上全部围绕这一个API,Google也推荐我们这么做,在很多系统控件都能看到它的影子,比如:AppBarLayout、DrawerLayout、NavigationBarView
等等都有用到,内部都是来处理系统安全距离的。
系统栏适配
上面提到了手机有各种系统栏(状态栏、导航栏),如果一个全屏+刘海屏+透明系统栏+屏幕旋转的页面处理这些安全距离就更复杂,比如短视频页,这里先给大家列几条可能出现的问题:
没有导航栏或者可以动态隐藏导航栏的设备
导航栏不会旋转的设备(就是导航栏一直在屏幕的一个边,不会跟随屏幕旋转)
导航栏跟随屏幕旋转的设备(主要是手势导航的设备和一些平板上)
刘海在屏幕底部的设备(开发者选项可以开启双刘海模式,设备两个短边都有刘海)
底部刘海+导航栏一起显示的设备
... ...
这些所有的问题通过 ViewCompat.setOnApplyWindowInsetsListener()
来优雅处理, 通过 WindowInsetsCompat.getInsets(type)
可以获取系统的各个栏的大小, 我们也可以同时获取多个系统栏的高度,各个距离内部会进行累加,返回一个类似Rect的对象,对应屏幕的左上右下需要插入的距离:
val stableInsets = insets.getInsets(
WindowInsetsCompat.Type.statusBars() or
WindowInsetsCompat.Type.navigationBars() or
WindowInsetsCompat.Type.displayCutout())
然后在对不同位置的控件添加对应的边距。除了上面提到的三种类型的安全距离,还有一些其他的类型,有兴趣的可以自己了解。
其他适配
ViewCompat.setOnApplyWindowInsetsListener()
能解决大部分安全距离的问题,但是有一点它是处理不了的,就是 屏幕圆角,这些安全距离的计算是不处理屏幕圆角的,所以如果有圆角要处理那我们就要另辟蹊径了。
好在Android 12中官方添加了对圆角的支持:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val roundedCorner = insets.toWindowInsets()
?.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT)
roundedCorner?.center
}
我用了Pixel4真机发现能获取到数据,但是模拟器获取不到。
除了圆角支持,还有对隐私指示器提供了支持:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val rect = insets.toWindowInsets()?.privacyIndicatorBounds
// 页面控件需要避开这个区域,不然可能会被遮挡
}
隐私指示器的范围,主要是 摄像头和麦克风 使用中状态的指示器边界,如果是录制直播或者相机的页面需要处理这个区域。
除了圆角以外,好像没有找到官方对打孔屏的支持,可能后面会加入对打孔屏的支持吧。
来源:https://juejin.cn/post/7106404688924246023
猜你喜欢
- FTP 是File Transfer Protocol(文件传输协议)的英文简称,而中文简称为“文传协议”。用于Internet上的控制文件
- 1.配置代理系统管理---configure Global Security(全局安全设置)---Tcp port for inbound
- 前言本文准确来讲是探讨如何用 Jackson 来序列化 Apache avro 对象,因为简单用 Jackson 来序列化 Apache a
- 由于Android对单个应用所施加的内存限制,比如16MB,这导致加载Bitmap的时候很容易出现内存溢出,本文主要包含2个方面的内容分析B
- 关联篇:HandlerThread 使用及其源码完全解析关联篇:Handler内存泄漏详解及其解决方案一说到Android的消息机制,自然就
- 什么是SkyWalking查看官网https://skywalking.apache.org/分布式系统的应用程序性能监视工具,专为微服务、
- 前言在日常开发中,除了修改请求参数、设置响应header,响应body外,还有一种需求就是url重新,或者是修改url,这里简述一下怎么在z
- springboot和springmvc的区别spring boot 内嵌tomcat,Jetty和Undertow容器,可以直接运行起来,
- 本节了解一下 SpringBoot 中 Web 开发的静态资源导入和首页设置,对应 SpringBoot-03-Web 项目。1. 静态资源
- 前言Date 类Date 类表示系统特定的时间戳,可以精确到毫秒。Date 对象表示时间的默认顺序是星期、月、日、小时、分、秒、年。构造方法
- 在这篇文章中,我将向您展示如何用新的Java 8 forEach语句循环一个List和Map。1、forEach 和 Map1.1、常规循环
- 1. 自动化装配介绍Spring Boot针对mvc做了大量封装,简化开发者的使用,内部是如何管理资源配置,Bean配置,环境变量配置以及启
- 前情提要本文中提供了九种方式获取resources目录下文件的方式。其中打印文件的方法如下: /**
- @JsonInclude(JsonInclude.Include.NON_NULL)不起作用记录一下使用@JsonInclude(JsonI
- 1.重载重载指在一个类中,具有多个相同名称的方法,他们的参数列表却不相同(参数类型不同、参数个数不同甚至是参数顺序不同)重载对返回类型没有要
- 本文实例讲述了C#实现在前端网页弹出警告对话框(alert)的方法。分享给大家供大家参考。具体如下:通常我们通过JS生成警告对话框,下面的代
- 多数据源配置首先是配置文件这里采用yml配置文件,其他类型配置文件同理我配置了两个数据源,一个名字叫ds1数据源,一个名字叫ds2数据源,如
- 下面是一个AOP实现的简单例子:首先定义一些业务方法:/** * Created with IntelliJ IDEA. 
- 一、简单介绍翻看Spring的源码时,发现@Bean注解的源码上标注了Since: 3.0,也就是说,@Bean注解是Spring从3.0版
- 本文实例讲述了C#基于NPOI生成具有精确列宽行高的Excel文件的方法,是非常具有实用价值的技巧分享给大家供大家参考。具体方法如下:。一、