Android 17 大屏适配变化解读

0 阅读7分钟

Android 17 这次有个点很值得单独写,不是新控件,也不是某个单独 API,而是大屏适配这件事开始进一步收紧了。

Google 在 2026 年 2 月 13 日 发布的官方文章里说得很直接:当应用 target API level 37,在大屏设备上,开发者将不能再继续依赖之前那套“锁方向、限制比例、不让拉伸”的做法来维持旧布局。简单说,Android 17 不是突然加了一条新规则,而是把 Android 16 里那套过渡期政策继续往前推了一步,把 opt-out 去掉了。

这件事为什么值得写?因为它影响的不是某个细分领域,而是大量还停留在“手机竖屏思维”里的应用。以前很多项目默认把页面当成一块固定尺寸的长方形来设计,竖屏、全屏、比例稳定,开发起来很省心。一旦到了平板、折叠屏、桌面窗口、多任务分屏,这套假设就会开始出问题。Android 17 的意思其实很明确:这种问题不能再一直往后拖了。

官方文档里提到,在大屏设备上,也就是 smallest width >= 600dp 的场景里,下面这些限制会被忽略:

  • screenOrientation
  • setRequestedOrientation()
  • resizeableActivity
  • minAspectRatio
  • maxAspectRatio

如果以前你的项目靠这些配置维持“只能竖屏”“只能某个比例”“不能自由拉伸”,那 target 到 API 37 以后,这些手段在大屏上就不再可靠了。这里要注意两个边界。第一,这些变化主要针对 sw >= 600dp 的设备,也就是平板、大屏折叠设备展开态、桌面窗口这类场景,不是说所有手机都会立刻一样。第二,官方文档也明确写了,games 是例外之一,这个判断基于 android:appCategory

从开发角度看,这次变化最真实的影响,不是“方向锁没了”这么简单,而是你的布局假设被系统拆掉了。以前页面可能默认永远是竖着的,输入框永远在上半屏,按钮永远在底部可见,图片区域永远按某个比例展示。现在一旦窗口能变宽、变矮、旋转、折叠、拉伸,这些前提都不稳了。

官方给出的几个高频问题其实很典型。第一个是 UI 被拉得很难看。很多页面在手机上写 fillMaxWidth() 没问题,但一到平板横屏,按钮、卡片、输入框会被直接撑得很空。第二个问题是内容不可达。比如底部的 SaveLoginNext 在高度变矮后可能直接跑到屏幕外面,如果容器又不能滚动,用户就卡住了。第三个问题是相机预览错位,尤其是依赖旧的方向假设来处理 Camera2 预览时,容易出现拉伸、旋转错误、裁切不对。第四个问题是状态丢失,因为窗口变化会更频繁,如果页面还是老思路,旋转一下、折叠一下、窗口拖一下,表单内容和滚动位置就丢了。

所以这类文章如果要写得有价值,重点不是重复“Google 要你适配大屏”,而是把工程上的改法讲出来。

先看一个最容易中招的写法。很多页面在 Compose 里会习惯这么铺:

Column(
    modifier = Modifier
        .fillMaxSize()
        .padding(16.dp)
) {
    OutlinedTextField(
        value = username,
        onValueChange = { username = it },
        modifier = Modifier.fillMaxWidth()
    )

    Spacer(modifier = Modifier.height(12.dp))

    Button(
        onClick = ::submit,
        modifier = Modifier.fillMaxWidth()
    ) {
        Text("Login")
    }
}

这段代码在手机竖屏通常没什么问题,但到了平板或者可调整窗口里,内容可能会被横向拉得很散。官方给出的思路是,不要再默认组件应该无限制铺满整个宽度,而是给内容区域一个最大宽度。

改法可以像这样:

Box(
    modifier = Modifier.fillMaxSize(),
    contentAlignment = Alignment.Center
) {
    Column(
        modifier = Modifier
            .widthIn(max = 420.dp)
            .fillMaxWidth()
            .padding(16.dp)
    ) {
        OutlinedTextField(
            value = username,
            onValueChange = { username = it },
            modifier = Modifier.fillMaxWidth()
        )

        Spacer(modifier = Modifier.height(12.dp))

        Button(
            onClick = ::submit,
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("Login")
        }
    }
}

