アプリ開発備忘録

PlayStationMobile、Android、UWPの開発備忘録

【Jetpack Compose】テキストのベースラインとその他の要素の下部を合わせる

テキストのベースラインとアイコン等を下部に揃えます。
f:id:matsudamper:20210816042448p:plain

ベースラインではなくて、ただ単純に要素の下部を揃えたい場合は Row(verticalAlignment = Alignment.Bottom) で十分です。

テキストのベースラインを揃える

まずテキストのベースラインを揃えるには alignByBaseline を使用します。
この処理を追っていくと FirstBaseline というものが使用されている事がわかります。そのため、テキスト以外にもFirstBaselineを提供してあげれば揃えられる事がわかります。

1つのテキストだけサイズを変えました。
f:id:matsudamper:20210816043857p:plain

Row(
    modifier = Modifier.fillMaxWidth(),
    horizontalArrangement = Arrangement.Center,
) {
    val baseModifier = remember {
        Modifier.alignByBaseline()
    }
    Text("a", fontSize = 40.sp,modifier = baseModifier)
    Text("A", fontSize = 40.sp, modifier = baseModifier)
    Text("人", fontSize = 40.sp, modifier = baseModifier)
    Text("1", fontSize = 30.sp, modifier = baseModifier)
}

ここで alignByBaseline を抜くとこうなります。
f:id:matsudamper:20210816043844p:plain

やりたいこと

以下の Icon をBaselineに揃えます。
f:id:matsudamper:20210816044145p:plain

Row(
    modifier = Modifier.fillMaxWidth(),
    horizontalArrangement = Arrangement.Center,
) {
    val baseModifier = remember {
        Modifier.alignByBaseline()
    }
    Text("a", fontSize = 40.sp, modifier = baseModifier)
    Text("A", fontSize = 40.sp, modifier = baseModifier)
    Text("人", fontSize = 40.sp, modifier = baseModifier)
    Text("1", fontSize = 30.sp, modifier = baseModifier)
    Icon(
        modifier = Modifier
            .then(baseModifier),
        imageVector = Icons.Default.Home, contentDescription = null
    )
}

FirstBaselineを提供する

FirstBaselineはLayout中に提供できます。ModifierでLayoutを上書きする事ができるため、こちらを利用します。
FirstBaselineIconの高さ を提供します。
※LastBaselineもついでに提供します。3行のテキストなら、Topから3行目ベースラインまでの距離がLastBaselineになります。

public fun Modifier.baseline(alignment: Alignment.Vertical): Modifier {
    return then(
        layout { measurable, constraints ->
            val measued = measurable.measure(constraints)

            val baseline = -alignment.align(measued.height, 0)
            layout(
                measued.width, measued.height,
                mapOf(
                    FirstBaseline to baseline,
                    LastBaseline to baseline,
                )
            ) {
                measued.place(0, 0)
            }
        }
    )
}

Usage

Row(
    modifier = Modifier.fillMaxWidth(),
    horizontalArrangement = Arrangement.Center,
) {
    val baseModifier = remember {
        Modifier.alignByBaseline()
    }
    Text("a", fontSize = 40.sp, modifier = baseModifier)
    Text("A", fontSize = 40.sp, modifier = baseModifier)
    Text("人", fontSize = 40.sp, modifier = baseModifier)
    Text("1", fontSize = 30.sp, modifier = baseModifier)
    Icon(
        modifier = Modifier
+            .baseline(Alignment.Bottom)
            .then(baseModifier),
        imageVector = Icons.Default.Home, contentDescription = null
    )
}

もちろん、Icon自体にPaddingがあると揃わないので、Paddingが無いアイコンを使用してください。
f:id:matsudamper:20210816044825j:plain