在 Compose 中,您可以将多个修饰符串联在一起,以更改可组合项的外观。这些修饰符链会影响传递给可组合项的约束,这些约束定义了宽度和高度的边界。
本页面介绍了链式修饰符如何影响约束,进而影响可组合项的测量和放置。
UI 树中的修饰符
为了理解修饰符如何相互影响,将它们在 UI 树中显示的方式可视化会很有帮助,UI 树是在组合阶段生成的。有关更多信息,请参阅组合部分。
在 UI 树中,您可以将修饰符可视化为布局节点的包装器节点。

为可组合项添加多个修饰符会创建修饰符链。当您将多个修饰符串联在一起时,每个修饰符节点都会包装链的其余部分和其中的布局节点。例如,当您将 clip
和 size
修饰符串联在一起时,clip
修饰符节点会包装 size
修饰符节点,后者再包装 Image
布局节点。
在布局阶段,遍历树的算法保持不变,但每个修饰符节点也会被访问。这样,修饰符就可以更改其包装的修饰符或布局节点的大小要求和放置位置。
如图 2 所示,Image
和 Text
可组合项的实现本身由包装单个布局节点的修饰符链组成。Row
和 Column
的实现仅仅是描述如何布局其子节点的布局节点。

总结
- 修饰符包装单个修饰符或布局节点。
- 布局节点可以布局多个子节点。
以下部分介绍了如何使用此心理模型来推断修饰符链及其如何影响可组合项的大小。
布局阶段的约束
布局阶段遵循三步算法来查找每个布局节点的宽度、高度和 x、y 坐标:
- 测量子节点:节点测量其子节点(如果有)。
- 决定自身大小:基于这些测量,节点决定自身大小。
- 放置子节点:每个子节点相对于节点自身位置放置。
Constraints
有助于在算法的前两个步骤中找到节点的正确大小。约束定义了节点宽度和高度的最小和最大边界。当节点决定其大小时,其测量大小应在此大小范围内。
约束类型
约束可以是以下类型之一:
- 有界:节点具有最大和最小宽度和高度。

- 无界:节点不受任何大小的约束。最大宽度和高度边界设置为无穷大。

- 精确:要求节点遵循精确的大小要求。最小和最大边界设置为相同的值。

- 组合:节点遵循上述约束类型的组合。例如,约束可以限制宽度,同时允许无界的最大高度,或设置精确的宽度但提供有界的高度。

下一节将介绍这些约束如何从父级传递到子级。
约束如何从父级传递到子级
在布局阶段的约束中描述的算法的第一步中,约束从 UI 树中的父节点传递到子节点。
当父节点测量其子节点时,它会向每个子节点提供这些约束,让它们知道自己允许的最大或最小尺寸。然后,当它决定自身大小时,它也会遵循其自身父节点传递进来的约束。
从高层来看,该算法的工作方式如下:
- 为了决定它实际想要占据的大小,UI 树中的根节点测量其子节点并将其相同约束转发给其第一个子节点。
- 如果子节点是不影响测量的修饰符,它会将约束转发给下一个修饰符。约束在修饰符链中原样传递,除非遇到影响测量的修饰符。然后,约束会相应地重新调整大小。
- 一旦达到没有子节点的节点(称为“叶节点”),它会根据传入的约束决定其大小,并将此解析后的大小返回给其父节点。
- 父节点根据子节点的测量结果调整其约束,并使用这些调整后的约束调用其下一个子节点。
- 一旦父节点的所有子节点都测量完毕,父节点就决定自身大小并将其通知其父节点。
- 这样,整个树就会进行深度优先遍历。最终,所有节点都决定了它们的大小,测量步骤完成。
有关详细示例,请参阅约束和修饰符顺序视频。
影响约束的修饰符
您在上一节中了解到,某些修饰符会影响约束大小。以下部分介绍了影响约束的特定修饰符。
size
修饰符
size
修饰符声明了内容的优选大小。
例如,以下 UI 树应在 300dp
宽、200dp
高的容器中渲染。约束是有界的,允许宽度在 100dp
到 300dp
之间,高度在 100dp
到 200dp
之间。

