Compose 的规则之一是您应该只测量您的子项一次;测量子项两次会抛出运行时异常。但是,有时您需要在测量子项之前获取有关子项的一些信息。
内在测量让您可以在子项实际测量之前查询它们。
对于一个可组合组件,您可以询问它的intrinsicWidth
或 intrinsicHeight
(min|max)IntrinsicWidth
:给定这个宽度,您能以什么最小/最大宽度正确地绘制您的内容?(min|max)IntrinsicHeight
:给定这个高度,您能以什么最小/最大高度正确地绘制您的内容?
例如,如果您询问具有无限height
的Text
的minIntrinsicHeight
,它将返回Text
的height
,就像文本在一行中绘制一样。
内在测量在实际中的应用
假设我们想要创建一个可组合组件,它在屏幕上显示两个文本,中间用分隔线隔开,如下所示
我们该如何实现?我们可以使用一个包含两个Text
的Row
,它们尽可能地扩展,并在中间添加一个Divider
。我们希望Divider
与最高的Text
一样高,并且很薄 (width = 1.dp
)。
@Composable fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) { 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 ) } }
如果我们预览它,我们会看到Divider
扩展到整个屏幕,这不是我们想要的。
之所以会这样,是因为Row
会分别测量每个子元素,而Text
的高度无法用来限制Divider
。我们希望Divider
使用给定的高度填充可用空间。为此,我们可以使用height(IntrinsicSize.Min)
修饰符。
height(IntrinsicSize.Min)
会将它的子元素的大小设置为它们的最小内在高度。由于它是递归的,它将查询Row
及其子元素的minIntrinsicHeight
。
将其应用到我们的代码中,它将按预期工作。
@Composable fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) { 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 ) } } // @Preview @Composable fun TwoTextsPreview() { MaterialTheme { Surface { TwoTexts(text1 = "Hi", text2 = "there") } } }
带有预览
Row
可组合组件的minIntrinsicHeight
将是其子元素的最大minIntrinsicHeight
。Divider
元素的minIntrinsicHeight
为 0,因为它在没有给定约束条件的情况下不会占用空间;Text
的minIntrinsicHeight
将是给定特定width
的文本的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 ) { Layout( content = content, modifier = modifier, measurePolicy = 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 ): 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. }
为您推荐
- 注意:当 JavaScript 关闭时,链接文本会显示。
- 自定义布局 {:#custom-layouts }
- Jetpack Compose 中的对齐线
- Jetpack Compose 阶段