Jetpack Compose 可让您更轻松地设计和构建应用的界面。本文档介绍了 Compose 为了帮助您布置界面元素而提供的一些构建块,并向您展示了如何在需要时构建更专业的布局。
Compose 中布局的目标
布局系统的 Jetpack Compose 实现有两个主要目标:一是实现高性能,二是让开发者能够轻松编写自定义布局。在 Compose 中,通过避免多次测量布局子级可实现高性能。如果需要进行多次测量,Compose 具有一个特殊系统,即固有特性测量。如需详细了解此功能,请参阅固有特性测量部分。
可组合函数的基础知识
可组合函数是 Compose 的基本构建块。可组合函数是一种发出 Unit
的函数,用于描述界面中的某一部分。该函数接受一些输入并生成屏幕上显示的内容。如需详细了解可组合项,请参阅 Compose 构思模型文档。
一个可组合函数可能会发出多个界面元素。不过,如果您未提供有关如何排列这些元素的指导,Compose 可能会以您不喜欢的方式排列它们。例如,以下代码会生成两个文本元素:
@Composable
fun ArtistCard() {
Text("Alfred Sisley")
Text("3 minutes ago")
}
如果您未提供有关如何排列这两个文本元素的指导,Compose 会将它们堆叠在一起,使其无法阅读:
Compose 提供了一系列现成可用的布局来帮助您排列界面元素,并且可让您轻松地定义您自己的更专业的布局。
标准布局组件
在许多情况下,您只需使用 Compose 的标准布局元素即可。
使用 Column
可将多个项垂直地放置在屏幕上。
@Composable
fun ArtistCard() {
Column {
Text("Alfred Sisley")
Text("3 minutes ago")
}
}
同样,使用 Row
可将多个项水平地放置在屏幕上。Column
和 Row
都支持配置它们所含元素的对齐方式。
@Composable
fun ArtistCard(artist: Artist) {
Row(verticalAlignment = Alignment.CenterVertically) {
Image(/*...*/)
Column {
Text(artist.name)
Text(artist.lastSeenOnline)
}
}
}
使用 Box
可将一个元素放在另一个元素上。
通常,您只需要这些构建块。您可以自行编写可组合函数,将这些布局组合成更精美的布局,以适合您的应用。
如需在 Row
中设置子项的位置,请设置 horizontalArrangement
和 verticalAlignment
参数。对于 Column
,请设置 verticalArrangement
和 horizontalAlignment
参数:
@Composable
fun ArtistCard(artist: Artist) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
Image(/*...*/)
Column { /*...*/ }
}
}
修饰符
借助修饰符,您可以修饰或扩充可组合项。您可以使用修饰符来执行以下操作:
- 更改可组合项的大小、布局、行为和外观
- 添加信息,如无障碍标签
- 处理用户输入
- 添加高级互动,如使元素可点击、可滚动、可拖动或可缩放
修饰符是标准的 Kotlin 对象。您可以通过调用某个 Modifier
类函数来创建修饰符。您可以将以下函数连在一起以将其组合起来:
@Composable
fun ArtistCard(
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 = 4.dp) { /*...*/ }
}
}
请注意,在上面的代码中,结合使用了不同的修饰符函数。
clickable
使可组合项响应用户输入,并显示涟漪。padding
在元素周围留出空间。fillMaxWidth
使可组合项填充其父项为它提供的最大宽度。size()
指定元素的首选宽度和高度。
修饰符顺序很重要
修饰符函数的顺序非常重要。由于每个函数都会对上一个函数返回的 Modifier
进行更改,因此顺序会影响最终结果。让我们来看看这方面的一个示例:
@Composable
fun ArtistCard(/*...*/) {
val padding = 16.dp
Column(
Modifier
.clickable(onClick = onClick)
.padding(padding)
.fillMaxWidth()
) {
// rest of the implementation
}
}
在上面的代码中,整个区域(包括周围的内边距)都是可点击的,因为 padding
修饰符应用在 clickable
修饰符后面。如果修饰符顺序颠倒,由 padding
添加的空间就不会响应用户输入:
@Composable
fun ArtistCard(/*...*/) {
val padding = 16.dp
Column(
Modifier
.padding(padding)
.clickable(onClick = onClick)
.fillMaxWidth()
) {
// rest of the implementation
}
}
内置修饰符
Jetpack Compose 提供了一个内置修饰符列表,可帮助您修饰或扩充可组合项。已引入 padding
、clickable
和 fillMaxWidth
等修饰符。下面列出了其他常用修饰符:
size
默认情况下,Compose 中提供的布局会封装其子项。但是,您可以使用 size
修饰符设置尺寸:
@Composable
fun ArtistCard(/*...*/) {
Row(
modifier = Modifier.size(width = 400.dp, height = 100.dp)
) {
Image(/*...*/)
Column { /*...*/ }
}
}
请注意,如果指定的尺寸不符合来自布局父项的约束条件,则可能不会采用该尺寸。如果您希望可组合项的尺寸固定不变,而不考虑传入的约束条件,请使用 requiredSize
修饰符:
@Composable
fun ArtistCard(/*...*/) {
Row(
modifier = Modifier.size(width = 400.dp, height = 100.dp)
) {
Image(
/*...*/
modifier = Modifier.requiredSize(150.dp)
)
Column { /*...*/ }
}
}
在此示例中,即使父项的 height
设置为 100.dp
,Image
的高度还是 150.dp
,因为 requiredSize
修饰符优先级较高。
如果您希望子布局填充父项允许的所有可用高度,请添加 fillMaxHeight
修饰符(Compose 还提供了 fillMaxSize
和 fillMaxWidth
):
@Composable
fun ArtistCard(/*...*/) {
Row(
modifier = Modifier.size(width = 400.dp, height = 100.dp)
) {
Image(
/*...*/
modifier = Modifier.fillMaxHeight()
)
Column { /*...*/ }
}
}
如需在文本基线上方添加内边距,以实现从布局顶部到基线保持特定距离,请使用 paddingFromBaseline
修饰符:
@Composable
fun ArtistCard(artist: Artist) {
Row(/*...*/) {
Column {
Text(
text = artist.name,
modifier = Modifier.paddingFromBaseline(top = 50.dp)
)
Text(artist.lastSeenOnline)
}
}
}
偏移量
如需相对于原始位置放置布局,请添加 offset
修饰符,并设置在 x 轴和 y 轴的偏移量。偏移量可以是正数,也可以是非正数。padding
和 offset
之间的区别在于,向可组合项添加 offset
不会改变其测量结果:
@Composable
fun ArtistCard(artist: Artist) {
Row(/*...*/) {
Column {
Text(artist.name)
Text(
text = artist.lastSeenOnline,
modifier = Modifier.offset(x = 4.dp)
)
}
}
}
offset
修饰符根据布局方向水平应用。在从左到右的上下文中,如果 offset
为正值,会将元素向右移,而在从右到左的上下文中,则会将元素向左移。如果您需要设置偏移量,而不考虑布局方向,请参阅 absoluteOffset
修饰符,其中,正偏移值始终将元素向右移。
Compose 中的类型安全
在 Compose 中,有些修饰符仅适用于某些可组合项的子项。例如,如果您希望使某个子项与父项 Box
同样大,而不影响 Box
尺寸,请使用 matchParentSize
修饰符。
Compose 通过自定义作用域强制实施此类型安全机制。例如,matchParentSize
仅在 BoxScope
中可用。因此,仅当在 Box
中使用子项时,才能使用此修饰符。
限定作用域的修饰符会将父项应知晓的关于子项的一些信息告知父项。这些修饰符通常称为“父项数据修饰符”。它们的内部构件与通用修饰符不同,但从使用角度来看,这些差异并不重要。
Box 中的 matchParentSize
如上所述,如果您希望子布局与父项 Box
尺寸相同而不影响 Box
的尺寸,请使用 matchParentSize
修饰符。
请注意,matchParentSize
仅在 Box
作用域内可用,这意味着它仅适用于 Box
可组合项的直接子项。
在以下示例中,子项 Spacer
从其父项 Box
获取自己的尺寸,在这种情况下,后者又会从其最大的子项 ArtistCard
中获取自己的尺寸。
@Composable
fun MatchParentSizeComposable() {
Box {
Spacer(Modifier.matchParentSize().background(Color.LightGray))
ArtistCard()
}
}
如果使用 fillMaxSize
代替 matchParentSize
,Spacer
将占用父项允许的所有可用空间,反过来使父项展开并填满所有可用空间。
Row 和 Column 中的 weight
如前文的内边距和尺寸部分所述,默认情况下,可组合项的尺寸由其封装的内容定义。您可以使用仅可在 RowScope
和 ColumnScope
中使用的 weight
修饰符,将可组合项的尺寸设置为可在其父项内灵活调整。
让我们以包含两个 Box
可组合项的 Row
为例。第一个框的 weight
是第二个框的两倍,因此其宽度也相差两倍。由于 Row
的宽度为 210.dp
,因此第一个 Box
的宽度为 140.dp
,第二个的宽度为 70.dp
:
@Composable
fun ArtistCard(/*...*/) {
Row(
modifier = Modifier.fillMaxWidth()
) {
Image(
/*...*/
modifier = Modifier.weight(2f)
)
Column(
modifier = Modifier.weight(1f)
) {
/*...*/
}
}
}
可滚动布局
如需详细了解可滚动布局,请参阅 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 项目时提供),旨在简化界面的构建。诸如 Drawer
、FloatingActionButton
和 TopAppBar
之类的元素都有提供。
Material 组件大量使用槽位 API,这是 Compose 引入的一种模式,它在可组合项之上带来一层自定义设置。这种方法使组件变得更加灵活,因为它们接受可以自行配置的子元素,而不必公开子元素的每个配置参数。槽位会在界面中留出空白区域,让开发者按照自己的意愿来填充。例如,下面是您可以在 TopAppBar
中自定义的槽位:
可组合项通常采用 content
可组合 lambda (content: @Composable
() -> Unit
)。槽位 API 会针对特定用途公开多个 content
参数。例如,TopAppBar
可让您为 title
、navigationIcon
和 actions
提供内容。
例如,Scaffold
可让您实现具有基本 Material Design 布局结构的界面。Scaffold
可以为最常见的顶级 Material 组件(如 TopAppBar
、BottomAppBar
、FloatingActionButton
和 Drawer
)提供槽位。通过使用 Scaffold
,可轻松确保这些组件得到适当放置且正确地协同工作。
@Composable
fun HomeScreen(/*...*/) {
Scaffold(
drawerContent = { /*...*/ },
topBar = { /*...*/ },
content = { /*...*/ }
)
}
ConstraintLayout
ConstraintLayout
有助于根据可组合项的相对位置将它们放置在屏幕上,它是使用多个嵌套 Row
、Column
、Box
和自定义布局元素的替代方案。在实现对齐要求比较复杂的较大布局时,ConstraintLayout
很有用。
如需使用 Compose 中的 ConstraintLayout
,您需要在 build.gradle
中添加以下依赖项:
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-alpha08"
Compose 中的 ConstraintLayout
支持 DSL:
- 引用是使用
createRefs()
或createRefFor()
创建的,ConstraintLayout
中的每个可组合项都需要有与之关联的引用。 - 约束条件是使用
constrainAs()
修饰符提供的,该修饰符将引用作为参数,可让您在主体 lambda 中指定其约束条件。 - 约束条件是使用
linkTo()
或其他有用的方法指定的。 parent
是一个现有的引用,可用于指定对ConstraintLayout
可组合项本身的约束条件。
下面是使用 ConstraintLayout
的可组合项的示例:
@Composable
fun ConstraintLayoutContent() {
ConstraintLayout {
// Create references for the composables to constrain
val (button, text) = createRefs()
Button(
onClick = { /* Do something */ },
// Assign reference "button" to the Button composable
// and constrain it to the top of the ConstraintLayout
modifier = Modifier.constrainAs(button) {
top.linkTo(parent.top, margin = 16.dp)
}
) {
Text("Button")
}
// Assign reference "text" to the Text composable
// and constrain it to the bottom of the Button composable
Text("Text", Modifier.constrainAs(text) {
top.linkTo(button.bottom, margin = 16.dp)
})
}
}
此代码使用 16.dp
的外边距来约束 Button
顶部到父项的距离,同样使用 16.dp
的外边距来约束 Text
到 Button
底部的距离。
如需查看有关如何使用 ConstraintLayout
的更多示例,请参考布局 Codelab。
Decoupled API
在 ConstraintLayout
示例中,约束条件是在应用它们的可组合项中使用修饰符以内嵌方式指定的。不过,在某些情况下,最好将约束条件与应用它们的布局分离开来。例如,您可能会希望根据屏幕配置来更改约束条件,或在两个约束条件集之间添加动画效果。
对于此类情况,您可以通过不同的方式使用 ConstraintLayout
:
- 将
ConstraintSet
作为参数传递给ConstraintLayout
。 - 使用
layoutId
修饰符将在ConstraintSet
中创建的引用分配给可组合项。
@Composable
fun DecoupledConstraintLayout() {
BoxWithConstraints {
val constraints = if (minWidth < 600.dp) {
decoupledConstraints(margin = 16.dp) // Portrait constraints
} else {
decoupledConstraints(margin = 32.dp) // Landscape constraints
}
ConstraintLayout(constraints) {
Button(
onClick = { /* Do something */ },
modifier = Modifier.layoutId("button")
) {
Text("Button")
}
Text("Text", Modifier.layoutId("text"))
}
}
}
private fun decoupledConstraints(margin: Dp): ConstraintSet {
return ConstraintSet {
val button = createRefFor("button")
val text = createRefFor("text")
constrain(button) {
top.linkTo(parent.top, margin = margin)
}
constrain(text) {
top.linkTo(button.bottom, margin)
}
}
}
然后,当您需要更改约束条件时,只需传递不同的 ConstraintSet
即可。
了解详情
如需详细了解 Compose 中的 ConstraintLayout,请参阅 Jetpack Compose Codelab 中的布局的约束布局部分,以及使用 ConstraintLayout
的 Compose 示例中的 API 实际运用。
自定义布局
在 Compose 中,界面元素由可组合函数表示,此类函数在被调用后会发出一部分界面,这部分界面随后会被添加到呈现在屏幕上的界面树中。每个界面元素都有一个父元素,还可能有多个子元素。此外,每个元素在其父元素中都有一个位置,指定为 (x, y) 位置;也都有一个尺寸,指定为 width
和 height
。
父元素定义其子元素的约束条件。元素需要在这些约束条件内定义尺寸。约束条件可限制元素的最小和最大 width
和 height
。如果某个元素有子元素,它可能会测量每个子元素,以帮助确定其尺寸。一旦某个元素确定并报告了它自己的尺寸,就有机会定义如何相对于自身放置它的子元素,如创建自定义布局中所详述。
单遍测量对性能有利,使 Compose 能够高效地处理较深的界面树。如果某个元素测量了它的子元素两次,而该子元素又测量了它的一个子元素两次,依此类推,那么一次尝试布置整个界面就不得不做大量的工作,这将很难让应用保持良好的性能。不过,有时除了子元素的单遍测量告知您的信息之外,您确实还需要一些额外的信息。有一些方法可以有效地处理这样的情况,这些方法在固有特性测量部分进行了介绍。
作用域的使用决定了您可以衡量和放置子项的时间。只能在测量和布局传递期间测量布局,并且只能在布局传递期间以及在事前测量之后才能放置子项。由于 Compose 作用域(如 MeasureScope 和 PlacementScope),此操作在编译时强制执行。
使用布局修饰符
您可以使用 layout
修饰符来修改元素的测量和布局方式。Layout
是一个 lambda;它的参数包括您可以测量的元素(以 measurable
的形式传递)以及该可组合项的传入约束条件(以 constraints
的形式传递)。自定义布局修饰符可能如下所示:
fun Modifier.customLayoutModifier(...) =
this.layout { measurable, constraints ->
...
})
假设我们在屏幕上显示 Text
,并控制从顶部到第一行文本基线的距离。这正是 paddingFromBaseline
修饰符的作用,我们在这里将其作为一个示例来实现。为此,请使用 layout
修饰符将可组合项手动放置在屏幕上。Text
上内边距设为 24.dp
时的预期行为如下:
生成该间距的代码如下:
fun Modifier.firstBaselineToTop(
firstBaselineToTop: Dp
) = layout { measurable, constraints ->
// Measure the composable
val placeable = measurable.measure(constraints)
// Check the composable has a first baseline
check(placeable[FirstBaseline] != AlignmentLine.Unspecified)
val firstBaseline = placeable[FirstBaseline]
// Height of the composable with padding - first baseline
val placeableY = firstBaselineToTop.roundToPx() - firstBaseline
val height = placeable.height + placeableY
layout(placeable.width, height) {
// Where the composable gets placed
placeable.placeRelative(0, placeableY)
}
}
下面对该代码中发生的过程进行了说明:
- 在
measurable
lambda 参数中,您需要通过调用measurable.measure(constraints)
来测量以可测量参数表示的Text
。 - 您需要通过调用
layout(width, height)
方法指定可组合项的尺寸,该方法还会提供一个用于放置被封装元素的 lambda。在本例中,它是最后一条基线和增加的上内边距之间的高度。 - 您通过调用
placeable.place(x, y)
将被封装的元素放到屏幕上。如果未放置被封装的元素,它们将不可见。y
位置对应于上内边距,即文本的第一条基线的位置。
如需验证这段代码是否可以发挥预期的作用,请在 Text
上使用以下修饰符:
@Preview
@Composable
fun TextWithPaddingToBaselinePreview() {
MyApplicationTheme {
Text("Hi there!", Modifier.firstBaselineToTop(32.dp))
}
}
@Preview
@Composable
fun TextWithNormalPaddingPreview() {
MyApplicationTheme {
Text("Hi there!", Modifier.padding(top = 32.dp))
}
}
创建自定义布局
layout
修饰符仅更改调用可组合项。如需测量和布置多个可组合项,请改用 Layout
可组合项。此可组合项允许您手动测量和布置子项。Column
和 Row
等所有较高级别的布局都使用 Layout
可组合项构建而成。
我们来构建一个非常基本的 Column
。大多数自定义布局都遵循以下模式:
@Composable
fun MyBasicColumn(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Layout(
modifier = modifier,
children = content
) { measurables, constraints ->
// measure and position children given constraints logic here
}
}
与 layout
修饰符类似,measurables
是需要测量的子项的列表,而 constraints
是来自父项的约束条件。按照与前面相同的逻辑,可按如下方式实现 MyBasicColumn
:
@Composable
fun MyBasicColumn(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Layout(
modifier = modifier,
content = content
) { measurables, constraints ->
// Don't constrain child views further, measure them with given constraints
// List of measured children
val placeables = measurables.map { measurable ->
// Measure each children
measurable.measure(constraints)
}
// Set the size of the layout as big as it can
layout(constraints.maxWidth, constraints.maxHeight) {
// Track the y co-ord we have placed children up to
var yPosition = 0
// Place children in the parent layout
placeables.forEach { placeable ->
// Position item on the screen
placeable.placeRelative(x = 0, y = yPosition)
// Record the y co-ord placed up to
yPosition += placeable.height
}
}
}
}
可组合子项受 Layout
约束条件(没有 minHeight
约束条件)的约束,它们的放置基于前一个可组合项的 yPosition
。
该自定义可组合项的使用方式如下:
@Composable
fun CallingComposable(modifier: Modifier = Modifier) {
MyBasicColumn(modifier.padding(8.dp)) {
Text("MyBasicColumn")
Text("places items")
Text("vertically.")
Text("We've done it by hand!")
}
}
自定义布局的实际运用
如需详细了解自定义布局和修饰符,请参阅 Jetpack Compose 中的布局 Codelab,然后查看自定义布局的 Compose 示例中的 API 实际运用。
布局方向
您可以通过更改 LocalLayoutDirection
CompositionLocal 来更改可组合项的布局方向。
如果您要将可组合项手动放置在屏幕上,则 LayoutDirection
是 layout
修饰符或 Layout
可组合项的 LayoutScope
的一部分。
使用 layoutDirection
时,应使用 place
放置可组合项。与 placeRelative
方法不同,place
不会根据布局方向(从左到右与从右到左)发生变化。
固有特性测量
Compose 有一项规则,即,子项只能测量一次,测量两次就会引发运行时异常。但是,有时需要先收集一些关于子项的信息,然后再测量子项。
借助固有特性,您可以先查询子项,然后再进行实际测量。
对于可组合项,您可以查询其 intrinsicWidth
或 intrinsicHeight
:
(min|max)IntrinsicWidth
:给定此高度,可以正确绘制内容的最小/最大宽度是多少?(min|max)IntrinsicHeight
:给定此宽度,可以正确绘制内容的最小/最大高度是多少?
例如,如果您查询具有无限 width
的 Text
的 minIntrinsicHeight
,它将返回 Text
的 height
,就好像该文本是在单行中绘制的一样。
固有特性的实际运用
假设我们需要创建一个可组合项,该可组合项在屏幕上显示两个用分隔线隔开的文本,如下所示:
我们该怎么做?我们可以将两个 Text
放在同一 Row
,并在其中最大程度地扩展,另外在中间放置一个 Divider
。我们需要将分隔线的高度设置为与最高的 Text
相同,粗细设置为 width = 1.dp
。
@Composable
fun TwoTexts(
text1: String,
text2: String,
modifier: Modifier = Modifier
) {
Row(modifier = modifier) {
Text(
modifier = Modifier
.weight(1f)
.padding(start = 4.dp)
.wrapContentWidth(Alignment.Start),
text = text1
)
Divider(
color = Color.Black,
modifier = Modifier.fillMaxHeight().width(1.dp)
)
Text(
modifier = Modifier
.weight(1f)
.padding(end = 4.dp)
.wrapContentWidth(Alignment.End),
text = text2
)
}
}
@Preview
@Composable
fun TwoTextsPreview() {
MaterialTheme {
Surface {
TwoTexts(text1 = "Hi", text2 = "there")
}
}
}
预览时,我们发现分隔线扩展到整个屏幕,这并不是我们想要的效果:
之所以出现这种情况,是因为 Row
会逐个测量每个子项,并且 Text
的高度不能用于限制 Divider
。我们希望 Divider
以一个给定的高度来填充可用空间。为此,我们可以使用 height(IntrinsicSize.Min)
修饰符。
height(IntrinsicSize.Min)
可将其子项的高度强行调整为最小固有高度。由于该修饰符具有递归性,因此它将查询 Row
及其子项 minIntrinsicHeight
。
将其应用到代码中,就能达到预期的效果:
@Composable
fun TwoTexts(
text1: String,
text2: String,
modifier: Modifier = Modifier
) {
Row(modifier = modifier.height(IntrinsicSize.Min)) {
Text(
modifier = Modifier
.weight(1f)
.padding(start = 4.dp)
.wrapContentWidth(Alignment.Start),
text = text1
)
Divider(
color = Color.Black,
modifier = Modifier.fillMaxHeight().width(1.dp)
)
Text(
modifier = Modifier
.weight(1f)
.padding(end = 4.dp)
.wrapContentWidth(Alignment.End),
text = text2
)
}
}
预览如下:
Row
可组合项的 minIntrinsicHeight
将作为其子项的最大 minIntrinsicHeight
。Divider element's
minIntrinsicHeight
为 0,因为如果没有给出约束条件,它不会占用任何空间;如果给出特定 width
,Text
minIntrinsicHeight
将为文本的高度。因此,Row
元素的 height
约束条件将为 Text
的最大 minIntrinsicHeight
。而 Divider
会将其 height
扩展为 Row
给定的 height
约束条件。
自定义布局中的固有特性
创建自定义 Layout
或 layout
修饰符时,系统会根据近似值自动计算固有测量结果。因此,计算结果可能并不适用于所有布局。这些 API 提供了替换这些默认值的选项。
要指定自定义 Layout
的固有特性测量,则在创建该布局时替换 MeasurePolicy
的 minIntrinsicWidth
、minIntrinsicHeight
、maxIntrinsicWidth
和 maxIntrinsicHeight
。
@Composable
fun MyCustomComposable(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
return object : MeasurePolicy {
override fun MeasureScope.measure(
measurables: List<Measurable>,
constraints: Constraints
): MeasureResult {
// Measure and layout here
}
override fun IntrinsicMeasureScope.minIntrinsicWidth(
measurables: List<IntrinsicMeasurable>,
height: Int
) = {
// Logic here
}
// Other intrinsics related methods have a default value,
// you can override only the methods that you need.
}
}
创建自定义 layout
修饰符时,替换 LayoutModifier
界面中的相关方法。
fun Modifier.myCustomModifier(/* ... */) = this.then(object : LayoutModifier {
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
): MeasureResult {
// Measure and layout here
}
override fun IntrinsicMeasureScope.minIntrinsicWidth(
measurable: IntrinsicMeasurable,
height: Int
): Int = {
// Logic here
}
// Other intrinsics related methods have a default value,
// you can override only the methods that you need.
})
了解详情
如需详细了解固有特性衡量,请参阅 Jetpack Compose 中的布局 Codelab 中的固有特性部分。
了解详情
如需了解详情,请参阅 Jetpack Compose 中的布局 Codelab。