android: 中央に表示する (2)
2024/10/15
昨日の記事を読み返し、中央に表示することについて述べていないことに気付く。。。
ともかく、画面の中央に表示したいのであれば下地になるどこかでfillMaxSize()して全部を使えるようにするのが準備として簡単だろう、というのが前回までだ。
そしてようやく中央に表示する話ができる。
私が気になったのは、wrapContentSize()で中央寄せするのは普通なのか、ということ。
Codelabs に書いてあったのでそういうものかと思っていたが、ColumnならverticalArrangementとhorizontalAlignmentで同じことができる。
@Preview(showBackground = true)
@Composable
fun MyPreview() {
LemonadeTheme {
Surface(modifier = Modifier.fillMaxSize()) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
painter = painterResource(R.drawable.lemon_tree),
contentDescription = "lemon tree"
)
Button(onClick = {}) {
Text("Lemon Tree")
}
}
}
}
}
プレビュー画像で確認する。
左が今回、右が昨日のwrapContentSize()での場合だ。

だいたい同じだが、右の方は下にちょっとスペースが空いている。が、そのくらいだ。
そもそも、wrapContentSize()はコンテンツサイズに関する関数なのになぜ位置についても指定できるのだろうか?
Modifierなんだからalign()も使えるだろうし。
wrapContentSize()でいくつか検索した。
- Compose 修飾子 - Jetpack Compose - Android Developers
- 親からの制約を
requiredSize()で上書きする説明文に出てきた - レイアウトシステムがスペースの中央に子を配置しようとする動作を
wrapContentSize()で上書きすることができる
- 親からの制約を
- ComposeのwrapContentSize. Composeの wrapContentSize… - by Kenji Abe - Medium
wrapContentSize()の説明- 引数
modifierにwrapContentSize()を挟んでsize()を設定するとそれぞれ反映されるということに驚きを覚えた。順番も大切だし、設定したら設定しただけ積み重なるので無駄にも注意だ。
- Jetpack Compose のレイアウトまとめ2: alignment, weight, arrangement
Textの縦方向はwrapContentSize()系かBoxでしか中央に寄らないらしい
レイアウトシステムがスペースの中央に配置しようとする、というのがwrapContentSize()がデフォルトで中央寄せする挙動の元になっているのだろうか。
Columnはデフォルトで左上に配置しようとするからか、どうも中央に配置しようとする姿が思いつかないのだ。
wrapContentSize だけ使う
昨日は wrapContentSize()を使ったが horizontalAlignmentも併用していた。
wrapContentSize()だけだとどうなるか。
@Preview(showBackground = true)
@Composable
fun MyPreview() {
LemonadeTheme {
Surface(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier.wrapContentSize(),
) {
Text("なるべく長い文字列を表示")
Image(
painter = painterResource(R.drawable.lemon_tree),
contentDescription = "lemon tree",
)
Button(onClick = {}) {
Text("Lemon Tree")
}
}
}
}
}
Columnの外側だけが中央に寄せられ、中のコンテンツは左に寄っている。

horizontalAlignment を指定しない場合
wrapContentSize()を使わず verticalArrangement だけ設定するとどうなるか。
@Preview(showBackground = true)
@Composable
fun MyPreview() {
LemonadeTheme {
Surface(modifier = Modifier.fillMaxSize()) {
Column(
verticalArrangement = Arrangement.Center,
) {
Text("なるべく長い文字列を表示")
Image(
painter = painterResource(R.drawable.lemon_tree),
contentDescription = "lemon tree",
)
Button(onClick = {}) {
Text("Lemon Tree")
}
}
}
}
}
Columnの外側だけが左に寄るのではなく、全部が左に寄ってしまった。
中のコンテンツ位置については wrapContentSize() だけ使った場合と同じだ。

Columnの中と外を別々に位置揃えしたいなら、間にもう1つ Column を挟んでコンテンツが水平方向にまとまった部品を作れば良い。
例えばこうすると、全体は左寄せ、中身は中央寄せになる。
@Preview(showBackground = true)
@Composable
fun MyPreview() {
LemonadeTheme {
Surface(modifier = Modifier.fillMaxSize()) {
Column(
verticalArrangement = Arrangement.Center,
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text("なるべく長い文字列を表示")
Image(
painter = painterResource(R.drawable.lemon_tree),
contentDescription = "lemon tree",
)
Button(onClick = {}) {
Text("Lemon Tree")
}
}
}
}
}
}

結局どうなのよ
Columnだけでしか見ていないが、こうだった。
Modifier.wrapContentSize()はColumnの垂直方向と水平方向に影響を及ぼすが、Columnの中のコンテンツ位置については影響しないようだverticalArrangementは 垂直方向に影響を及ぼすhorizontalAlignmentはColumnの水平方向に影響を及ぼすし、中のコンテンツ位置にも影響がある
今のところ、どっちかじゃないとできないというものはない。
wrapContentSize()だけ設定したのと同じ効果を得ようとしたら Column の外に Column を置いて水平方向だけ制御してもらうことになる、くらいか。
個人的には wrapContentSize() と言われてもピンとこないので、水平方向と垂直方向を操作しそうな verticalArrangement と horizontalAlignment を使うのがいいかと思っている。
が、その意見も実装し続けていたら変わりそうな気がする。
おまけ
呼び出せない Modifier.align() ?
Modifier.align()はImageには使えたがColumnでは使えなさそうだ。
同じModifierを返す関数なのになんでだ?
なるほど、細かいところは読んでいないが直接の型チェック以外にスコープという要素があるのか。
- Compose でのスコープの安全性
-
Compose には、特定のコンポーザブルの子に適用される場合にのみ使用できる修飾子があります。Compose はカスタム スコープによって、これを可能にしています。
-
先ほど Image では Modifier.align()が使えたと書いたが、あれはColumnの中にImageがあったので使えたのだった。
なので、先ほど Column では使えなかったと書いた箇所を Columnで囲むと・・・ほら使えた。
なるほどねー。
importするやつが間違っているのかと思って悩んでいたのだ。
こちらはColumnScope の Modifier.align()。
interfaceなのでコメントしかないが horizontalAlignment 系、つまり横方向しか動かせないことになる。
Columnなので縦の配置をそれぞれが自由に設定されても困るわな。
Surface ?
最初に見たレイアウトのCodelabsで Surface を一番下に置くようになっていたのでそう使っていたが、そもそも Surface は何者なのか。
リファレンスページ
Material surface is the central metaphor in material design. (Google翻訳)マテリアルの表面はマテリアル デザインの中心的なメタファーです。
デザインのメタファー、とか言われてもよくわからん。
“surface” もここでは小文字で出てきたので Surface とは違うだろう。
「メタファー(metaphor)=暗喩、隠喩、たとえ」なので、要素、みたいなものだろうか。
そこにあったリンクがマテリアルデザインの “Surfaces” へのリンクだった。
そういうものがあるのね。
わかっていたけど、やはり私はデザインに興味を持っていないようだ。
センスがあるとかなしとかもあるだろうが、そもそもやる気がないのだ。
「私が見づらいのをなんとかしたい」であって「みんなが見やすいようにしたい」ではないのだ。
そういうのは他の人にお任せしたい。
ただ基本を分かっていないとデザインの人とお話しするのにお互い大変そうなので、多少はなんとかしたい気はする。
難しい
これでそろそろレイアウトも簡単なやつならやれるのでは、と思ったがそうもいかなかった。