图标按钮

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

图标按钮分为两种类型:

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

API Surface

使用 IconButton 可组合项实现标准图标按钮。如需创建不同的视觉样式(例如填充、填充色调或轮廓),请分别使用 FilledIconButton、FilledTonalIconButton 和 OutlinedIconButton。

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 状态在配置更改(例如屏幕旋转)后保持不变。
  • IconButton 的 onClick lambda 定义了按钮在被点击时的行为,即在 true 和 false 之间切换状态。
  • Icon 可组合项的 painter 参数会根据 isToggled 状态有条件地加载不同的 painterResource。这会更改图标的视觉外观。
    • 如果 isToggled 为 true,则会加载实心心形可绘制对象。
    • 如果 isToggled 为 false,则会加载空心心形可绘制对象。
  • Icon 的 contentDescription 也会根据 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。当 isPressed 为 true 时,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 可组合项,用于构建一个可递增和递减计数器的界面。
  • 它使用 remember 和 mutableIntStateOf 维护一个 pressedCount 可变状态变量,该变量会初始化为 0。当 pressedCount 发生变化时,观察它的任何可组合项(例如 Text 可组合项)都会重组,以反映新值。
  • 第一个 MomentaryIconButton 在点击或按住时会减少 pressedCount。
  • 第二个 MomentaryIconButton 在点击或按住时会增加 pressedCount。
  • 两个按钮都使用 100 毫秒的 stepDelay,这意味着在按住按钮时,onClick 操作会每 100 毫秒重复一次。

结果

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

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

其他资源