15.3 Compose 源码解析之测量、布局(三) 流程分析

208 阅读2分钟

A9F152B1-FD07-4895-A693-7AC37ACB7FBE.png

internal val innerLayoutNodeWrapper: LayoutNodeWrapper = InnerPlaceable(this)

innerLayoutNodeWrapper 是 InnerPlaceable 类型的对象 ,从图中可以看出其作用是执行 LayoutNode MeasurePolicy 中的 measure() 方法。

通过 15.1 我们知道 MeasureScope.measure   方法,测量子元素与容器本身的大小并决定如何在容器中布局子元素。子元素测量时又会从 LayoutNode 开始 ,再次执行蓝色部分的流程。

private val outerMeasurablePlaceable = OuterMeasurablePlaceable(this, innerLayoutNodeWrapper)

通过 15.2 我们知道 outerMeasurablePlaceable.wrapped 是以 innerLayoutNodeWrapper 为初始值遍历 modifier  将每个 Modifier 对象转换成 LayoutNodeWrapper 对象包装起来得到的。

至此我们流程图中我们还剩尺寸相关的 Modifier 是如何转换成约束传递下去的这一个问题没有解。

尺寸相关的 Modifier 是如何转换成约束

在 setModifier() 方法中所有嵌套的 LayoutNodeWrapper 都继承自 DelegatingLayoutNodeWrapper,与尺寸无关的 Modifier 对应的 LayoutNodeWrapper 没有重写 measure() 方法 ,会执行DelegatingLayoutNodeWrapper#measure 方法。例如: ModifiedDrawNode。

internal open class DelegatingLayoutNodeWrapper<T : Modifier.Element>(
    override var wrapped: LayoutNodeWrapper,
    open var modifier: T
) : LayoutNodeWrapper(wrapped.layoutNode) {
    override fun measure(constraints: Constraints): Placeable = performingMeasure(constraints) {
      	//调用包装时 wrapped 的 measure 方法
        val placeable = wrapped.measure(constraints)
        measureResult = object : MeasureResult {
            override val width: Int = wrapped.measureResult.width
            override val height: Int = wrapped.measureResult.height
            override val alignmentLines: Map<AlignmentLine, Int> = emptyMap()
            override fun placeChildren() {
                with(PlacementScope) {
                    placeable.place(-apparentToRealOffset)
                }
            }
        }
        return this
    }
}  

DelegatingLayoutNodeWrapper#measure 中会一层层向包装内部传递一直传递到 InnerPlaceable,这中间如果遇到 ModifiedLayoutNode(尺寸相关的 Modifier 都会解析成 ModifiedLayoutNode)

internal class ModifiedLayoutNode(
    wrapped: LayoutNodeWrapper,
    modifier: LayoutModifier
) : DelegatingLayoutNodeWrapper<LayoutModifier>(wrapped, modifier) {

    override fun measure(constraints: Constraints): Placeable = performingMeasure(constraints) {
        with(modifier) {
            measureResult = measureScope.measure(wrapped, constraints)
            this@ModifiedLayoutNode
        }
    }
}

进而执行 Modifier#measure 方法,假设此时的 Modifier 是 SizeModifier

    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {
      	//计算最大/最小尺寸约束
        val wrappedConstraints = targetConstraints.let { targetConstraints ->
            if (enforceIncoming) {
                constraints.constrain(targetConstraints)
            } else {
                val resolvedMinWidth = if (minWidth != Dp.Unspecified) {
                    targetConstraints.minWidth
                } else {
                    constraints.minWidth.coerceAtMost(targetConstraints.maxWidth)
                }
                val resolvedMaxWidth = if (maxWidth != Dp.Unspecified) {
                    targetConstraints.maxWidth
                } else {
                    constraints.maxWidth.coerceAtLeast(targetConstraints.minWidth)
                }
                val resolvedMinHeight = if (minHeight != Dp.Unspecified) {
                    targetConstraints.minHeight
                } else {
                    constraints.minHeight.coerceAtMost(targetConstraints.maxHeight)
                }
                val resolvedMaxHeight = if (maxHeight != Dp.Unspecified) {
                    targetConstraints.maxHeight
                } else {
                    constraints.maxHeight.coerceAtLeast(targetConstraints.minHeight)
                }
                Constraints(
                    resolvedMinWidth,
                    resolvedMaxWidth,
                    resolvedMinHeight,
                    resolvedMaxHeight
                )
            }
        }
      	// measurable 是传过来的 wrapped
      	// 将约束传递到下一层 wrapped 的 measure 中
        val placeable = measurable.measure(wrappedConstraints)
        return layout(placeable.width, placeable.height) {
            placeable.placeRelative(0, 0)
        }
    }

SizeModifier 中设置的尺寸就这样转换成约束传递到下一层 wrapped 的测量中。

只有 MeasureAndLayoutDelegate#relayoutNodes 中的 LayoutNode 才会被测量

    fun measureAndLayout(): Boolean {
        require(root.isAttached)
        require(root.isPlaced)
        require(!duringMeasureLayout)
        // we don't need to measure any children unless we have the correct root constraints
        val rootConstraints = rootConstraints ?: return false

        var rootNodeResized = false
        if (relayoutNodes.isNotEmpty()) {
            duringMeasureLayout = true
            try {
                relayoutNodes.popEach { layoutNode ->

根节点 root 在 AndroidComposeView#onMeasure 时先调用 measureAndLayoutDelegate.updateRootConstraints 将 root 添加到 relayoutNodes 中再开启的测量流程

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        trace("AndroidOwner:onMeasure") {
            if (!isAttachedToWindow) {
                invalidateLayoutNodeMeasurement(root)
            }
            val (minWidth, maxWidth) = convertMeasureSpec(widthMeasureSpec)
            val (minHeight, maxHeight) = convertMeasureSpec(heightMeasureSpec)

            val constraints = Constraints(minWidth, maxWidth, minHeight, maxHeight)
            if (onMeasureConstraints == null) {
                // first onMeasure after last onLayout
                onMeasureConstraints = constraints
                wasMeasuredWithMultipleConstraints = false
            } else if (onMeasureConstraints != constraints) {
                // we were remeasured twice with different constraints after last onLayout
                wasMeasuredWithMultipleConstraints = true
            }
            measureAndLayoutDelegate.updateRootConstraints(constraints)
            measureAndLayoutDelegate.measureAndLayout()
    fun updateRootConstraints(constraints: Constraints) {
        if (rootConstraints != constraints) {
            require(!duringMeasureLayout)
            rootConstraints = constraints
            root.layoutState = NeedsRemeasure
            relayoutNodes.add(root)
        }
    }

此外 LayoutNode#requestRemeasure 也会将其添加到 relayoutNodes 中。