美文网首页
Jetpack Compose-自定义布局

Jetpack Compose-自定义布局

作者: zcwfeng | 来源:发表于2022-09-30 11:15 被阅读0次

在Compose中,提到布局,第一个想到的肯定是Modifier。利用Kotlin的扩展函数,像一个解剖刀一样,扩展功能,达到自定义布局的效果。

核心思想

在界面树中布置每个节点的过程分为三个步骤。每个节点必须:

  • 测量所有子项
  • 确定自己的尺寸
  • 放置其子项

核心方法Layout

  • Messure Children
  • Desice own size
  • Place Children

Compose 界面不允许多遍测量。这意味着,布局元素不能为了尝试不同的测量配置而多次测量任何子元素。

如何理解:
只能在测量和布局传递期间测量布局,并且只能在布局传递期间且已进行事先测量之后才能放置子项。由于 Compose 作用域(如 MeasureScopePlacementScope),此操作在编译时强制执行。

LayoutModifier.kt

fun Modifier.layout(
    measure: MeasureScope.(Measurable, Constraints) -> MeasureResult
) = this.then(
    LayoutModifierImpl(
        measureBlock = measure,
        inspectorInfo = debugInspectorInfo {
            name = "layout"
            properties["measure"] = measure
        }
    )
)

private class LayoutModifierImpl(
    val measureBlock: MeasureScope.(Measurable, Constraints) -> MeasureResult,
    inspectorInfo: InspectorInfo.() -> Unit,
) : LayoutModifier, InspectorValueInfo(inspectorInfo) {
    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ) = measureBlock(measurable, constraints)

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        val otherModifier = other as? LayoutModifierImpl ?: return false
        return measureBlock == otherModifier.measureBlock
    }

    override fun hashCode(): Int {
        return measureBlock.hashCode()
    }

    override fun toString(): String {
        return "LayoutModifierImpl(measureBlock=$measureBlock)"
    }
}

interface MeasureResult {
    val width: Int
    val height: Int
    val alignmentLines: Map<AlignmentLine, Int>
    fun placeChildren()
}

官网的例子,麻雀虽小五脏俱全

fun Modifier.firstBaselineToTop(
    firstBaselineToTop: Dp
) = layout { measurable, constraints ->
    // Measure the composable
    val placeable = measurable.measure(constraints)

    // Check the composable has a first baseline
    check(placeable[FirstBaseline] != AlignmentLine.Unspecified)
    val firstBaseline = placeable[FirstBaseline]

    // Height of the composable with padding - first baseline
    val placeableY = firstBaselineToTop.roundToPx() - firstBaseline
    val height = placeable.height + placeableY
    layout(placeable.width, height) {
        // Where the composable gets placed
        placeable.placeRelative(0, placeableY)
    }
}

按照这个套路去做简单的自定义布局应该没问题了。

扩展一个组合的自定义布局

@Composable
fun MyBasicColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->
        // Don't constrain child views further, measure them with given constraints
        // List of measured children
        val placeables = measurables.map { measurable ->
            // Measure each children
            measurable.measure(constraints)
        }

        // Set the size of the layout as big as it can
        layout(constraints.maxWidth, constraints.maxHeight) {
            // Track the y co-ord we have placed children up to
            var yPosition = 0

            // Place children in the parent layout
            placeables.forEach { placeable ->
                // Position item on the screen
                placeable.placeRelative(x = 0, y = yPosition)

                // Record the y co-ord placed up to
                yPosition += placeable.height
            }
        }
    }
}


@Composable
fun CallingComposable(modifier: Modifier = Modifier) {
    MyBasicColumn(modifier.padding(8.dp)) {
        Text("MyBasicColumn")
        Text("places items")
        Text("vertically.")
        Text("We've done it by hand!")
    }
}

传入多个布局参数,然后遍历布局参数,每个布局参数在测量,最后便利安放Children

布局方向

您可以通过更改 [LocalLayoutDirection]来更改可组合项的布局方向。

如果您要将可组合项手动放置在屏幕上,则 LayoutDirectionlayout 修饰符或 Layout 可组合项的 LayoutScope 的一部分。

使用 layoutDirection 时,应使用 [place]放置可组合项。与 [placeRelative] 方法不同,place 不会根据布局方向(从左到右与从右到左)发生变化。

【⚠️注:layoutDirection与place配合使用,遇到在研究】

相关文章

网友评论

      本文标题:Jetpack Compose-自定义布局

      本文链接:https://www.haomeiwen.com/subject/fpmlartx.html