支持不同的屏幕尺寸,让最广泛的用户能够访问您的应用 各种各样的设备,拥有最多的用户。
为了支持尽可能多的屏幕尺寸,在设计应用时 响应式和自适应。响应式/自适应布局可为用户提供经过优化的体验 无论屏幕尺寸如何,都能让应用适应手机的需求, 平板电脑、可折叠设备、ChromeOS 设备、纵向和横向模式,以及 可调整大小的配置,例如多窗口模式 模式。
响应式/自适应布局会根据可用的显示空间而变化。变更 从填满空间的小布局调整(自适应设计)到 将一种布局完全替换为另一种布局,以便您的应用 不同的显示大小(自适应设计)。
作为声明式界面工具包,Jetpack Compose 非常适合用于 实现可动态变化以以不同方式呈现内容的布局 以各种不同的展示广告尺寸显示
以显式方式对屏幕级可组合项布局进行大幅调整
使用 Compose 布置整个应用时,应用级和屏幕级可组合项会占用分配给应用进行渲染的所有空间。在应用设计的这个层面上,可能有必要更改屏幕的整体布局以充分利用屏幕空间。
避免根据物理硬件值来确定布局。它可能会 很容易根据固定的有形价值(设备是否是 平板电脑?物理屏幕是否有特定的宽高比?), 对确定界面的工作空间可能没什么用
![一张显示多种不同设备外形规格(包括手机、可折叠设备、平板电脑和笔记本电脑)的示意图。](https://developer.android.google.cn/static/develop/ui/compose/images/layouts/adaptive/adaptive-many-screens.png?authuser=9&hl=nb)
在平板电脑上,应用可能会在多窗口模式下运行,也就是说,应用 可能正与另一个应用分屏显示在 ChromeOS 中,应用可能位于 可调整大小的窗口。甚至可能会有多个物理屏幕,例如可折叠设备。在所有这些情况下,物理屏幕尺寸都不是 决定如何显示内容具有重要意义。
相反,您应该根据分配给应用的实际屏幕区域来决定如何显示,例如 Jetpack WindowManager 库提供的当前窗口指标。如需了解如何在 Compose 应用中使用 WindowManager,请查看 JetNews 示例。
遵循此方法可提高应用的灵活性,因为它将在以上所有场景中都能正常运行。让布局适应屏幕空间 也减少了向支持团队的特殊处理 ChromeOS 等平台,以及平板电脑和可折叠设备等外形规格的设备。
观察应用的可用空间后,这非常有用 将原始尺寸转换为有意义的尺寸类,如 Window 大小类别。这个 将尺寸分组到标准尺寸组中,也就是 力求在简单性与灵活性之间取得平衡,以便针对最独特应用优化应用 案例这些大小类表示应用的整个窗口,因此请使用这些 类,用于决定影响整体屏幕布局的布局决策。您可以 将这些大小类作为状态向下传递,或者您也可以执行其他逻辑 创建派生状态以传递给嵌套可组合项。
@OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable fun MyApp( windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo().windowSizeClass ) { // Perform logic on the size class to decide whether to show the top app bar. val showTopAppBar = windowSizeClass.windowHeightSizeClass != WindowHeightSizeClass.COMPACT // MyScreen knows nothing about window sizes, and performs logic based on a Boolean flag. MyScreen( showTopAppBar = showTopAppBar, /* ... */ ) }
这种分层方法将屏幕尺寸逻辑限制在单个位置, 而是分散在应用中的许多需要保持同步的地方 这个单一位置会生成状态,该状态可显式传递到 像处理任何其他应用状态一样。显式传递状态可以简化个别可组合项,因为它们只是一些接受 Size 类或指定配置以及其他数据的普通可组合函数。
灵活的嵌套可组合项可以重复使用
将可组合项放置在各种不同的位置,可以提高它们的可重用性。如果可组合项假定将始终放置在某个 具有特定尺寸的位置,那么就很难在 或可用空间不同。这也 这意味着,可重复使用的单个可组合项应避免隐式 在“global”上尺寸信息。
请考虑以下示例:假设一个嵌套可组合项实现 列表-详情 布局, 可以展示一个窗格或并排显示两个窗格
![并排显示两个窗格的应用屏幕截图。](https://developer.android.google.cn/static/develop/ui/compose/images/layouts/adaptive/adaptive-list-detail.png?authuser=9&hl=nb)
我们希望将此决策纳入应用的整体布局中,因此我们从屏幕级可组合项传递此决策,如上方所示:
@Composable fun AdaptivePane( showOnePane: Boolean, /* ... */ ) { if (showOnePane) { OnePane(/* ... */) } else { TwoPane(/* ... */) } }
如果我们想要某个可组合项根据情况独立更改其布局,该怎么办? 可用空间?例如,某张卡片想要显示更多详细信息 如果空间允许的话。我们希望根据一些可用大小执行一些逻辑, 具体是哪种尺寸?
![两张不同卡片的示例。](https://developer.android.google.cn/static/develop/ui/compose/images/layouts/adaptive/adaptive-card-title-desc.png?authuser=9&hl=nb)
如上所示,我们应该避免尝试使用设备的实际 屏幕。这对于多个屏幕并不一定都准确 。
由于该可组合项不是屏幕级可组合项,为了最大限度地提高可重用性,我们也不应该直接使用当前的窗口指标。如果组件在放置时有内边距(例如边衬区),或者有导航栏或应用栏等组件,那么系统为可组合项分配的空间量与应用可使用的总空间量可能会有很大的差距。
因此,我们应使用可组合项实际提供给的宽度 呈现自身我们可以通过以下两种方法获取该宽度:
如果您想更改内容的位置或方式显示,可以使用 修饰符集合或自定义布局 让布局具备自适应能力这很简单,只需让某个子项填充所有可用空间,或者为子级布置多个列(如果有足够的空间)。
如果您想更改显示的内容,可以使用 BoxWithConstraints
作为更强大的替代方案。此可组合项可提供
限制条件
您可以用它来根据所放置的空间调用不同的可组合项
可用。不过,这也会带来一些费用,因为 BoxWithConstraints
会推迟
组合到布局阶段,此时这些约束条件已知,会导致
在布局期间需要执行的工作更多。
@Composable fun Card(/* ... */) { BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(/* ... */) Title(/* ... */) } } else { Row { Column { Title(/* ... */) Description(/* ... */) } Image(/* ... */) } } } }
确保在不同尺寸下所有数据都可以呈现
如果可以利用额外的屏幕空间,您在大屏幕上向用户显示的内容可以比在小屏幕上多。当实现具有此行为的可组合项时,您可能想要提高效率,根据当前屏幕尺寸来加载数据。
不过,这违背了单向数据流的原则, 数据可以提升并提供给可组合项,以便进行适当渲染。够了 应向可组合项提供数据,以便可组合项始终具有 即使数据的某些部分可能不 始终使用。
@Composable fun Card( imageUrl: String, title: String, description: String ) { BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(imageUrl) Title(title) } } else { Row { Column { Title(title) Description(description) } Image(imageUrl) } } } }
请注意,就 Card
示例而言,我们始终都会将 description
传递给 Card
。尽管 description
仅在宽度允许显示它时才会用到,但无论有多少可用宽度,Card
都始终需要 description。
始终传递数据会降低自适应布局的有状态性,从而简化这些布局。 并避免在不同尺寸之间切换时触发副作用(可能会出现 (因窗口大小调整、屏幕方向变化或设备折叠和展开而产生的)。
这一原则还可以在布局发生变化时保留状态。通过提升
信息可能并非在所有尺寸下都会使用,我们可以保留用户的状态,
随着布局尺寸的变化而变化例如,我们可以提升 showMore
布尔标志
以便在调整大小导致布局切换时保留用户的状态
隐藏和显示说明之间:
@Composable fun Card( imageUrl: String, title: String, description: String ) { var showMore by remember { mutableStateOf(false) } BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(imageUrl) Title(title) } } else { Row { Column { Title(title) Description( description = description, showMore = showMore, onShowMoreToggled = { newValue -> showMore = newValue } ) } Image(imageUrl) } } } }
了解详情
如需详细了解 Compose 中的自定义布局,请参阅下面列出的其他资源。
示例应用
- 大屏幕规范布局 是一个存储库,其中包含久经考验的设计模式, 大屏设备上的体验
- JetNews 展示了如何设计一款可调整界面以充分利用可用空间的应用
- 回复 是一个支持移动设备、平板电脑和可折叠设备的自适应示例
- Now in Android 是 使用自适应布局来支持不同屏幕尺寸的应用
视频
为您推荐
- 注意:当 JavaScript 处于关闭状态时,系统会显示链接文字
- 将组件映射到现有代码
- Compose 布局基础知识
- Jetpack Compose 的阶段