参考文章
1. 折叠屏简介
1.1 诞生的背景
手机屏幕目前的整体发展趋势是大屏化,大屏化主要表现在两方面:屏幕面积的增大,屏占比的提高。但是目前这两方面已经发展的相当成熟,很难再有大的突破。
折叠屏手机的出现不仅实现了屏幕尺寸增加,同时还满足携带方便的需求,有效解决大屏和便携矛盾,未来或将成为手机发展的重要方向之一?
1.2 折叠屏手机结构
折叠屏的主要结构由柔性屏与铰链组成,利用全新的柔性屏技术,可实现屏幕的弯曲和折叠。折叠屏手机一般都有一个折页或者铰链,将两个显示屏分开,带有铰链的设备可以在铰链后面跨越内容,有些设备还可以半折叠或者翻盖,支持折叠和部分折叠状态。但通常,我们见到的折叠屏手机都是由1整块AMOLED屏组成,并且铰链位置可以显示画面。
除此之外,还有双屏手机(屏幕由2块组成,铰链区域不可显示画面),也被称为折叠屏手机,如:微软2020年9月推出的基于Android 10.0系统的Surface Duo。

1.3 折叠屏手机市场规模
2020国内智能手机销量约3亿部,而同期全球的折叠屏手机出货量仅194.73 万部,三星以**71.59%的市场份额占据全球折叠屏市场第一,华为市场份额10.56%**位居第二。
从销量来说,折叠屏市场占有依然比较小众,主要原因在于折叠屏手机目前良品率还不够高,产能受限,进而导致手机售价过高(华为Mate X 的AMOLED屏供应商为京东方,京东方折叠屏良品率数据为:2018,65%;2021,85%);同时,软件方面,大部分软件还未进行完全适配,影响用户体验。
2. 折叠屏适配发展
2.1 基础屏幕适配(包括折叠屏)
2.1.1 灵活布局
使用wrap_content、match_parent避免硬编码
使用 ConstraintLayout做根布局,方便屏幕尺寸变化,视图自动移动和拉伸
2.1.2 备用布局
布局文件使用宽度、屏幕方向限定符(port 或 land)单独适配
Fragment界面组件
2.1.3 图片资源
图片采用.9图、矢量图
2.2 折叠屏手机适配
其主要经历了三个重要阶段。
2.2.1 Android官方折叠屏适配指南
2018年8月Google发布Android 9.0,首次支持折叠屏功能,并推出了 Android官方折叠屏适配指南。主要从以下几个方面进行适配:
- 应用连续性:处理配置变更
- 屏幕兼容性:resizeableActivity 与 maxAspectRatio
- 多窗口适配:多项恢复与专属资源访问
2.2.2 厂商补充适配方案
各厂商陆续发布自己的折叠屏手机,以及补充的适配方案。不同折叠屏手机上,拥有各自研发的一些独有功能,要想体验这些独特功能,就需要使用厂商的方案进行适配。代表有:三星、华为、Microsoft Surface Duo等
2.2.3 Google 2021 I/O 发布Jetpack WindowManager等库
2021年5月,Google I/O开发者大会推出专门用于折叠屏适配的库 Jetpack WindowManager 1.0,以及更新了已有的一些库如:SlidingPaneLayout、NavRail等,以便应用更便捷的适配折叠屏功能。
3. Android 官方折叠屏适配指南
主要参考 官方文档《为可折叠设备构建应用》
Android 10 (API 级别 29) ,Google就已经对折叠屏提供了“Screen Continuity(屏幕连续性)”的原生系统支持,并将这项技术称之为:Foldables。
所谓屏幕连续性,就是在可折叠设备上运行时,应用可以自动从一个屏幕转换到另一个屏幕,并且当前任务能在转换后继续无缝执行,系统会在转换时触发配置变更。
3.1 应用连续性:处理配置变更
在默认情况下,折叠屏折叠变化时(或应用从一个屏幕转到另一个屏幕),系统会销毁并重新创建整个 Activity。
但我们希望屏幕变化之后,程序能够以切换前的状态继续运行,不需要重启页面。我们可以给 Activity 添加configChanges配置:
<activity
android:configChanges="smallestScreenSize|screenSize|screenLayout" />
应用内监听变化,根据当前窗口大小,调整布局大小和位置:
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// 获取当前窗口配置信息,调整布局大小
}
如果需要重启 Activity才能完成适配的场景,可以通过onSaveInstanceState()与onRestoreInstanceState() 或 ViewModel对象来进行之前状态保存和后续的恢复。

