LoginSignup
12
7

More than 1 year has passed since last update.

Jetpack Compose の Button に無意識で確保されるスペース

Last updated at Posted at 2022-11-16

Jetpack Compose で Button 系のコンポーネントをレイアウトに詰めていると
指定してないのに謎のスペースが空いてなぜだろうとなりませんか (ならないか……)

たとえば こういうレイアウトをしてみます:

Column {
    Button(onClick = { /*TODO*/ }) {
        Text(text = "Hello")
    }
    Button(onClick = { /*TODO*/ }) {
        Text(text = "World")
    }
}

なんか微妙にスペースがあいてることに気づくかもしれません

M3 Buttons

Layout Inspector でみるとわかりやすいです:

M3 Buttons -- Layout Inspector

理由

そういえば 私たちはタッチしやすくなるように clickable な UI 要素には タップ ターゲットサイズ を考慮する必要があって、
Compose Material はバージョン 1.1.0 から 48 dp を確保してくれているからということですね:

利用可能なタップ ターゲットがないマテリアル コンポーネントに、最小サイズのタップ ターゲットを追加しました。十分な大きさのタップ ターゲットを確保するためにコンポーネント間のスペースを増やしているため、タップ ターゲットのサイズが考慮されず、コンポーネントのサイズが表示されるサイズのままであることを前提とする、既存の UI が変更される可能性があります。

タッチ ターゲットのサイズ調整

Compose 1.0 と比較した場合、マテリアル コンポーネントのレイアウトでは、マテリアルのアクセシビリティ ガイドライン(英語)に記載されているタッチ ターゲット サイズ(英語)を満たすようにレイアウト領域が拡大されます。たとえば、RadioButton のタッチ ターゲットは、最小サイズの 48x48 dp に拡大されます。RadioButton のサイズをそれ以下に設定した場合も同様です。

(ところでタップなのかタッチなのか表記が揺れていてどっちなのでしょうね)

比較

たしかに androidx.compose.material:material1.0.01.1.0 を使ってみて比較すると違いがわかります:

1.0.0 1.1.0
androidx.compose.material:material:1.0.0 androidx.compose.material:material:1.1.0

Material 3 では androidx.compose.material3:material3:1.0.0-alpha01 からこの動作ですね。
(リリースがこの変更の後だからそうだろうという感じですね)

どうしてもなくしたいとき

Composition Local の LocalMinimumTouchTargetEnforcementfalse にすることで、この配慮をなくすことができるということです。

@OptIn(ExperimentalMaterial3Api::class)
CompositionLocalProvider(LocalMinimumTouchTargetEnforcement provides false) {
    Column {
        Button(onClick = { /*TODO*/ }) {
            Text(text = "Hello")
        }
        Button(onClick = { /*TODO*/ }) {
            Text(text = "World")
        }
    }
}

M3 Buttons -- LocalMinimumTouchTargetEnforcement

それでも 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")
        }
    }
}

test20221116mintap_–MainActivity_kt__test20221116mintap_app_main.png

しかしながら…

試験運用版の LocalMinimumTouchTargetEnforcement コンポジションをローカルで使用し、階層全体でこの動作を無効にできます。ただしこれは、新しい最小サイズに対応して既存の UI を更新するまでの間、一時的なエスケープ ハッチとなることのみを目的としています。
https://developer.android.com/jetpack/androidx/releases/compose-material?hl=ja#1.1.0-alpha05

脱出ハッチ用途ということで理由がなければありのままのほうがいいでしょう (アナと雪の女王)

12
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
7