アイコンボタン

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

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

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

API サーフェス

標準のアイコンボタンを実装するには、IconButton コンポーザブルを使用します。塗りつぶし、塗りつぶしトーン、枠線など、さまざまな視覚スタイルを作成するには、それぞれ FilledIconButtonFilledTonalIconButtonOutlinedIconButton を使用します。

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

結果

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

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

高度な例: 押すたびに繰り返し実行されるアクション

このセクションでは、ユーザーがアイコンボタンを長押ししている間、1 回のクリックで 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 は、ボタンが押されていないときのアイコンのドローアブル リソース ID である unselectedImage: Int と、ボタンが押されたときのアイコンのドローアブル リソース ID である selectedImage: Int を受け取ります。
  • interactionSource を使用して、ユーザーからの「タップ」操作を具体的にトラッキングします。
  • isPressed は、ボタンがアクティブに押されている場合は true、それ以外の場合は false です。isPressedtrue の場合、LaunchedEffect はループに入ります。
    • このループ内で、delaystepDelay を使用)を使用して、トリガー アクション間の一時停止を作成します。coerceIn は、無限ループを防ぐために遅延が 1 ms 以上であることを確認します。
    • pressedListener は、ループ内の各遅延後に呼び出されます。これにより、アクションが繰り返されます。
  • pressedListenerrememberUpdatedState を使用して、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 インスタンスと Text コンポーザブルを含む Row を表示し、カウンタをインクリメントおよびデクリメントするための UI を構築します。
  • remembermutableIntStateOf を使用して pressedCount 可変状態変数を保持し、0 に初期化します。pressedCount が変更されると、それを監視しているコンポーザブル(Text コンポーザブルなど)は、新しい値を反映するように再コンポーズされます。
  • 最初の MomentaryIconButton は、クリックまたは長押しすると pressedCount を減らします。
  • 2 番目の MomentaryIconButton は、クリックまたは長押しすると pressedCount を増やします。
  • どちらのボタンも 100 ミリ秒の stepDelay を使用します。つまり、ボタンが押されている間、onClick アクションが 100 ミリ秒ごとに繰り返されます。

結果

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

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

参考情報