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

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

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

  • По умолчанию. Эти кнопки могут открывать другие элементы, например меню или поиск.
  • Переключатели. Эти кнопки могут представлять бинарные действия, которые можно включать или выключать, например "Избранное" или "Закладка".
Пять кнопок с разными значками (настройки, дополнительные параметры и т. д.). Некоторые из них заполнены, что указывает на выбор, а некоторые выделены контуром.
Рисунок 1. Кнопки со значками, некоторые из которых заполнены (указывает на выбор) и обведены.

API surface

Используйте composable-функцию IconButton, чтобы добавить стандартные кнопки со значками. Чтобы создать разные визуальные стили, например заполненный, заполненный тональный или контурный, используйте 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."
        )
    }
}

Важные сведения о коде

  • Composable-функция ToggleIconButtonExample определяет переключаемый IconButton.
    • mutableStateOf(false) создает объект MutableState, который содержит логическое значение, изначально false. Это делает isToggled хранилищем состояния, то есть Compose перекомпонует пользовательский интерфейс при каждом изменении его значения.
    • rememberSaveable гарантирует, что состояние isToggled сохраняется при изменении конфигурации, например при повороте экрана.
  • Лямбда-выражение onClick в IconButton определяет поведение кнопки при нажатии, переключая состояние между true и false.
  • Параметр painter composable-функции 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 }
        )
    }
}

Важные сведения о коде

  • Composable-функция MomentaryIconButtonExample отображает Row, содержащий два экземпляра MomentaryIconButton и composable-функцию Text, для создания пользовательского интерфейса, позволяющего увеличивать и уменьшать значение счетчика.
  • Она поддерживает изменяемую переменную состояния pressedCount с помощью remember и mutableIntStateOf, инициализированную значением 0. Когда pressedCount меняется, все наблюдающие за ним composable-функции (например, Text) перекомпонуются, чтобы отразить новое значение.
  • Первая кнопка (MomentaryIconButton) уменьшает значение pressedCount при нажатии или удержании.
  • Вторая кнопка MomentaryIconButton увеличивает pressedCount при нажатии или удержании.
  • Для обеих кнопок задано значение stepDelay, равное 100 миллисекундам. Это значит, что действие onClick повторяется каждые 100 миллисекунд, пока кнопка нажата.

Результат

На видео ниже показан интерфейс с кнопками-значками и счетчиком:

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

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