图标按钮

图标按钮显示用户可以执行的操作。图标按钮必须使用含义清晰的图标,并且通常代表常用或频繁执行的操作。

图标按钮有两种类型

  • 默认: 这些按钮可以打开其他元素,例如菜单或搜索。
  • 切换: 这些按钮可以表示可以开启或关闭的二元操作,例如“收藏”或“书签”。
5 icon buttons with different icons (settings, more, etc). Some are filled, indicating selection, and some are outlined.
图 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,则加载填充的心形 drawable。
    • 如果 isToggledfalse,则加载带轮廓的心形 drawable。
  • IconcontentDescription 也根据 isToggled 状态进行更新,以提供适当的无障碍信息。

结果

下图显示了前面代码段中处于未选中状态的切换图标按钮

A favorite toggle icon button (a heart) in its unselected state (unfilled).
图 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(按钮未按下时图标的 drawable 资源 ID)和一个 selectedImage: Int(按钮按下时图标的 drawable 资源 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,用于递增和递减计数器。

其他资源