Closed4

ComposeのLayoutを調べてみる

mtkw0127mtkw0127

インターフェースはこんな感じ

@UiComposable
@Composable
inline fun Layout(
    content: @Composable @UiComposable () -> Unit,
    modifier: Modifier = Modifier,
    measurePolicy: MeasurePolicy
) {

何に使われているか

ColumnとかRowとかの内部で使われている。

@Composable
inline fun Column(
    modifier: Modifier = Modifier,
    verticalArrangement: Arrangement.Vertical = Arrangement.Top,
    horizontalAlignment: Alignment.Horizontal = Alignment.Start,
    content: @Composable ColumnScope.() -> Unit
) {
    val measurePolicy = columnMeasurePolicy(verticalArrangement, horizontalAlignment)
    Layout( // ←使われている
        content = { ColumnScopeInstance.content() },
        measurePolicy = measurePolicy,
        modifier = modifier
    )
}

MeasurePolicyを定義しておくと、contentsをそれに沿って画面に配置してくれる。

例えばRow用のMeasurePolicyはライブラリ内で定義している。
https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt;l=46?q=rowColumnMeasurePolicy&sq=

mtkw0127mtkw0127

書いてみた

@Composable
fun LayoutTest() {
    Layout(
        content = {
            Text("Hoge")
            Text("Piyo")
        },
        modifier = Modifier.fillMaxSize(),
        measurePolicy = { measurables, constraints ->
            layout(constraints.minWidth, constraints.maxHeight) {
                measurables[0].measure(constraints).place(IntOffset.Zero)
                measurables[1].measure(constraints).place(IntOffset.Zero)
            }
        }
    )
}

どんな風に描画されるか

なぜそのように描画されるか

layout(constraints.minWidth, constraints.maxHeight) {
      measurables[0].measure(constraints).place(IntOffset.Zero)
      measurables[1].measure(constraints).place(IntOffset.Zero)
}

measurable[0]は"Hoge"を表している
measurable[1]は"Piyo"を表している
place(IntOffset.Zero)で(x,y)=(0,0)に配置している。

mtkw0127mtkw0127

Alignmentで位置を計算できる

@Composable
fun LayoutTest() {
    Layout(
        content = {
            Text(
                text = "Hoge",
                modifier = Modifier
                    .background(Color.Red)
                    .wrapContentSize() // [1]
            )
            Text(
                text = "Piyo",
                modifier = Modifier
                    .background(Color.Blue)
                    .wrapContentSize() // [1]
            )
        },
        modifier = Modifier.fillMaxSize(),
        measurePolicy = { measurables, constraints ->
            layout(constraints.minWidth, constraints.maxHeight) {
                val hoge = measurables[0].measure(Constraints())// [2] 制約なし
                val hogePosition = Alignment.TopCenter.align(
                    size = IntSize(hoge.width, hoge.height), // [3] wrapoContentのサイズが得られる
                    space = IntSize(constraints.maxWidth, constraints.maxHeight),
                    layoutDirection,
                )
                hoge.place(hogePosition)

                val piyo = measurables[1].measure(Constraints())
                val piyoPosition = hogePosition.copy(y = hogePosition.y + 100) // [4] piyoの下に100だけ移動
                piyo.place(piyoPosition)
            }
        }
    )
}

このスクラップは2023/10/01にクローズされました