3.2 屏幕兼容性
在Anroid 11版本中,对折叠屏的支持进行了增强性扩展,为开发者适配折叠屏应用提供了更多的接口。
3.2.1 应用大小可调:resizeableActivity
- 应用能够在多窗口模式下运行
- 应用的大小可动态调整
设置 resizeableActivity=true 即可实现此目的。
这可以为应用可能遇到的任何设备类型和环境(例如可折叠设备、桌面模式或自由窗口)提供最大兼容性。当系统编译设置target >= 24(Android 7.0) 不需要手动设置,系统默认为 true,支持多窗口和调节尺寸。
如果应用设置 resizeableActivity=false,则会告知平台其不支持多窗口模式。
但是系统还是可能会调整应用的大小或将其置于多窗口模式;要实现兼容性,便需要对应用中的所有组件(如应用的所有 Activity 等)应用同一配置。在某些情况下,重大变更(例如,显示屏尺寸更改)可能会重启进程,而不会更改配置。这时需要支持折叠屏连续性,需添加属性:
<meta-data android:name="android.supports_size_changes" android:value="true" />
例如,以下 Activity 设置了 resizableActivity=false 以及 maxAspectRatio。设备展开时,系统会将应用置于兼容模式,保以此来保持 Activity 配置,如大小和宽高比。

如果未设置 resizeableActivity,或将其设置为 true,系统会假定该应用完全支持多窗口并且可调整大小。
*注意:某些原始设备制造商 (OEM) 可能会在 Activity 的显示区域发生更改时,在屏幕上添加一个小型重启图标。为用户提供在新配置中重启 Activity 的机会。
3.2.2 屏幕宽高比
Android 10 (API 级别 29) 或更高版本 支持更多种宽高比。对于可折叠设备而言,设备类型可以是超长、超宽的屏幕(例如屏幕宽高比为 21:9 的折叠设备、 1:1 的屏幕)。
当resizableActivity=true时设置maxAspectRatio 无效;
而当resizeableActivity = false时,使用 maxAspectRatio、 minAspectRatio 指定最小或最大纵横比,在设置的纵横比限制范围内,折叠屏会自动对尺寸进行调节,超出限制的进入兼容模式(黑边)。
如要兼容尽可能多的设备,推荐纵横比设置范围:[1, 2.4],即1:1到12:5。google推荐说 应该尽量多针对以下屏幕宽高比测试自己的应用:
3.3 多窗口模式
大屏幕的好处之一是能够运行多个窗口。也就是分屏,现在的技术可支持三个或更多应用同时在一个屏幕上运行,并且相互之间还可以共享内容:

如果应用不能妥善支持多窗口模式,则可以设置 resizeableActivity=false。
3.3.1 多页面恢复
多窗口的行为变化历史:
- Android 7.0 支持分屏:左右/上下显示两个窗口
- Android 8.0 支持画中画,此时处于画中画的Activity虽处于前台,但处于 Paused状态
- Android 9.0 (API 28) 及以下:多窗口下,只有获得焦点的应用是处于Resumed状态,其它可见Activity扔处于Paused状态
- Android 10.0 (API 29) :多窗口模式时,所有Acttivity全部处于Resumed状态
在搭载 Android 9.0 及更低版本的设备上运行时,只有获得焦点的应用处于已恢复状态。任何其他可见 Activity 都处于已暂停状态。如果应用在处于暂停状态时关闭资源或停止内容,则可能会产生问题。
在 Android 10 中,对此做了优化,即当设备处于多窗口模式时,所有 Activity 都会保持已恢复状态。这称为多项恢复。
注意,如果顶部有透明 Activity,或者 Activity 不可成为焦点,则相应 Activity 可能会处于已暂停状态。
为解决Android 9.0及以下,只有获得焦点应用才处于Resume状态,其它可见Activity处于Paused状态问题。可添加下列属性,手动添加开启多项恢复。
<meta-data
android:name="android.allow_multiple_resumed_activities" android:value="true" />
3.3.2 专属资源访问
为了帮助支持多项恢复功能,系统提供了一个新的生命周期回调
Activity#onTopResumedActivityChanged()
当 Activity 获得或失去顶部恢复 Activity 位置时,系统会调用此方法。当 Activity 使用共享的单一资源(例如麦克风或摄像头)时。
protected void onTopResumedActivityChanged(boolean topResumed) {
if (topResumed) {
// Top resumed activity
// Can be a signal to re-acquire exclusive resources
} else {
// No longer the top resumed activity
}
}
当我们使用了独占资源时就要用到这个方法。什么叫独占资源?麦克风、摄像头就是,这类资源同一时间只能给一个 Activity 使用。以摄像头使用为例,在Android10上,官方建议使用 CameraManager.AvailabilityCallback#onCameraAccessPrioritiesChanged()监听摄像头是否可用。
当收到Activity#onTopResumedActivityChanged(isTopResumed)回调,且,
- isTopResumed = false 时,需要在此时判断是否释放独占资源,而不必在一失去焦点时就释放资源;
- isTopResumed = true 时 ,可以申请独占的摄像头资源,原持有摄像头资源的应用将收到 CameraDevice.StateCallback#onDisconnected() 回调。

