アイコンボタン

アイコンボタンは、ユーザーが実行できるアクションを表示します。アイコンボタンには、明確な意味を持つアイコンを使用する必要があります。通常は、一般的または頻繁に使用されるアクションを表します。

アイコンボタンには次の 2 種類があります。

  • デフォルト: これらのボタンは、メニューや検索などの他の要素を開くことができます。
  • 切り替え: 「お気に入り」や「ブックマーク」など、オンとオフを切り替えることができるバイナリ操作を表すボタンです。
5 つのアイコンボタン(設定、その他など)。一部は塗りつぶされて選択を示し、一部はアウトライン表示されています。
図 1. アイコンボタン。一部は塗りつぶされ(選択されていることを示す)、一部は枠線のみ。

API サーフェス

標準のアイコンボタンを実装するには、IconButton コンポーザブルを使用します。塗りつぶし、塗りつぶし(色調)、アウトラインなど、さまざまなビジュアル スタイルを作成するには、それぞれ FilledIconButton、FilledTonalIconButton、OutlinedIconButton を使用します。

IconButton の主なパラメータは次のとおりです。

  • onClick: ユーザーがアイコンボタンをタップしたときに実行されるラムダ関数。
  • enabled: ボタンの有効状態を制御するブール値。false の場合、ボタンはユーザー入力に反応しません。
  • content: ボタン内のコンポーザブル コンテンツ。通常は Icon。

基本的な例: 切り替えアイコンボタン

この例では、切り替えアイコンボタンを実装する方法を示します。切り替えアイコンボタンは、選択されているかどうかに基づいて外観が変わります。

@Preview
@Composable
fun ToggleIconButtonExample() {
    // isToggled initial value should be read from a view model or persistent storage.
    var isToggled by rememberSaveable { mutableStateOf(false) }

    IconButton(
        onClick = { isToggled = !isToggled }
    ) {
        Icon(
            painter = if (isToggled) painterResource(R.drawable.favorite_filled) else painterResource(R.drawable.favorite),
            contentDescription = if (isToggled) "Selected icon button" else "Unselected icon button."
        )
    }
}

コードに関する主なポイント

  • ToggleIconButtonExample コンポーザブルは、切り替え可能な IconButton を定義します。
    • mutableStateOf(false) は、ブール値を保持する MutableState オブジェクトを作成します。初期値は false です。これにより、isToggled が状態ホルダーになり、値が変更されるたびに Compose が UI を再コンポーズします。
    • rememberSaveable を使用すると、画面の回転などの構成変更後も isToggled の状態が維持されます。
  • IconButton の onClick ラムダは、クリックされたときのボタンの動作を定義し、true と false の間で状態を切り替えます。
  • Icon コンポーザブルの painter パラメータは、isToggled 状態に基づいて、異なる painterResource を条件付きで読み込みます。これにより、アイコンの外観が変更されます。
    • isToggled が true の場合は、塗りつぶされたハートのドローアブルを読み込みます。
    • isToggled が false の場合は、輪郭線付きのハートのドローアブルを読み込みます。
  • Icon の contentDescription も、isToggled の状態に基づいて更新され、適切なユーザー補助情報が提供されます。

結果

次の画像は、前のスニペットの切り替えアイコンボタンが選択されていない状態を示しています。

未選択状態(塗りつぶされていない)のお気に入り切り替えアイコンボタン(ハート)。
図 2. 未選択状態の「お気に入り」切り替えアイコンボタン。

高度な例: 押したときにアクションを繰り返す

このセクションでは、クリックごとに 1 回だけアクションをトリガーするのではなく、ユーザーが長押ししている間、アクションを継続的にトリガーするアイコンボタンを作成する方法について説明します。

