折叠屏
-
折叠屏为什么需要适配
折叠屏在视觉效果来说就是,屏幕变大了,手机变平板。这样就需要我们的app在可折叠设备展开时,当前应用页面必须无缝延续到另一个屏幕,并可自动调整大小匹配新的布局,也就是说,应用程序需要准备好在多个屏幕(不同分辨率、密度等)之间切换。折叠屏之所以需要适配,是因为我们的应用有可能在运行的过程中,所在的屏幕尺寸发生了变化,这种情况对现有项目多少都会产生一些问题。
其实这种情况并不是折叠屏出现之后才有的,应用的纵向横向切换也会发生同样的情况,只不过很多应用都强制纵向,不需要处理这种适配了。
折叠屏适配的本质
折叠屏适配的本质是:当应用运行时,屏幕的尺寸、密度或比例发生了变化,应用能够继续在变化后的屏幕上正常显示和正常运行。
对于开发同学来说,对折叠屏的适配首先要确定一个预期,即要先确定好交互和设计,才能评估工作。因此”折叠屏“的适配先是一个设计问题,然后才是一个适配问题。
适配指导
- 相对单位:为了适应不同屏幕尺寸和不同使用场景,使用绝对单位容易出现问题。
- 临界点:比如屏幕宽度小于这个宽度时显示一个样式,大于这个宽度时显示另一个样式。
- 应用支持自适应能力:建议应用支持可变比例显示,在可预见的屏幕比例范围内,都可以做到良好适配。
<application
android:resizeableActivity="true"//Android7及以上默认开启
- 设置应用支持的最大比例和最小比例适配
当resizeableActivity为false时,展开折叠屏可能会变成这样的效果,这个效果类似在iPad上使用不兼容的iPhone应用,这个四周用黑色填充的模式,叫兼容模式。
兼容模式的显示和最大支持比例maxAspectRatio有关,当屏幕比例超过maxAspectRatio时才会用黑边填充,官方建议把maxAspectRatio设为2.4(12:5)
- 切换显示比例应用不重启适配
折叠/展开的操作过程将触发系统向应用发送新布局的配置更改,包括smallestScreenSize、screenSize和screenLayout的配置
折叠屏展开——折叠之后Activity的生命周期,可以发现Activity会销毁后重建
onPause()->onStop()->onDestory()->onCreate()->onStart()->onResume()
如果想禁止Activity销毁重建,则需要在AndroidManifest中对Activity的configChanges进行如下的配置:
<activity
android:configChanges="screenSize|smallestScreenSize|screenLayout"
android:name=".MainActivity"
android:exported="true">
这个时候折叠屏展开——折叠会走Activity#onConfigurationChanged,就不会走activity的生命周期,更新视图布局、重新加载资源。通过此方法既能实现在系统不重启Activity的情况下重置UI。总的来说,折叠屏的适配有点类似平板+旋转屏的综合体,也需要配置文件+多布局。
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if(FoldableDeviceUtil.INSTANCE.isFold()){//是否有折叠屏
//瀑布流
}
}
package com.example.hilibrary.util
import android.app.Application
import android.os.Build
import android.text.TextUtils
import org.w3c.dom.Text
object FoldableDeviceUtil {
val application: Application? =null
fun isFold():Boolean{
return if(TextUtils.equals(Build.BOARD,"samsung")&&
TextUtils.equals(Build.DEVICE,"Galaxy Z Fold2")){
return HiDisplayUtil.getDisplayWidthInPx(application!!)!=1768
}else if(TextUtils.equals(Build.BOARD,"huawei")&&
TextUtils.equals(Build.DEVICE,"MateX")){
return HiDisplayUtil.getDisplayWidthInPx(application!!)!=2200
}else if(TextUtils.equals(Build.BOARD,"google")&&
TextUtils.equals(Build.DEVICE,"generic_x86")){//模拟器
return HiDisplayUtil.getDisplayWidthInPx(application!!)!=2200
}else{
true
}
}
}
可折叠设备没有android api或回调可以让我们知道当前处于折叠模式还是展开模式。
对于不同尺寸屏幕适配过程中,为了确保在折叠屏各个屏幕形态下获取最佳的布局显示效果,应用界面正确、美观的布局和显示,包含如下:
- 布局能够根据屏幕适当地调整大小
- 根据屏幕配置提供合适的UI布局
- 提供可正常缩放的位图
ConstraintLayout
- 基于自适应尺寸的wrap_content、match_parent、weight避免固定的尺寸单位。
- 使用基于控件相对位置的布局,现在更加推荐使用ConstraintLayout,对于布局性能以及扁平化更具有优势。
- 考虑使用自动拉伸的.9图或者使用矢量图。
Android Q暗黑模式适配
暗黑模式的适配大概可以分为三部分:
- 背景
- 文字
- 图标
适配的方式也分为三种:自动适配、自定义适配、换肤框架适配
自动适配暗黑模式
Android 10提供Force Dark功能,此功能可以让开发者快速实现深色主题背景。
<style name="Base.Theme.MyApplication" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your light theme here. -->
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
<item name="android:forceDarkAllowed" tools:ignore="NewApi" />//添加这句代码即可
</style>
自定义适配
主动切换主题
换肤方式适配
这种方式主要是获取和监听系统深色主题模式的开启状态动态设置主题或皮肤。
- 首先我们需要定义好深色主题或者皮肤,换肤功能通过Android-skin-support来实现。
- onCreate方法中在setContentView之前判断是否开启暗黑模式,设置暗主题。
Android权限治理
- 仅申请业务功能所需权限
- 告知申请权限的目的
- 调用权限的频率应合理
- 增强用户的权限控制机制
仅申请业务功能所需权限
App运营者应充分调研并明确业务功能所需的权限,不应申请App不会用到的权限或者一次性申请所有权限,还需要注意区分必要权限和业务所需权限。
- 必要权限
是指保障App正常运行所必需的最少权限,一般都是在App首次开启时,需用户同意授权必要权限后,才能正常进入App主页面(常见的有“存储权限”等) - 业务所需权限
是指为了保障app在运行时某项功能正常运行,不得不向用户申请权限(比如录音,电话,相机)。App运营者需保证业务所需权限的合理性。
不能在实现某种业务功能时,将开启权限作为唯一的实现方式,强迫用户打开权限(比如在实现添加联系人信息火手机充值功能时,仅提供打开通讯录权限并选取联系人这一种方式,不同时提供用户自主输入联系人的方式)
告知申请权限的目的
App向用户申请权限时未告知目的导致的误会已成为一大痛点,比如,某音乐类App向用户申请存储权限(部分手机在申请该权限会显示是否允许读取照片/视频),用户会疑惑音乐类app为什么要读取照片,而且不同意还不能使用,这不是典型的强制索要无关权限吗?
事实上,此类App申请存储权限是为了音乐下载和离线缓存,和读取照片视频无关,申请权限时做出解释立马真相大白,可见告知申请权限的目的的重要性。
申请权限的弹窗机制在不同手机操作系统上表现不同,比如,在iOS系统中,申请权限的弹窗事实上是可以编辑文字的,如此可很方便地将申请权限的目的在弹窗予以注明。但是Android系统就无法更改,这个是手机厂商写死的。
申请权限前弹窗告知
申请权限弹窗出现前,由App自行以弹窗等形式将需要申请权限的目的予以明确告知
被拒后再弹窗说明
先由系统进行弹窗申请权限,如果用户不理解点击拒绝后,再次弹出提示框向用户告知申请权限的目的
并不是所有的申请权限均需要采取弹窗告知方式
在使用App的过程中,用户选择了“拍照”或“共享位置”功能,App弹窗告知需要获取“拍摄照片和录制视频”等权限,虽未单独再通过弹窗等方式告知申请目的,但完全不影响用户对申请该权限目的的理解。
综上,使用额外弹窗等方式告知申请权限的目的,更多地是针对首次运行App时申请权限的情形,对于用户而言,在首次打开App且未进入主界面前,用户无法看到App的功能界面,无法理解具体的功能需求,因此,在此阶段以增强提示的方式告知申请权限的目的是合规的重点。
选择恰当的时机和方式
从App申请权限的时机来看,通常分为以下三类情形
- 第一类是在安装App时申请
以安装App时申请权限为例,有App会基于安卓系统的权限申请机制,采取将其targetSDKversion值设置小于23的方式,要求用户一次性同意开启多个可收集个人信息权限,不同意则无法安装使用APP。这种是应当被叫停的一种权限申请机制。 - 第二类是在首次打开app时申请
以首次打开App时申请权限为例,绝大部分App会在该阶段申请其主要业务功能所需的权限,尤其是支持App基本业务功能运行的必要权限。根据不同手机所采取的机制不同,有的是通过多次弹窗形式实现,有的是一次性让用户选择需开启的权限。
该阶段受手机操作系统的制约较大,不便于向用户同步告知申请权限的目的,也不便于让用户判断哪些权限是必要的,充分行使其选择权。因此,建议App在该阶段仅申请支持其基本业务功能所需的最小必要的权限范围。
比如,App无需任何权限可进入主界面进行浏览等基本操作,可选择在该阶段实现零申请的方式,减少用户对申话权限的疑惑和误解,省去以弹窗等方式同步告知申话权限目的,给予用户零打扰的良好体验。 - 第三类是使用App时根据需求逐项申请
使用App时根据实际需求逐项申请,该方式无疑是最为合理和推荐的权限申请方式。一方面用户可以根据具体业务功能和服务类型等准确理解申请权限的目的,App无需再对目的进行资述,另一方面用户可以充分行使其选择权,让授权过程不再成为一笔糊涂账。比如在需要扫码或照相时,App才会申请相机权限,在同步通讯录好友时,才会申请通讯录权限。而当用户拒绝时,仅影响当前功能的实现。
此外,频繁申请权限(如每次重新打开App便重新索要权限、使用App过程中会反复触发申请权限机制等)造成用户打扰已经成为网友们广泛“诟病”的问题。
新发布的国家标准草案“移动互联网应用 (App)收集个人信息基本规范”给出明确指导性建议,用户明确拒绝使用某服务类型后,App不得频繁 (每48小时不超过一次)征求用户同意使用该类型服务,并保证其他服务类型正常使用。
总之,申请权限的时机和方式,与用户体验紧密相关,建议App运营者全面考虑各种要素,优化App中请权限过程,不要让申请权限过程变得别扭、将就,最终影响的是用户对App的印象和用户的转化率。
调用权限的频率应合理
权限是手机个人信息的一扇窗户,一旦窗户打开,手机个人信息的行为随时可能发生。而对于用户来讲,调用权限相关函数获取个人信息的过程无法感知,心里没底。这就可能会导致出现以下超出合理频率范畴过度采集个人信息的行为。
-
在用户未使用某业务功能时,就已经发生调用权限收集与该业务功能相关个人信息的行为。
用户在首次开启App时开启了“通讯录”、“短信”等权限,用户并未使用涉及该权限的功能,仅是浏览主界面,便出现了调用权限相关函数上传个人信息的行为。 -
调用权限收集个人信息的频次明显超出实现业务功能所需。
比如开启位置权限实现查询当地天气的功能,但实际App每秒都获取并上传用户的精准位置。 -
当App在静默或后台运行时,仍存在不断调用权限收集个人信息的行为。
事实上,以上不规范的调用权限方式均可以使用技术手段进行监测,建议App运营者详尽分析自身业务功能所需调用权限的具体频率,必要时,还可以在隐私政策等向用户直接说明。
增强用户的权限控制机制
众所周知,手机操作系统自身已经为用户提供了开启、关闭权限的控制机制。但是,从用户体验来看,在安装大量App后,使用该机制查询权限开启情况或改变权限设置等操作变得不太方便。此外,很多平台型APP上存在大量第三方应用或服务也涉及到调用权限的问题,但手机操作系统并未提供额外的控制机制。建议App运营者进一步考虑用户对于权限的控制需要,优化和创新用户对权限的控制机制。比如,某App专门在其"隐私设置”中设置了方便查询权限开启情况的交互式页面,并提供简单易懂可迅速切换至权限控制页面的通道。