Compose 约束条件和修饰符顺序

Constraints and modifier order - MAD Skills

Compose 中的修饰符可以串联起来,且串联的顺序很重要。不过,究竟有多重要呢?了解修饰符串联的原因及其对可组合项的尺寸有何影响。

1. Phases of Compose

2. Problem set

以下有三个案例,右边哪个选项表现是正确的?

3. Constraints

Constraints 约束是节点宽度和高度的最小和最大界限。当节点决定其大小时,其测量大小应落在给定的大小范围内。

在布局阶段,约束在 UI 树中从父级传递到子级。当父节点测量其子节点时,它会向每个子节点提供这些约束。

有界约束

无界约束

精确约束

联合约束

有界、无界和精确约束相结合。

当一个节点决定了自己的大小时,它会将该大小传回给树。

4. An example

5. Size modifiers

Modifier.size()

kotlin 复制代码
@Stable
fun Modifier.size(size: Dp) = this.then(
    SizeElement(
        minWidth = size,
        maxWidth = size,
        minHeight = size,
        maxHeight = size,
        enforceIncoming = true,
        inspectorInfo = debugInspectorInfo {
            name = "size"
            value = size
        }
    )
)

父节点约束(0-300, 0-200),子节点约束(40-40, 40-40),子节点约束在父节点约束之内,所以最终尺寸为(40, 40)。

父节点约束(0-300, 0-200),子节点约束(400-400, 400-400),子节点约束超过父节点约束,所以最终尺寸为(300, 200),遵循父节点约束的条件下,尽可能匹配子节点约束。

父节点约束(100-300, 100-200),子节点约束(50-50, 50-50),子节点约束小于父节点约束,所以最终尺寸为(100, 100),遵循父节点约束的条件下,尽可能匹配子节点约束。

多个 Modifier.size() 链接

上面的规则解释了为什么多个 Modifier.size() 修改器连接不起作用。

父节点约束(0-300, 0-200),第一个 Modifier.size() 约束(100-100, 100-100),第二个 Modifier.size() 约束(50-50, 50-50)。

第一个 Modifier.size() 约束符合父节点约束,但是第二个 Modifier.size() 约束小于第一个 Modifier.size() 约束,所以最终约束(100, 100),遵循第一个 Modifier.size() 约束的条件下,尽可能匹配第二个 Modifier.size() 约束。

Modifier.requiredSize()

kotlin 复制代码
@Stable
fun Modifier.requiredSize(size: Dp) = this.then(
    SizeElement(
        minWidth = size,
        maxWidth = size,
        minHeight = size,
        maxHeight = size,
        enforceIncoming = false,
        inspectorInfo = debugInspectorInfo {
            name = "requiredSize"
            value = size
        }
    )
)

如果不希望节点遵循传入的约束,可以使用 Modifier.requiredSize() 替换 Modifier.size(),它可以覆盖传入的约束,并传递你指定的尺寸。当将尺寸传递回树时,它会将报告的尺寸重置为传入的约束。

Modifier.width()

kotlin 复制代码
@Stable
fun Modifier.width(width: Dp) = this.then(
    SizeElement(
        minWidth = width,
        maxWidth = width,
        enforceIncoming = true,
        inspectorInfo = debugInspectorInfo {
            name = "width"
            value = width
        }
    )
)

Modifier.width() 只约束宽度。

Modifier.height()

kotlin 复制代码
@Stable
fun Modifier.height(height: Dp) = this.then(
    SizeElement(
        minHeight = height,
        maxHeight = height,
        enforceIncoming = true,
        inspectorInfo = debugInspectorInfo {
            name = "height"
            value = height
        }
    )
)

Modifier.height() 只约束高度。

Modifier.sizeIn()

kotlin 复制代码
@Stable
fun Modifier.sizeIn(
    minWidth: Dp = Dp.Unspecified,
    minHeight: Dp = Dp.Unspecified,
    maxWidth: Dp = Dp.Unspecified,
    maxHeight: Dp = Dp.Unspecified
) = this.then(
    SizeElement(
        minWidth = minWidth,
        minHeight = minHeight,
        maxWidth = maxWidth,
        maxHeight = maxHeight,
        enforceIncoming = true,
        inspectorInfo = debugInspectorInfo {
            name = "sizeIn"
            properties["minWidth"] = minWidth
            properties["minHeight"] = minHeight
            properties["maxWidth"] = maxWidth
            properties["maxHeight"] = maxHeight
        }
    )
)

Modifier.sizeIn() 实现更细粒度的约束控制。

6. Solutions

案例一

假设整个父节点的约束为(0-300, 0-200),fillMaxSize() 已经约束了(300, 200),因为 size(50) 约束小于 fillMaxSize() 约束,需要先遵循 fillMaxSize() 约束,因此最终尺寸为(300, 200)。

案例二

wrapContentSize() 默认会重置为有界约束,并将内容放在节点中心,因为 size(50) 约束符合(0-300, 0-200)约束,所以最终尺寸为(50, 50)。

kotlin 复制代码
@Stable
fun Modifier.wrapContentSize(
    align: Alignment = Alignment.Center,
    unbounded: Boolean = false
) = this.then(
    if (align == Alignment.Center && !unbounded) {
        WrapContentSizeCenter
    } else if (align == Alignment.TopStart && !unbounded) {
        WrapContentSizeTopStart
    } else {
        WrapContentElement.size(align, unbounded)
    }
)

案例三

Modifier.clip() 不会改变约束。

Modifier.clip() 作用于 120dp * 120dp 的画布。

Modifier.padding(10) 将画布尺寸降低到 100dp * 100dp

然后将图像绘制在画布上,图片是根据原来的 120dp * 120dp 的画布进行裁剪的。

相关推荐
2501_916008892 小时前
深入解析iOS机审4.3原理与混淆实战方法
android·java·开发语言·ios·小程序·uni-app·iphone
独行soc3 小时前
2026年渗透测试面试题总结-20(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
常利兵3 小时前
2026年,Android开发已死?不,它正迎来黄金时代!
android
Risehuxyc3 小时前
备份三个PHP程序
android·开发语言·php
Doro再努力13 小时前
【Linux操作系统10】Makefile深度解析:从依赖推导到有效编译
android·linux·运维·服务器·编辑器·vim
Daniel李华13 小时前
echarts使用案例
android·javascript·echarts
做人不要太理性14 小时前
CANN Runtime 运行时组件深度解析:任务调度机制、存储管理策略与维测体系构建逻辑
android·运维·魔珐星云
我命由我1234514 小时前
Android 广播 - 静态注册与动态注册对广播接收器实例创建的影响
android·java·开发语言·java-ee·android studio·android-studio·android runtime
朗迹 - 张伟15 小时前
Tauri2 导出 Android 详细教程
android
lpruoyu16 小时前
【Android第一行代码学习笔记】Android架构_四大组件_权限_持久化_通知_异步_服务
android·笔记·学习