在 Compose 中,您可以将多个修饰符串联在一起,以更改可组合项的外观和风格。这些修饰符链可能会影响传递给可组合函数的限制,这些限制定义了宽度和高度边界。
本页介绍了串联的修饰符如何影响限制,进而影响可组合项的测量和放置。
界面树中的修饰符
为了了解修饰符之间的相互影响,最好直观地了解它们在界面树中的显示方式(界面树是在组合阶段生成的)。如需了解详情,请参阅合成部分。
在界面树中,您可以将修饰符可视化为布局节点的封装容器节点:

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

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

- 无界限:节点不受任何大小限制。最大宽度和高度边界设置为无限大。

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

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

下一部分将介绍如何将这些限制从父级传递到子级。
约束条件如何从父级传递到子级
在布局阶段的限制条件中所述算法的第一步中,限制条件会从界面树中的父级传递到子级。
当父节点测量其子节点时,它会向每个子节点提供这些限制,以告知它们允许的大小。然后,当它确定自己的大小时,也会遵守自己的父级传递的约束条件。
从宏观上讲,该算法的运作方式如下:
- 为了确定自己实际想要占用的尺寸,界面树中的根节点会测量其子节点,并将相同的约束条件转发给其第一个子节点。
- 如果子项是不影响测量的修饰符,则将限制转发给下一个修饰符。除非遇到会影响测量的修饰符,否则约束会按原样传递到修饰符链中。然后,相应地调整限制条件的大小。
- 当到达没有任何子节点的节点(称为“叶节点”)时,该节点会根据传入的约束条件确定自身的大小,并将此已解析的大小返回给其父节点。
- 父项会根据此子项的测量结果调整其约束条件,并使用这些调整后的约束条件调用其下一个子项。
- 在测量完父节点的所有子节点后,父节点会自行决定其大小,并将该大小告知其父节点。
- 这样,整个树就会以深度优先的方式遍历。最终,所有节点都已确定其大小,测量步骤完成。
如需查看深入示例,请观看限制条件和修饰符顺序视频。
影响限制的修饰符
您在上一部分中了解到,某些修饰符会影响约束大小。以下部分介绍了会影响限制的具体修饰符。
size
修饰符
size
修饰符用于声明内容的首选尺寸。
例如,以下界面树应由 200dp
渲染到 300dp
的容器中。约束条件是有界限的,允许的宽度介于 100dp
和 300dp
之间,高度介于 100dp
和 200dp
之间:

size
修饰符会调整传入的限制条件,以匹配传递给它的值。在此示例中,该值为 150dp
:

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
的确切约束界限(即200
),从而有效地忽略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
的原始圆形剪裁图片,因此输出结果不是圆形。