圖示按鈕

圖示按鈕會顯示使用者可採取的動作。圖示按鈕必須使用具有明確意義的圖示,通常代表常見或常用的動作。

圖示按鈕分為兩種:

  • 預設:這些按鈕可開啟其他元素,例如選單或搜尋功能。
  • 切換鈕:這些按鈕可代表可切換開啟或關閉的二元動作,例如「收藏」或「書籤」。
5 個圖示按鈕,每個按鈕的圖示不同 (設定、更多等)。其中有些是填滿的,表示已選取,有些則是輪廓線。
圖 1. 圖示按鈕,其中部分是實心圖示 (表示已選取) 和空心圖示。

API 介面

使用 IconButton 可組合項實作標準圖示按鈕。如要建立不同的視覺樣式 (例如填滿、填滿色調或外框),請分別使用 FilledIconButtonFilledTonalIconButtonOutlinedIconButton

IconButton 的關鍵參數包括:

  • onClick:使用者輕觸圖示按鈕時執行的 lambda 函式。
  • 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 狀態在螢幕旋轉等設定變更後持續存在。
  • IconButtononClick lambda 會定義按鈕在按下時的行為,在 truefalse 之間切換狀態。
  • Icon 可組合函式的 painter 參數會根據 isToggled 狀態,有條件地載入不同的 painterResource。這會變更圖示的視覺外觀。
    • 如果 isToggledtrue,則會載入填滿的心形可繪項目。
    • 如果 isToggledfalse,則會載入帶有輪廓的愛心可繪項目。
  • IconcontentDescription 也會根據 isToggled 狀態更新,提供適當的無障礙資訊。

結果

下圖顯示前述程式碼片段中的切換圖示按鈕,處於未選取狀態:

未選取狀態 (未填入) 的收藏切換鈕圖示 (愛心)。
圖 2. 未選取狀態的「收藏」切換圖示按鈕。

進階範例:按下按鈕時重複執行動作

本節將說明如何建立圖示按鈕,讓使用者按住按鈕時持續觸發動作,而非每次點選觸發一次。

@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。當 isPressedtrue 時,LaunchedEffect 會進入迴圈。
    • 在這個迴圈中,它會使用 delay (搭配 stepDelay) 在觸發動作之間建立暫停時間。coerceIn 可確保延遲時間至少為 1 毫秒,以免發生無限迴圈。
    • 系統會在迴圈內每次延遲後呼叫 pressedListener。這會導致動作重複執行。
  • pressedListener 會使用 rememberUpdatedState,確保 onClick lambda (要執行的動作) 一律來自最新的組合。
  • Icon 會根據按鈕目前是否按下,變更顯示的圖片。
    • 如果 isPressed 為 true,就會顯示 selectedImage
    • 否則會顯示 unselectedImage

接著,在範例中使用這個 MomentaryIconButton。以下程式碼片段示範控制計數器的兩個圖示按鈕:

@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 可組合函式會顯示 Row,其中包含兩個 MomentaryIconButton 例項和 Text 可組合函式,用於建構用來遞增和遞減計數器的 UI。
  • 它會使用 remembermutableIntStateOf 維護 pressedCount 可變動狀態變數,並將其初始化為 0。當 pressedCount 變更時,任何觀察它的可組合項 (例如 Text 可組合項) 都會重新組合,以反映新的值。
  • 第一個 MomentaryIconButton 在點選或按住時會減少 pressedCount
  • 第二個 MomentaryIconButton 在按下或按住時會增加 pressedCount
  • 兩個按鈕都使用 100 毫秒的 stepDelay,也就是說,按住按鈕時,onClick 動作會每隔 100 毫秒重複一次。

結果

以下影片顯示含有圖示按鈕和計數器的 UI:

圖 3。計數器 UI 包含兩個圖示按鈕 (加號和減號),可用來增加或減少計數器。

其他資源