Кнопки с иконками

Кнопки-значки отображают действия, которые могут выполнять пользователи. Кнопки-значки должны использовать значок с понятным значением и, как правило, представлять общие или часто используемые действия.

Существует два типа кнопок-значков:

  • По умолчанию : эти кнопки могут открывать другие элементы, такие как меню или поиск.
  • Переключение : эти кнопки могут представлять двоичные действия, которые можно включать и выключать, например, «избранное» или «закладка».
5 кнопок-значков с различными значками (настройки, еще и т. д.). Некоторые из них заполнены, что указывает на выбор, а некоторые обведены.
Рисунок 1. Кнопки-иконки, некоторые из которых закрашены (что указывает на выбор) и обведены.

API поверхность

Используйте IconButton composable для реализации стандартных кнопок со значками. Для создания различных визуальных стилей, таких как заполненные, заполненные тонально или обведенные, используйте FilledIconButton , FilledTonalIconButton и OutlinedIconButton соответственно.

Ключевые параметры 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) создает объект MutableState , который содержит логическое значение, изначально false . Это делает isToggled держателем состояния, то есть Compose перестраивает пользовательский интерфейс всякий раз, когда его значение изменяется.
    • rememberSaveable обеспечивает сохранение состояния isToggled при изменении конфигурации, например при повороте экрана.
  • Лямбда-функция onClick элемента IconButton определяет поведение кнопки при нажатии, переключая состояние между true и false .
  • Параметр painter компонуемого Icon условно загружает другой painterResource на основе состояния isToggled . Это изменяет внешний вид значка.
    • Если isToggled имеет true , загружается закрашенное сердце.
    • Если isToggled имеет значение false , загружается выделенное сердце.
  • contentDescription Icon также обновляется в зависимости от состояния 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 , идентификатор отрисовываемого ресурса для значка, когда кнопка не нажата, и selectedImage: Int , идентификатор отрисовываемого ресурса для значка, когда кнопка нажата.
  • Он использует interactionSource для отслеживания взаимодействий пользователя с помощью «нажатий».
  • isPressed имеет значение true, когда кнопка активно нажимается, и false в противном случае. Когда isPressed имеет значение true , LaunchedEffect входит в цикл.
    • Внутри этого цикла используется delay ( stepDelay ) для создания пауз между запускаемыми действиями. coerceIn гарантирует, что задержка составит не менее 1 мс, чтобы предотвратить бесконечные циклы.
    • pressedListener вызывается после каждой задержки в цикле. Это заставляет действие повторяться.
  • pressedListener использует rememberUpdatedState , чтобы гарантировать, что лямбда-функция onClick (действие, которое нужно выполнить) всегда является самой актуальной из последней композиции.
  • 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 для создания пользовательского интерфейса для увеличения и уменьшения счетчика.
  • Он поддерживает переменную изменяемого состояния pressedCount с помощью remember и mutableIntStateOf , инициализированную значением 0. Когда pressedCount изменяется, любые компонуемые объекты, наблюдающие за ним (например, компонуемый Text ), перестраиваются, чтобы отразить новое значение.
  • Первая MomentaryIconButton уменьшает pressedCount при нажатии или удержании.
  • Вторая MomentaryIconButton увеличивает pressedCount при нажатии или удержании.
  • Обе кнопки используют stepDelay в 100 миллисекунд, что означает, что действие onClick повторяется каждые 100 мс, пока удерживается кнопка.

Результат

В следующем видео показан пользовательский интерфейс с иконками кнопок и счетчиком:

Рисунок 3. Пользовательский интерфейс счетчика с двумя кнопками-значками (плюс и минус), которые увеличивают и уменьшают счетчик.

Дополнительные ресурсы