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

相关推荐
Jony_26 分钟前
Android 设计架构演进历程
android·android jetpack
犹若故人归1 小时前
Android开发应用--高级UI界面设计
android·ui
zzhongcy2 小时前
复合索引 (item1, item2, item3 ) > (?, ?, ?) 不起作用,EXPLAIN 后type=ALL(全表扫描)
android·数据库
冬奇Lab3 小时前
稳定性性能系列之十三——CPU与I/O性能优化:Simpleperf与存储优化实战
android·性能优化
像风一样自由4 小时前
android native 中的函数动态注册方式总结
android·java·服务器·安卓逆向分析·native函数动态注册·.so文件分析
nono牛4 小时前
Makefile中打印变量
android
没有了遇见5 小时前
Android 关于RecycleView和ViewPager2去除边缘反馈
android
城东米粉儿5 小时前
android gzip数据压缩 笔记
android
城东米粉儿5 小时前
android 流量优化笔记
android
似霰6 小时前
HIDL Hal 开发笔记10----添加硬件访问服务(Java 层调用 HIDL)
android·framework·hal