假设我想创建一个 Column ,其宽度与其中最宽的子项 Text 一样宽。为此,列可以使用
.wrapContentWidth()
修饰符或 .width(IntrinsicSize.Max)
,但结果看起来相同。这两个修饰符有什么区别?例如:
Column(
modifier = Modifier.wrapContentWidth()
// modifier = Modifier.width(IntrinsicSize.Max)
) {
Text("short text", Modifier.background(Color.LightGray))
Text("some longer text", Modifier.background(Color.LightGray))
}
让我们在文本之间添加一个 Divider。我们希望分隔线与最宽的文本一样宽。
Column(
modifier = Modifier.wrapContentWidth()
// modifier = Modifier.width(IntrinsicSize.Max)
) {
Text("short text", Modifier.background(Color.LightGray))
Divider(color = Color.Red)
Text("some longer text", Modifier.background(Color.LightGray))
}
如您所见,在
Modifier.wrapContentWidth()
的情况下,分隔线会强制列与其父列一样宽。发生这种情况是因为 Divider
在底层使用了 .fillMaxWidth()
,并且由于列的 .wrapContentWidth()
不限制其子级宽度,因此分隔线会尽可能地扩展。
同时
Modifier.width(IntrinsicSize.Max)
的行为符合我们的预期。如果没有给出约束,分隔线不会占用空间,因此它的固有宽度为 0,并且不会影响列宽。
当您分配任何尺寸修饰符时,您实际上并没有设置要测量的
Composables
的约束条件。
假设您分配了 Modifier.fillMaxWidth()。约束设置为 minWidth=MaxWidth 和 minHeight=MaxHeight 并且等于来自父级的宽度或高度。在这种情况下,Composable 只能具有父级的宽度和高度。
如果您分配
Modifier.widthIn(min=100.dp, max=200.dp)
,您的可组合项可以在 100.dp-200.dp 之间测量。
它可以使用任何大小、填充、aspectRatio、布局、wrapX 修饰符完成,基本上如下
LayoutModifierNode, Modifier.Node() {
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
): MeasureResult {
// Some logic here
val placeable = measurable.measure(wrappedConstraints)
return layout(placeable.width, placeable.height) {
placeable.placeRelative(0, 0)
}
}
}
而具有默认大小修饰符的东西只能缩短约束最小值和最大值设置的间隔。您可以检查此答案的约束部分,以查看每个大小修改器返回的约束。基本上你不能扩大范围。假设您分配了
Modifier.size(100.dp).size(400.dp)
可组合项以 100.dp 进行测量并获得 100.dp 大小。
如果您没有分配任何大小修饰符,您可以测量 0 和父级大小之间的可组合项,这意味着它获取的是内容维度,或者 xml 的
wrap_content
是默认值。这不是在 Jetpack COmpose 中使用 Modifier.wrapContent 的方式
但是,正如我上面提到的,如果父约束或来自先前修饰符的约束设置的范围大于或小于您的可组合项,您可以通过两种方式使用 Modifier.wrapContentSize(unBounded)
@Preview
@Composable
fun WrapCOntentTest() {
Text(
modifier = Modifier
.background(Color.Yellow)
.fillMaxSize()
.wrapContentSize(
align = Alignment.Center,
unbounded = false
)
.border(2.dp, Color.Blue),
text = "Hello World",
fontSize = 26.sp
)
}
结果
正如您所看到的,背景修改器获取父级的大小,因为我们传递了最小和最大尺寸,它们都是相同的,因此黄色背景被测量并设置为父级大小。然而,使用
wrapContentSize
我们让文本在 0 到 fillMaxSize
的最大值之间进行测量。由于范围现在更大,所以它是用自己的大小来测量的。检查蓝色边框。
如果您使用 Modifier.layout 执行此操作(Modifier.wrapContent 也执行此操作),则结果如下
@Preview
@Composable
fun LayoutTest() {
Text(
modifier = Modifier
.background(Color.Yellow)
.fillMaxSize()
.layout { measurable, constraints ->
val wrappedConstraints = constraints.copy(
minWidth = 0,
minHeight = 0
)
val placeable = measurable.measure(wrappedConstraints)
layout(placeable.width, placeable.height){
placeable.placeRelative(0,0)
}
}
.border(2.dp, Color.Blue),
text = "Hello World",
fontSize = 26.sp
)
}
如您所见,我们将 minWidth 和 height 设置为 0。您还可以使用对齐方式来更改 wrapContent 修饰符中的放置位置。
第二个用途是解除最大尺寸的绑定,而不是像上面那样的最小尺寸。
假设您从父级或修改器获得了 200.dp 大小,但您希望您的图像可组合为 300.dp,然后您设置 unBounded = true ,将 maxWidth/Height 设置为 COstraints.Infinity 并且您的图像可以在来自以下的最小尺寸之间进行测量200.dp 和 Infinity,所以它的大小可以是 300.dp。
内在尺寸修饰符调用测量和放置两次,它基于尺寸修饰符或列、行等布局如何设置内在尺寸逻辑。但默认情况下,它的工作原理是这样的,第一次他们用 0-Constraints.Infinity 调用它,然后在获得正确的尺寸后,他们调用这个并将大小设置为彼此相等。
例如,在这个自定义布局中,我将它们设置为固定以进行演示。这就是基本上如何获得这些来进行测量。
@Composable
fun CustomColumnWithIntrinsicDimensions2(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
val measurePolicy = object : MeasurePolicy {
override fun MeasureScope.measure(
measurables: List<Measurable>,
constraints: Constraints
): MeasureResult {
val looseConstraints = constraints.copy(minHeight = 0)
val placeables = measurables.map { measurable ->
measurable.measure(looseConstraints)
}
var yPosition = 0
val totalHeight: Int = placeables.sumOf {
it.height
}
// 🔥 This can be sum or longest of Composable widths, or maxWidth of Constraints
val maxWidth: Int = placeables.maxOf {
it.width
}
return layout(maxWidth, totalHeight) {
placeables.forEach { placeable ->
placeable.placeRelative(x = 0, y = yPosition)
yPosition += placeable.height
}
}
}
override fun IntrinsicMeasureScope.minIntrinsicHeight(
measurables: List<IntrinsicMeasurable>,
width: Int
): Int {
println("🚙 minIntrinsicHeight() width: $width, measurables: ${measurables.size}")
// 🔥 This is just sample to show usage of minIntrinsicHeight, don't set
// static values
return 80
}
override fun IntrinsicMeasureScope.maxIntrinsicHeight(
measurables: List<IntrinsicMeasurable>,
width: Int
): Int {
println("🚗 maxIntrinsicHeight() width: $width, measurables: ${measurables.size}")
// 🔥 This is just sample to show usage of maxIntrinsicHeight, don't set
// static values
return 500
}
}
Layout(modifier = modifier, content = content, measurePolicy = measurePolicy)
}
您可以看到此布局的固有尺寸在哪里
CustomColumnWithIntrinsicDimensions2(
modifier = Modifier
// This effects height of this composable
// Parent height comes from the one in Layout by comparing
// it with other Composable's intrinsic height
// Even without this parent will have same height
.height(IntrinsicSize.Min)
.width(100.dp)
.background(Yellow400)
.padding(4.dp)
) {
Text(
"First Text",
modifier = Modifier
.background(Color(0xffF44336)),
color = Color.White
)
}
}
您还可以在本教程中查看有关布局、约束和修饰符的更多示例和示例。