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

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

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

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

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

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

Основные параметры для IconButton:

  • onClick – лямбда-функция, которая выполняется, когда пользователь нажимает кнопку со значком.
  • enabled – логическое значение, определяющее, включена ли кнопка. Когда false, кнопка не реагирует на ввод данных пользователем.
  • content – контент, который можно составить внутри кнопки, обычно Icon.

Пример 1. Кнопка с переключателем

В этом примере показано, как реализовать кнопку с переключаемым значком. Значок переключателя меняет внешний вид в зависимости от того, выбран он или нет.

@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 функции 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 меняется, все наблюдающие за ним composable-функции (например, Text) перекомпонуются, чтобы отразить новое значение.
  • Первая кнопка MomentaryIconButton уменьшает громкость pressedCount при нажатии или удерживании.
  • Второй элемент MomentaryIconButton увеличивает значение pressedCount при нажатии или удержании.
  • Обе кнопки используют stepDelay в 100 миллисекунд, то есть действие onClick повторяется каждые 100 мс, пока кнопка удерживается.

Результат

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

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

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