这个改动不复杂,但效果很直接。它不是强行让你的页面变成平板专版,而是先把最容易失控的那部分收住。

第二个要补的是滚动能力。很多表单页面在手机上高度够用,所以一开始就没做滚动容器。可一旦应用进了横屏窗口或者折叠态窗口,底部按钮就可能直接掉出可视区域。这个问题的修法也不花哨,先保证内容能滚起来。

Column(
    modifier = Modifier
        .fillMaxSize()
        .verticalScroll(rememberScrollState())
        .padding(16.dp)
) {
    // form content
}

第三个是不要再把“设备类型”当布局判断的核心。官方现在更推的思路是看窗口,而不是猜你运行在哪个设备上。因为同一台设备,窗口都可能变化很多次。这个时候 WindowSizeClass 比“这是平板还是手机”这种判断更可靠。

比如一个很常见的写法就是根据窗口宽度切单栏和双栏:

@Composable
fun MailScreen(windowSizeClass: WindowSizeClass) {
    when (windowSizeClass.widthSizeClass) {
        WindowWidthSizeClass.Compact -> MailListOnly()
        WindowWidthSizeClass.Medium -> MailListAndDetail()
        WindowWidthSizeClass.Expanded -> MailListAndDetail()
        else -> MailListOnly()
    }
}

这类代码的思路,比“平板就走 A 布局,手机就走 B 布局”要稳很多,因为它是围绕当前窗口空间在判断。

再往下就是状态保存问题。Android 17 这类变化的麻烦之处在于,窗口变化会更频繁。旋转、分屏、拖拽窗口、折叠展开,都会让页面不断经历配置变化。如果页面状态没保存好,用户很容易一通操作之后发现内容没了。

Compose 下最基本的一步,是先把能保存的 UI 状态用 rememberSaveable 收起来:

@Composable
fun ProfileEditor() {
    var nickname by rememberSaveable { mutableStateOf("") }
    var bio by rememberSaveable { mutableStateOf("") }

    Column(modifier = Modifier.padding(16.dp)) {
        OutlinedTextField(
            value = nickname,
            onValueChange = { nickname = it },
            label = { Text("Nickname") }
        )

        Spacer(modifier = Modifier.height(12.dp))

        OutlinedTextField(
            value = bio,
            onValueChange = { bio = it },
            label = { Text("Bio") }
        )
    }
}

如果是业务状态,就不要只指望 Composable 自己记,还是要放到 ViewModel 里。因为大屏适配说到底不是一个“加几个 modifier”就结束的事,很多时候它逼着你把页面状态、布局结构、组件宽度、滚动能力一起重新整理。

相机类应用会更需要单独注意。官方在这次文章里专门把相机预览拿出来讲,是因为这块特别容易出问题。以前很多相机页面默认拿设备方向去推预览方向,或者把屏幕尺寸直接拿来算预览比例,这种做法在多窗口、大屏和桌面模式下都容易翻车。Google 更推荐的路线是优先用 CameraX PreviewView,或者至少用更现代的 CameraViewfinder,不要继续把方向换算和比例换算全手搓。

如果你写这篇文章,我觉得可以把结论写得更直接一点:Android 17 真正的信号不是“平板又重要了”,而是 Android 官方已经把“自适应布局”从最佳实践,往接近基础能力的方向推了。以前可以先不做,现在不太行了。尤其是当你后面要 target API 37,并且 Google Play 未来会要求新应用和更新应用对齐这个 target 时,这件事就不是可选项。

如果想给读者一个很实用的检查清单,可以就留下面这几个:

  • 不要再假设页面永远是竖屏
  • 不要默认 fillMaxWidth() 到处都安全
  • 表单和长页面先补上滚动
  • 布局判断尽量基于窗口,不要基于设备名词
  • 重要状态放进 rememberSaveableViewModel
  • 相机预览优先用 CameraX PreviewView

写到最后,这篇文章最适合落在一句很朴素的话上:Android 17 不是让你“专门去做平板版应用”,而是逼着你承认,今天的 Android 应用本来就运行在各种尺寸和形态的窗口里。页面如果还是按单一手机竖屏思维去写,后面只会越来越被动。