对于 Android 开发者而言,在过去声明一个 Activity 时,大多第一件事就是添加一个 android:screenOrientation="portrait"
,而其实自 targetSdkVersion ≥ 31
(Android 12),在 2020 年的 Android Studio 3.6 就开始有相关警告:
这是因为从 Android 12 开始,某些可折叠设备会无视用户配置的 screenOrientation ,而是强制 Activity 采用 Letterboxing 模式:
关于这个我们在过去的 《Android 折叠屏适配详解》 聊过,是否进入 Letterboxing 模式和 TargetSDK 版本、 App 配置和屏幕分辨率都有关系,并且不同 OS 版本上 Letterboxing 模式的呈现方式也可能有所不同。
而从 Android 16 开始,Google 将逐步淘汰用于限制应用屏幕方向和大小调整的清单属性及运行时 API ,而最初生效的设备类型为 "大屏"场景,即显示区域的最小宽度大于或等于 600dp 的情况(sw >= 600dp) ,也就是:
-
大屏可折叠设备的内屏
-
平板电脑,包括桌面窗口模式
-
桌面环境,包括 Chromebook
具体为 TargetSDK >= 36 之后,在 sw >= 600dp 时,以下配置和 API 将被忽略:
基于
android:appCategory
标志的游戏暂时可不受这些变更的影响
也就是,大屏场景下,之前 App 在设置了 screenOrientation
的情况下,App 会是 letterboxed 状态 ,而 Android 16 开始会直接拉成充满:
当然,不升级 targetSDK 就不用适配的想法现在是行不通的,因为现在上架都会开始要求你升级 targetSDK ,另外:
- Android 16 (2025):在 API 36 下可以配置不适配
- 2026 年的 Android 版本:API 37 开始,开发者必须适配
比如在 API 36 时,如果你还是想「摆烂」,那么可以通过配置 PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY
来继续「拖延」适配:
<activity ...>
<property android:name="android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY" android:value="true" />
...
</activity>
<application ...>
<property android:name="android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY" android:value="true" />
</application>
当然,在 API 37 的时候,开发者将无法选择退出,而针对 Google Play 上架场景, 2026 年 8 月前应用需以 API 36 为目标,在 2027 年 8 月前应用需以 API 级别 37 为目标。
另外,在 API 36 target 上,
R.attr#windowOptOutEdgeToEdgeEnforcement
也会被弃用,也就是 App 无法再强制退出 edge-to-edge 模式,如果不做适配,横屏拉伸+ edge-to-edge 的效果看起来应该会很酸爽。
那么,开发者应该如何适配?最简单就是使用响应式布局如 Compose、Flutter 等。
如果是原生端肯定首选 Compose ,使用 material3-window-size-class
库,然后利用 calculateWindowSizeClass()
计算当前窗口的 WindowSizeClass
,从而改变 UI 的布局:
import androidx.activity.compose.setContent
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
class MyActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// Calculate the window size class for the activity's current window. If the window
// size changes, for example when the device is rotated, the value returned by
// calculateSizeClass will also change.
val windowSizeClass = calculateWindowSizeClass(this)
// Perform logic on the window size class to decide whether to use a nav rail.
val useNavRail = windowSizeClass.widthSizeClass > WindowWidthSizeClass.Compact
// MyScreen knows nothing about window size classes, and performs logic based on a
// Boolean flag.
MyScreen(useNavRail = useNavRail)
}
}
}
另外还可以通过 com.google.accompanist:accompanist-adaptive
的 TwoPane 进行适配,TwoPane
提供了两个固定的槽位,两个槽位的默认位置由 TwoPaneStrategy
驱动,它可以决定将两个槽位水平或垂直排列,并可配置它们之间的间隔:
不同场景 Compose 还可以使用 FlowLayout
适配折叠变化 ,FlowLayout
包含 FlowRow
和 FlowColumn
,当一行(或一列)放不下里边的内容时,会自动换行,这在折叠屏展开和收缩场景也非常实用。
而传统 View 场景,可以使用 Activity Embedding ,理论上 Activity Embedding 不需要代码重构,可以通过创建 XML 配置文件或进行 Jetpack WindowManager API 调用来确定 App 如何显示其 Activity(并排或堆叠) :
Jetpack WindowManager 管理和配置 Activity Embedding 其实相当灵活,另外 SlidingPaneLayout
也是一种兼容方式:
当然,你也可以直接使用 Jetpack WindowManager 的 FoldingFeature 等相关信息去自定义适配。
而现在 Android Studio 的模拟器也已经提供了相应场景支持,对于大多数没有折叠屏设备的开发者来说,模拟器适配是最合适不过的场景:
另外,针对 Flutter 场景,responsive_sizer、flutter_flexible_ui 和 ResponsiveFramework 等框架,在大屏幕设备下提供不错的动态设备支持:
最后,其实不难看出,在前面官方提及的 「桌面窗口模式」等场景,也看出来该操作是在为 Android PC 铺路,对于 Android PC,在集齐了「Linux 终端控制台支持」、「桌面模式」、「外部显示器支持」、「窗口多任务」,「最小化」,「多实例支持」、「Desktop View」、「外部显示器排列和切换」等场景后,在 App 端也终于开始迎来强制性的 UI 适配需求,看起来 Android 团队也重新开始重视 PC 场景,另外还有 Android XR 中的窗口场景,所以针对 Android 的大屏需求,未来只会会越来越多。
那么,接下来你会开始适配,还是选择能拖就拖?
参考链接: