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 的画布进行裁剪的。

相关推荐
千里马学框架4 小时前
安卓系统中线程优先级Priority查看方式汇总
android·framework·线程·安卓framework开发·优先级·priority
沐怡旸4 小时前
【Android】Handler/Looper机制相关的类图和流程图
android
生莫甲鲁浪戴5 小时前
Android Studio新手开发第二十一天
android·ide·android studio
生莫甲鲁浪戴5 小时前
Android Studio新手开发第二十二天
android·ide·android studio
用户41659673693555 小时前
Jetpack Compose 中实现带圆角边框的单词级富文本效果(分词与布局实践)
android
顾林海5 小时前
Android UI优化:让你的APP从“卡顿掉帧”到“丝滑如德芙”
android·面试·性能优化
啊森要自信6 小时前
【MySQL 数据库】MySQL用户管理
android·c语言·开发语言·数据库·mysql
黄毛火烧雪下6 小时前
(二)Flutter插件之Android插件开发
android·flutter
2501_916007476 小时前
iOS 上架技术支持全流程解析,从签名配置到使用 开心上架 的实战经验分享
android·macos·ios·小程序·uni-app·cocoa·iphone