Jetpack Compose 使设计和构建应用的 UI 变得更加容易。Compose 通过以下方式将状态转换为 UI 元素:
- 元素组合
- 元素布局
- 元素绘制
本文档重点介绍元素的布局,解释了 Compose 提供的一些构建块,以帮助您布置 UI 元素。
Compose 中布局的目标
Jetpack Compose 布局系统实现有两个主要目标
可组合函数的基础知识
可组合函数是 Compose 的基本构建块。可组合函数是一个发出 Unit
的函数,它描述了 UI 的一部分。该函数接收一些输入并生成屏幕上显示的内容。有关可组合项的更多信息,请查看Compose 心智模型 文档。
可组合函数可能会发出多个 UI 元素。但是,如果您不提供有关如何排列它们的指导,Compose 可能会以您不喜欢的方式排列这些元素。例如,此代码生成两个文本元素
@Composable fun ArtistCard() { Text("Alfred Sisley") Text("3 minutes ago") }
在没有关于您希望如何排列它们的指导的情况下,Compose 将文本元素堆叠在一起,使它们难以阅读
Compose 提供了一组现成的布局来帮助您排列 UI 元素,并使定义您自己的更专业的布局变得容易。
标准布局组件
在许多情况下,您只需使用Compose 的标准布局元素。
使用Column
在屏幕上垂直放置项目。
@Composable fun ArtistCardColumn() { Column { Text("Alfred Sisley") Text("3 minutes ago") } }
类似地,使用Row
在屏幕上水平放置项目。Column
和 Row
都支持配置其包含元素的对齐方式。
@Composable fun ArtistCardRow(artist: Artist) { Row(verticalAlignment = Alignment.CenterVertically) { Image(bitmap = artist.image, contentDescription = "Artist image") Column { Text(artist.name) Text(artist.lastSeenOnline) } } }
使用Box
将元素放置在另一个元素的顶部。Box
还支持配置其包含元素的特定对齐方式。
@Composable fun ArtistAvatar(artist: Artist) { Box { Image(bitmap = artist.image, contentDescription = "Artist image") Icon(Icons.Filled.Check, contentDescription = "Check mark") } }
通常,这些构建块就是您所需要的一切。您可以编写自己的可组合函数,将这些布局组合成更复杂的布局以适合您的应用。
要在 Row
中设置子级的位置,请设置 horizontalArrangement
和 verticalAlignment
参数。对于 Column
,请设置 verticalArrangement
和 horizontalAlignment
参数
@Composable fun ArtistCardArrangement(artist: Artist) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.End ) { Image(bitmap = artist.image, contentDescription = "Artist image") Column { /*...*/ } } }
布局模型
在布局模型中,UI 树在一遍扫描中布局。首先要求每个节点测量自身,然后递归地测量任何子节点,将大小约束向下传递到树中的子节点。然后,对叶节点进行大小调整和放置,并将已解析的大小和放置指令向上传递回树。
简而言之,父节点在子节点之前进行测量,但在子节点之后进行大小调整和放置。
考虑以下 SearchResult
函数。
@Composable fun SearchResult() { Row { Image( // ... ) Column { Text( // ... ) Text( // ... ) } } }
此函数生成以下 UI 树。
SearchResult
Row
Image
Column
Text
Text
在 SearchResult
示例中,UI 树布局遵循以下顺序
- 要求根节点
Row
进行测量。 - 根节点
Row
要求其第一个子节点Image
进行测量。 Image
是叶节点(即它没有子节点),因此它报告大小并返回放置指令。- 根节点
Row
要求其第二个子节点Column
进行测量。 Column
节点要求其第一个Text
子节点进行测量。- 第一个
Text
节点是叶节点,因此它报告大小并返回放置指令。 Column
节点要求其第二个Text
子节点进行测量。- 第二个
Text
节点是叶节点,因此它报告大小并返回放置指令。 - 现在
Column
节点已测量、调整大小并放置了其子节点,它可以确定自身的大小和位置。 - 现在根节点
Row
已测量、调整大小并放置了其子节点,它可以确定自身的大小和位置。
性能
Compose 通过仅测量子节点一次来实现高性能。单遍测量有利于性能,使 Compose 能够高效地处理深层 UI 树。如果一个元素对其子节点进行两次测量,并且该子节点对其每个子节点进行两次测量,依此类推,则尝试布局整个 UI 将需要执行大量工作,这使得难以保持应用的性能。
如果您的布局出于某种原因需要多次测量,Compose 提供了一个特殊的系统,即内在测量。您可以在Compose 布局中的内在测量中阅读有关此功能的更多信息。
由于测量和放置是布局传递的不同子阶段,因此仅影响项目放置(而不是测量)的任何更改都可以单独执行。
在布局中使用修饰符
如Compose 修饰符中所述,您可以使用修饰符来装饰或增强可组合项。修饰符对于自定义布局至关重要。例如,这里我们链接了几个修饰符来自定义 ArtistCard
@Composable fun ArtistCardModifiers( artist: Artist, onClick: () -> Unit ) { val padding = 16.dp Column( Modifier .clickable(onClick = onClick) .padding(padding) .fillMaxWidth() ) { Row(verticalAlignment = Alignment.CenterVertically) { /*...*/ } Spacer(Modifier.size(padding)) Card( elevation = CardDefaults.cardElevation(defaultElevation = 4.dp), ) { /*...*/ } } }
在上面的代码中,请注意一起使用的不同修饰符函数。
clickable
使可组合项对用户输入做出反应并显示波纹。padding
在元素周围留出空间。fillMaxWidth
使可组合项填充其父级给定的最大宽度。size()
指定元素的首选宽度和高度。
可滚动布局
在Compose 手势文档中了解有关可滚动布局的更多信息。
对于列表和惰性列表,请查看Compose 列表文档。
响应式布局
设计布局时应考虑不同的屏幕方向和外形尺寸。Compose 提供了一些开箱即用的机制,以方便您的可组合布局适应各种屏幕配置。
约束
为了了解来自父级的约束并相应地设计布局,您可以使用 BoxWithConstraints
。可以在内容 lambda 的作用域中找到测量约束。您可以使用这些测量约束为不同的屏幕配置组合不同的布局
@Composable fun WithConstraintsComposable() { BoxWithConstraints { Text("My minHeight is $minHeight while my maxWidth is $maxWidth") } }
基于插槽的布局
Compose 提供了大量基于 Material Design 的可组合函数,通过 androidx.compose.material:material
依赖项(在 Android Studio 中创建 Compose 项目时包含)简化 UI 构建。它提供了诸如 Drawer
、FloatingActionButton
和 TopAppBar
等元素。
Material 组件大量使用插槽 API,这是 Compose 引入的一种模式,用于在可组合函数之上添加一层自定义功能。这种方法使组件更加灵活,因为它们接受一个子元素来配置自身,而不是必须公开子元素的每个配置参数。插槽在 UI 中留出一个空位,供开发者根据需要填充。例如,以下是在 TopAppBar
中可以自定义的插槽。
可组合函数通常接受一个 content
可组合 lambda(content: @Composable () -> Unit
)。插槽 API 为特定用途公开了多个 content
参数。例如,TopAppBar
允许您提供 title
、navigationIcon
和 actions
的内容。
例如,Scaffold
允许您使用基本的 Material Design 布局结构实现 UI。Scaffold
为最常见的顶级 Material 组件提供插槽,例如 TopAppBar
、BottomAppBar
、FloatingActionButton
和 Drawer
。通过使用 Scaffold
,可以轻松确保这些组件正确放置并协同工作。
@Composable fun HomeScreen(/*...*/) { ModalNavigationDrawer(drawerContent = { /* ... */ }) { Scaffold( topBar = { /*...*/ } ) { contentPadding -> // ... } } }
推荐内容
- 注意:当 JavaScript 关闭时,会显示链接文本。
- Compose 修饰符
- Jetpack Compose 的 Kotlin
- Material 组件和布局