size
修饰符会调整传入的约束以匹配传递给它的值。在此示例中,值为 150dp
。

size
修饰符将约束调整为 150dp
。如果宽度和高度小于最小约束边界,或大于最大约束边界,则修饰符将尽可能地匹配传入的约束,同时仍遵守传入的约束。

size
修饰符尽可能遵守传入的约束。请注意,串联多个 size
修饰符不起作用。第一个 size
修饰符将最小和最大约束都设置为固定值。即使第二个 size
修饰符请求更小或更大的尺寸,它仍然需要遵守传入的精确边界,因此它不会覆盖这些值。

size
修饰符链,其中传入的第二个值 (50dp
) 不会覆盖第一个值 (100dp
)。requiredSize
修饰符
如果您需要节点覆盖传入的约束,请使用 requiredSize
修饰符,而不是 size
。 requiredSize
修饰符会替换传入的约束,并将您指定的大小作为精确边界传递。
当大小返回到树的上方时,子节点将居中于可用空间中。

requiredSize
修饰符覆盖来自 size
修饰符的传入约束。width
和 height
修饰符
size
修饰符调整约束的宽度和高度。使用 width
修饰符,您可以设置固定宽度但保持高度未定。同样,使用 height
修饰符,您可以设置固定高度但保持宽度未定。

width
修饰符和 height
修饰符分别设置固定宽度和高度。sizeIn
修饰符
sizeIn
修饰符允许您设置宽度和高度的精确最小和最大约束。如果您需要对约束进行精细控制,请使用 sizeIn
修饰符。

minWidth
、maxWidth
、minHeight
和 maxHeight
的 sizeIn
修饰符。示例
本节展示并解释了几个带有链式修饰符的代码片段的输出。
Image( painterResource(R.drawable.hero), contentDescription = null, Modifier .fillMaxSize() .size(50.dp) )
此代码片段会生成以下输出:
fillMaxSize
修饰符将约束更改为将最小宽度和高度都设置为最大值——宽度为300dp
,高度为200dp
。- 尽管
size
修饰符希望使用50dp
的大小,但它仍需遵守传入的最小约束。因此,size
修饰符也将输出300
x200
的精确约束边界,从而有效地忽略size
修饰符中提供的值。 Image
遵循这些边界并报告大小为300
x200
,此大小一路传递到树的上方。
Image( painterResource(R.drawable.hero), contentDescription = null, Modifier .fillMaxSize() .wrapContentSize() .size(50.dp) )
此代码片段会生成以下输出:
fillMaxSize
修饰符调整约束,将最小宽度和高度都设置为最大值 — 宽度300dp
,高度200dp
。wrapContentSize
修饰符会重置最小约束。因此,虽然fillMaxSize
导致了固定约束,但wrapContentSize
将其重置回有界约束。下一个节点现在可以再次占据整个空间,或者小于整个空间。size
修饰符将约束设置为最小和最大边界为50
。Image
解析为50
x50
的大小,size
修饰符转发该大小。wrapContentSize
修饰符有一个特殊属性。它会将其子项放置在其可用最小边界的中心,这些边界已传递给它。因此,它与其父项通信的大小等于传递给它的最小边界。
通过组合仅仅三个修饰符,您可以为可组合项定义大小并将其在其父项中居中。
Image( painterResource(R.drawable.hero), contentDescription = null, Modifier .clip(CircleShape) .padding(10.dp) .size(100.dp) )
此代码片段会生成以下输出:
clip
修饰符不更改约束。padding
修饰符降低了最大约束。size
修饰符将所有约束设置为100dp
。Image
遵守这些约束并报告大小为100
x100dp
。padding
修饰符在所有尺寸上添加了10dp
,因此它将报告的宽度和高度增加了20dp
。- 现在在绘制阶段,
clip
修饰符作用于一个120
x120dp
的画布。因此,它创建了该大小的圆形遮罩。 padding
修饰符随后在其所有尺寸上将内容内嵌10dp
,因此它将画布大小降低到100
x100dp
。Image
在该画布中绘制。图像根据原始120dp
圆形进行裁剪,因此输出是非圆形结果。