android: 中央に表示する (1)
2024/10/14
Android で画面レイアウトを作っていると、コンテンツを中央に寄せるというか揃えるというかしたいことがある。
Android Codelabs ではここで出てきた(他でも出ているが)。
Modifier.fillMaxSize().wrapContentSize(Alignment.Center)
実際に揃ったので何も考えていなかったが、よく考えるとModifier.align()というのもあるしColumnにはverticalArrangementやhorizontalAlignmentもある。
どうするのがよいのだろう?
Codelabsでの設定
この Codelabs ではどうなっているか。
Surface(fillMaxSize())DiceRollerApp(Modifier.fillMaxSize().wrapContentSize(Alignment.Center))DiceWithButtonAndImage(modifier)Column(modifier, horizontalAlignment)ImageButtonText
なのでModifier.fillMaxSize().wrapContentSize(Alignment.Center)はColumnが設定することになる。
コンテンツをそれぞれ上に積むような図にするとこうなるか。

fillMaxSize() しないといけないのか
fillMaxSize()がSurfaceとColumnに出てくるが、これは毎回いるのだろうか?
試してみよう。
両方付ける
Codelabs の例に似たような構造を作る。
@Preview(showBackground = true)
@Composable
fun MyPreview() {
LemonadeTheme {
Surface(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier.fillMaxSize().wrapContentSize(Alignment.Center),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
painter = painterResource(R.drawable.lemon_tree),
contentDescription = "lemon tree"
)
Button(onClick = {}) {
Text("Lemon Tree")
}
}
}
}
}
Android Studio の Previewで確認する。

Surface にだけ付ける
Surface にだけ fillMaxSize()を付け、Columnからは外してwrapContentSize()だけにする。
@Preview(showBackground = true)
@Composable
fun MyPreview() {
LemonadeTheme {
Surface(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier.wrapContentSize(Alignment.Center),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
painter = painterResource(R.drawable.lemon_tree),
contentDescription = "lemon tree"
)
Button(onClick = {}) {
Text("Lemon Tree")
}
}
}
}
}
両方付けたときと違いはないように見える。

Column にだけ付ける
今度は逆に Columnにだけ付ける。
@Preview(showBackground = true)
@Composable
fun MyPreview() {
LemonadeTheme {
Surface(modifier = Modifier) {
Column(
modifier = Modifier.fillMaxSize().wrapContentSize(Alignment.Center),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
painter = painterResource(R.drawable.lemon_tree),
contentDescription = "lemon tree"
)
Button(onClick = {}) {
Text("Lemon Tree")
}
}
}
}
}
こちらも違いはないように見える。

全部外す
SurfaceからもColumnからも外すと、プレビューで表示するサイズが分からないためコンテンツだけになる。
@Preview(showBackground = true)
@Composable
fun MyPreview() {
LemonadeTheme {
Surface(modifier = Modifier) {
Column(
modifier = Modifier.wrapContentSize(Alignment.Center),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
painter = painterResource(R.drawable.lemon_tree),
contentDescription = "lemon tree"
)
Button(onClick = {}) {
Text("Lemon Tree")
}
}
}
}
}
おそらく実機で表示すると左上に現れるのだろう。

どうすればいいんだ!
fillMaxSizeの説明がよくわからないのだ。
実際にやっている計算を書いているのだろうけど、結果としてどうなるのかがわからない。
制約と修飾子の順序の「レイアウトフェーズの制約」辺りを理解しないと API名だけで判断していたら痛い目を見そうだ。
制約の理解は課題として残しておこう。
上の例だと関数無しにしたのでわかりづらいが、ある程度のまとまりで関数化していって、上の方はそれを呼び出して配置するのに専念するのだろう。
今回だと、Column以下が 1つの関数になっていて上側はそれをどこに配置したいかでModifierだけ指定するような感じだろうか。
見た目では同じように見えるけど、実装していって仕様変更すると苦しみそうな分野だ。