@Composable
fun MomentaryIconButton(
    unselectedImage: Int,
    selectedImage: Int,
    contentDescription: String,
    modifier: Modifier = Modifier,
    stepDelay: Long = 100L, // Minimum value is 1L milliseconds.
    onClick: () -> Unit
) {
    val interactionSource = remember { MutableInteractionSource() }
    val isPressed by interactionSource.collectIsPressedAsState()
    val pressedListener by rememberUpdatedState(onClick)

    LaunchedEffect(isPressed) {
        while (isPressed) {
            delay(stepDelay.coerceIn(1L, Long.MAX_VALUE))
            pressedListener()
        }
    }

    IconButton(
        modifier = modifier,
        onClick = onClick,
        interactionSource = interactionSource
    ) {
        Icon(
            painter = if (isPressed) painterResource(id = selectedImage) else painterResource(id = unselectedImage),
            contentDescription = contentDescription,
        )
    }
}

コードに関する主なポイント

  • MomentaryIconButton は、unselectedImage: Int(ボタンが押されていないときのアイコンのドローアブル リソース ID)と selectedImage: Int(ボタンが押されたときのアイコンのドローアブル リソース ID)を受け取ります。
  • interactionSource を使用して、ユーザーからの「押す」操作を具体的に追跡します。
  • isPressed は、ボタンが実際に押されている場合は true、それ以外の場合は false です。isPressed が true の場合、LaunchedEffect はループに入ります。
    • このループ内では、delay(stepDelay を使用)を使用して、アクションのトリガー間に一時停止を作成します。coerceIn は、無限ループを防ぐために遅延が 1 ミリ秒以上になるようにします。
    • pressedListener は、ループ内の各遅延の後に呼び出されます。これにより、アクションが繰り返されます。
  • pressedListener は rememberUpdatedState を使用して、onClick ラムダ(実行するアクション)が常に最新のコンポジションから最新の状態になるようにします。
  • Icon は、ボタンが現在押されているかどうかに基づいて、表示される画像を変更します。
    • isPressed が true の場合、selectedImage が表示されます。
    • それ以外の場合は、unselectedImage が表示されます。

次に、この MomentaryIconButton を例で使用します。次のスニペットは、カウンタを制御する 2 つのアイコンボタンを示しています。

@Preview()
@Composable
fun MomentaryIconButtonExample() {
    var pressedCount by remember { mutableIntStateOf(0) }

    Row(
        modifier = Modifier.fillMaxWidth(),
        verticalAlignment = Alignment.CenterVertically
    ) {
        MomentaryIconButton(
            unselectedImage = R.drawable.fast_rewind,
            selectedImage = R.drawable.fast_rewind_filled,
            stepDelay = 100L,
            onClick = { pressedCount -= 1 },
            contentDescription = "Decrease count button"
        )
        Spacer(modifier = Modifier)
        Text("advanced by $pressedCount frames")
        Spacer(modifier = Modifier)
        MomentaryIconButton(
            unselectedImage = R.drawable.fast_forward,
            selectedImage = R.drawable.fast_forward_filled,
            contentDescription = "Increase count button",
            stepDelay = 100L,
            onClick = { pressedCount += 1 }
        )
    }
}

コードに関する主なポイント

  • MomentaryIconButtonExample コンポーザブルは、2 つの MomentaryIconButton インスタンスを含む Row と、カウンタを増減する UI を構築する Text コンポーザブルを表示します。
  • remember と mutableIntStateOf を使用して pressedCount 可変状態変数を保持し、0 に初期化します。pressedCount が変更されると、それを監視しているコンポーザブル(Text コンポーザブルなど)が再コンポーズされ、新しい値が反映されます。
  • 最初の MomentaryIconButton は、クリックまたは長押しすると pressedCount を減らします。
  • 2 つ目の MomentaryIconButton は、クリックまたは長押しすると pressedCount を増やします。
  • どちらのボタンも stepDelay が 100 ミリ秒に設定されているため、ボタンを長押ししている間、onClick アクションが 100 ミリ秒ごとに繰り返されます。

結果

次の動画は、アイコンボタンとカウンタを含む UI を示しています。

図 3. カウンタを増減する 2 つのアイコンボタン(プラスとマイナス)を備えたカウンタ UI。

参考情報