应用收到 CameraDevice.StateCallback#onDisconnected() 回调后,对摄像头设备进行的后续调用将抛出 CameraAccessException。
3.3.3 多窗口数据拖拽
借助 Android 拖放框架,用户可以通过交互式拖放手势来移动数据。用户可以在应用中的 View 之间拖动文本、图片、对象(可以通过 URI 表示的任何内容),也可以使用多窗口模式在应用之间拖动这些内容。
该框架包括拖动事件类、拖动监听器,以及程序类和方法。
详情见 官方文档《拖放》
4. 各大厂商适配
折叠屏厂商发布了对应适配文档的主要有:三星、华为、微软等。详情看下方链接:
- 三星折叠屏适配指南 《三星折叠屏手机常见适配问题及建议》
- 华为折叠屏适配指南 《华为折叠屏应用开发指导》
- 微软折叠屏适配指南 《双屏布局库》、《Surface Duo 开发人员文档 》、《适用于 Surface Duo 的 Android 示例应用》
5. 使用 Jetpack WindowManager 进行折叠屏适配
Jetpack WindowManager 出现之前,折叠屏适配面临如下一些问题
折叠状态下应用体验较差:如半开状态、桌面模式等折叠姿态下,全屏画面扭曲。
无法正确识别折叠屏类型、可显示范围等。如微软的双屏折叠手机,中间部分无画面区域被铰链遮挡。
缺乏统一适配库支持,开发人员对于不同机型,适配比较繁琐,工作量较大。

- 为所有类型的可折叠设备提供统一 API,避免各设备单独适配。
- 可折叠设备的物理属性的信息:折叠屏类型、折叠状态、铰链方向、窗口显示范围等信息。
- 最初版本面向可折叠设备,将来扩展为支持更多的显示类型和窗口功能。
5.1 Jetpack WindowManager API 使用
Jetpack WindowManager API的目标:
- 为所有类型的可折叠设备提供统一 API,避免各设备单独适配。
- 可折叠设备的物理属性的信息:折叠屏类型、折叠状态、铰链方向、窗口显示范围等信息。
WindowManager API 包含了以下内容:
- WindowLayoutInfo:包含了窗口的显示特性,例如该窗口是否可折叠或包含铰链。
- FoldingFeature: 让您能够监听可折叠设备的折叠状态得以判断设备的姿态。
- WindowMetrics: 提供当前窗口或全部窗口的显示指标
使用WindowManager适配的主要原理:通过WindowManager获取折叠屏设备信息FoldingFeature信息,对于不同的折叠姿态,更新UI显示内容。

FoldingFeature用来描述屏幕的折叠状态或两个物理显示面板之间的铰链状态。主要的API如下:
1.getType():获取屏幕物理类型。
- 值:TYPE_FOLD。表示屏幕为无物理间隙的柔性屏幕中的折叠。市面上大部分常见的折叠屏,如Samsung Fold、华为 Mate X。
- 值:TYPE_HINGE。表示屏幕为带有铰链(不可显示区域)的两块屏幕,市面上目前比较少见,典型的指:Surface Duo
2.getState():折叠状态,主要有两种状态:
- STATE_FLAT(展开)
- STATE_HALF_OPENED(半开)。
3.getBounds():折叠功能边界的 Rect 实例,指示折叠屏当前可显示的屏幕可见范围。
4.getOrientation():折叠屏铰链防线:
- ORIENTATION_HORIZONTAL
- ORIENTATION_VERTICAL。
5.isSeparating():内容区域是否分割为2块。
- true:半开或双屏设备;
- false:计算是否有遮挡
6. 测试、可折叠模拟器
模拟器支持折叠设备。开发者可在折叠情形下测试应用:

创建并运行。

测试。
… End!