在 Compose 中,您可以将多个修饰符链接在一起以更改可组合项的外观和感觉。这些修饰符链会影响传递给可组合项的约束,这些约束定义了宽度和高度边界。
本页面描述了链接的修饰符如何影响约束,进而影响可组合项的测量和放置。
UI 树中的修饰符
要理解修饰符如何相互影响,有助于可视化它们在 UI 树中的显示方式,该树是在组合阶段生成的。有关更多信息,请参阅 组合 部分。
在 UI 树中,您可以将修饰符可视化为布局节点的包装节点。
向可组合项添加多个修饰符会创建一个修饰符链。当您链接多个修饰符时,每个修饰符节点都会包装链的其余部分以及其中的布局节点。例如,当您链接一个 clip
和一个 size
修饰符时,clip
修饰符节点会包装 size
修饰符节点,然后包装 Image
布局节点。
在布局阶段,遍历树的算法 保持不变,但也会访问每个修饰符节点。这样,修饰符可以更改其包装的修饰符或布局节点的大小要求和放置位置。
如图 2 所示,Image
和 Text
可组合项本身的实现由包装单个布局节点的修饰符链组成。 Row
和 Column
的实现只是描述如何布局其子节点的布局节点。
总结
- 修饰符包装单个修饰符或布局节点。
- 布局节点可以布局多个子节点。
以下部分介绍如何使用这种思维模型来推断修饰符链以及它如何影响可组合项的大小。
布局阶段的约束
布局阶段 遵循三步算法来查找每个布局节点的宽度、高度以及 x、y 坐标。
- 测量子节点:节点测量其子节点(如果有)。
- 确定自身大小:根据这些测量值,节点确定自身的大小。
- 放置子节点:每个子节点相对于节点自身的位置进行放置。
Constraints
有助于在算法的前两个步骤中找到节点的正确尺寸。约束定义了节点宽度和高度的最小和最大边界。当节点确定其大小时,其测量大小应在此尺寸范围内。
约束类型
约束可以是以下类型之一:
- 有界:节点具有最大和最小宽度和高度。
- 无界:节点不受任何尺寸约束。最大宽度和高度边界设置为无穷大。
- 精确:要求节点遵循精确的大小要求。最小和最大边界设置为相同的值。
- 组合:节点遵循上述约束类型的组合。例如,约束可以限定宽度,同时允许无界最大高度,或者设置精确宽度,但提供限定高度。
下一部分介绍这些约束是如何从父节点传递到子节点的。
约束是如何从父节点传递到子节点的
在 布局阶段的约束 中描述的算法第一步中,约束会从 UI 树的父节点传递到子节点。
当父节点测量其子节点时,它会向每个子节点提供这些约束,以告知它们允许的大小。然后,当它确定自身大小时,它也会遵守其自身父节点传递过来的约束。
从高层次上讲,算法的工作原理如下:
- 为了确定它实际上想要占据的大小,UI 树中的根节点会测量其子节点,并将相同的约束转发给其第一个子节点。
- 如果子节点是不会影响测量的修饰符,它会将约束转发给下一个修饰符。除非遇到会影响测量的修饰符,否则约束会按原样向下传递修饰符链。然后,约束将相应地调整大小。
- 一旦到达没有子节点的节点(称为“叶节点”),它会根据传递过来的约束确定其大小,并将此已解决的大小返回给其父节点。
- 父节点会根据该子节点的测量值调整其约束,并使用这些调整后的约束调用其下一个子节点。
- 一旦父节点的所有子节点都被测量,父节点就会确定自身的大小,并将其告知其自身父节点。
- 这样,整个树就会以深度优先的方式遍历。最终,所有节点都确定了其大小,测量步骤完成。
有关深入示例,请参阅 约束和修饰符顺序 视频。
影响约束的修饰符
您在上一部分中了解到,一些修饰符会影响约束大小。以下部分介绍会影响约束的特定修饰符。
size
修饰符
size
修饰符声明内容的首选大小。
例如,以下 UI 树应在一个 300dp
x 200dp
的容器中呈现。约束是有界的,允许宽度在 100dp
和 300dp
之间,高度在 100dp
和 200dp
之间。
size
修饰符会调整传入的约束以匹配传递给它的值。在本例中,该值为 150dp
。
如果宽度和高度小于最小约束边界,或大于最大约束边界,则修饰符会尽可能匹配传递的约束,同时仍遵守传入的约束。
请注意,链接多个 size
修饰符不起作用。第一个 size
修饰符会将最小和最大约束都设置为固定值。即使第二个 size 修饰符请求更小或更大的尺寸,它仍然需要遵守传入的精确边界,因此它不会覆盖这些值。
requiredSize
修饰符
如果您需要节点覆盖传入的约束,请使用 requiredSize
修饰符,而不是 size
。 requiredSize
修饰符会替换传入的约束,并将您指定的大小作为精确边界传递。
当大小向上传递回树时,子节点将在可用的空间中居中。
width
和 height
修饰符
size
修饰符会调整约束的宽度和高度。使用 width
修饰符,您可以设置固定宽度,但让高度未定。类似地,使用 height
修饰符,您可以设置固定高度,但让宽度未定。
sizeIn
修饰符
sizeIn
修饰符允许您为宽度和高度设置精确的最小和最大约束。如果您需要对约束进行细粒度控制,请使用 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
圆形进行裁剪,因此输出结果不是圆形。