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 关闭时,将显示链接文本
- 组合修饰符
- Jetpack Compose 的 Kotlin
- Material Components 和布局