图标按钮

图标按钮用于显示用户可以执行的操作。图标按钮必须使用含义明确的图标,通常表示常用或经常执行的操作。

图标按钮有两种类型:

  • 默认:这些按钮可以打开其他元素,例如菜单或搜索。
  • 切换:这些按钮可以表示可开启或关闭的二进制操作,例如“收藏”或“书签”。
5 个带有不同图标(设置、更多等)的图标按钮。其中一些是实心图标,表示已选中,而另一些则是轮廓图标。
图 1. 图标按钮,其中一些是填充的(表示已选中)和轮廓的。

API Surface

使用 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 都会重组界面。
    • 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 可组合项会显示一个包含两个 MomentaryIconButton 实例和一个 Text 可组合项的 Row,以构建用于递增和递减计数器的界面。
  • 它使用 remembermutableIntStateOf 维护一个初始化为 0 的 pressedCount 可变状态变量。当 pressedCount 发生变化时,观察它的所有可组合项(例如 Text 可组合项)都会重组以反映新值。
  • 第一个 MomentaryIconButton 在被点击或按住时会降低 pressedCount
  • 第二个 MomentaryIconButton 在被点击或按住时会增加 pressedCount
  • 这两个按钮的 stepDelay 均为 100 毫秒,这意味着在按住某个按钮时,onClick 操作会每 100 毫秒重复一次。

结果

以下视频展示了包含图标按钮和计数器的界面:

图 3。一个计数器界面,其中包含两个用于递增和递减计数器的图标按钮(加号和减号)。

其他资源