Jetpack Compose で Button
系のコンポーネントをレイアウトに詰めていると
指定してないのに謎のスペースが空いてなぜだろうとなりませんか (ならないか……)
たとえば こういうレイアウトをしてみます:
Column {
Button(onClick = { /*TODO*/ }) {
Text(text = "Hello")
}
Button(onClick = { /*TODO*/ }) {
Text(text = "World")
}
}
なんか微妙にスペースがあいてることに気づくかもしれません
Layout Inspector でみるとわかりやすいです:
理由
そういえば 私たちはタッチしやすくなるように clickable な UI 要素には タップ ターゲットサイズ を考慮する必要があって、
Compose Material はバージョン 1.1.0 から 48 dp を確保してくれているからということですね:
利用可能なタップ ターゲットがないマテリアル コンポーネントに、最小サイズのタップ ターゲットを追加しました。十分な大きさのタップ ターゲットを確保するためにコンポーネント間のスペースを増やしているため、タップ ターゲットのサイズが考慮されず、コンポーネントのサイズが表示されるサイズのままであることを前提とする、既存の UI が変更される可能性があります。
タッチ ターゲットのサイズ調整
Compose 1.0 と比較した場合、マテリアル コンポーネントのレイアウトでは、マテリアルのアクセシビリティ ガイドライン(英語)に記載されているタッチ ターゲット サイズ(英語)を満たすようにレイアウト領域が拡大されます。たとえば、RadioButton のタッチ ターゲットは、最小サイズの 48x48 dp に拡大されます。RadioButton のサイズをそれ以下に設定した場合も同様です。
(ところでタップなのかタッチなのか表記が揺れていてどっちなのでしょうね)
比較
たしかに androidx.compose.material:material
の 1.0.0
と 1.1.0
を使ってみて比較すると違いがわかります:
1.0.0 | 1.1.0 |
---|---|
![]() |
![]() |
Material 3 では androidx.compose.material3:material3:1.0.0-alpha01
からこの動作ですね。
(リリースがこの変更の後だからそうだろうという感じですね)
どうしてもなくしたいとき
Composition Local の LocalMinimumTouchTargetEnforcement
を false
にすることで、この配慮をなくすことができるということです。
@OptIn(ExperimentalMaterial3Api::class)
CompositionLocalProvider(LocalMinimumTouchTargetEnforcement provides false) {
Column {
Button(onClick = { /*TODO*/ }) {
Text(text = "Hello")
}
Button(onClick = { /*TODO*/ }) {
Text(text = "World")
}
}
}
それでも Button
は通常最低 36 dp の高さが取られるということで、意識しないと異常に小さくすることはできないでしょう:
これも Note that you can override it by applying Modifier.heightIn directly on [Button].
って書いてあるように heightIn
でむりやり小さくはできる…
val buttonPaddingValues = PaddingValues(all = 0.dp)
val buttonModifier = Modifier.heightIn(min = 0.dp, max = 12.dp)
@OptIn(ExperimentalMaterial3Api::class)
CompositionLocalProvider(LocalMinimumTouchTargetEnforcement provides false) {
Column {
Button(
contentPadding = buttonPaddingValues,
modifier = buttonModifier,
onClick = { /*TODO*/ },
) {
Text(text = "Hello")
}
Button(
contentPadding = buttonPaddingValues,
modifier = buttonModifier,
onClick = { /*TODO*/ },
) {
Text(text = "World")
}
}
}
しかしながら…
試験運用版の
LocalMinimumTouchTargetEnforcement
コンポジションをローカルで使用し、階層全体でこの動作を無効にできます。ただしこれは、新しい最小サイズに対応して既存の UI を更新するまでの間、一時的なエスケープ ハッチとなることのみを目的としています。
https://developer.android.com/jetpack/androidx/releases/compose-material?hl=ja#1.1.0-alpha05
脱出ハッチ用途ということで理由がなければありのままのほうがいいでしょう (アナと雪の女王)