Многие компоненты поддерживают нажатия и клики и включают лямбда-выражение onClick. Например, вы можете создать кликабельный элемент Surface, который включает все элементы Material Design, подходящие для взаимодействия с поверхностями:
Surface(onClick = { /* handle click */ }) { Text("Click me!", Modifier.padding(24.dp)) }
Однако клики – не единственный способ взаимодействия пользователя с элементами интерфейса. На этой странице рассматриваются жесты, выполняемые одним указателем, положение которого не имеет значения для обработки события. В таблице ниже перечислены эти типы жестов.
Жест |
Описание |
Нажмите |
Указатель перемещается вниз, а затем вверх |
Двойное касание |
Указатель перемещается вниз, вверх, вниз, вверх |
Нажать и удерживать |
Указатель опускается и удерживается дольше |
Пресса |
Указатель опускается |
реагировать на нажатие или клик;
clickable – часто используемый модификатор, который позволяет composable-функции реагировать на нажатия или клики. Этот модификатор также добавляет дополнительные функции, такие как поддержка фокуса, наведения указателя мыши и стилуса, а также настраиваемый визуальный индикатор при нажатии. Модификатор реагирует на "клики" в широком смысле этого слова – не только на нажатия мыши или пальца, но и на события клика при вводе с клавиатуры или использовании сервисов специальных возможностей.
Представьте себе сетку изображений, где изображение показывается в полноэкранном режиме, когда пользователь нажимает на него:
Чтобы реализовать такое поведение, добавьте модификатор clickable к каждому элементу сетки:
@Composable private fun ImageGrid(photos: List<Photo>) { var activePhotoId by rememberSaveable { mutableStateOf<Int?>(null) } LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) { items(photos, { it.id }) { photo -> ImageItem( photo, Modifier.clickable { activePhotoId = photo.id } ) } } if (activePhotoId != null) { FullScreenImage( photo = photos.first { it.id == activePhotoId }, onDismiss = { activePhotoId = null } ) } }
Модификатор clickable также добавляет следующие возможности:
interactionSourceиindication, которые по умолчанию рисуют волну, когда пользователь нажимает на компонент. Подробнее о том, как настроить эти параметры, рассказывается на странице Обработка действий пользователя.- Позволяет службам специальных возможностей взаимодействовать с элементом, задавая семантическую информацию.
- Поддерживает взаимодействие с помощью клавиатуры или джойстика, позволяя выбирать элементы и нажимать
Enterили центр d-pad. - Сделайте элемент интерактивным, чтобы он реагировал на наведение на него курсора мыши или стилуса.
Нажмите и удерживайте, чтобы открыть контекстное меню
combinedClickable позволяет добавить поведение при двойном нажатии или долгом нажатии в дополнение к обычному нажатию. Вы можете использовать combinedClickable, чтобы показывать контекстное меню, когда пользователь нажимает на изображение в сетке и удерживает его:
var contextMenuPhotoId by rememberSaveable { mutableStateOf<Int?>(null) } val haptics = LocalHapticFeedback.current LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) { items(photos, { it.id }) { photo -> ImageItem( photo, Modifier .combinedClickable( onClick = { activePhotoId = photo.id }, onLongClick = { haptics.performHapticFeedback(HapticFeedbackType.LongPress) contextMenuPhotoId = photo.id }, onLongClickLabel = stringResource(R.string.open_context_menu) ) ) } } if (contextMenuPhotoId != null) { PhotoActionsSheet( photo = photos.first { it.id == contextMenuPhotoId }, onDismissSheet = { contextMenuPhotoId = null } ) }
Рекомендуется добавлять виброотклик при долгом нажатии на элементы. Именно поэтому в фрагменте кода есть вызов performHapticFeedback.
Как закрыть компонент, нажав на экран
В приведенных выше примерах clickable и combinedClickable добавляют полезные функции в ваши композиции. Они визуально реагируют на взаимодействие, отвечают на наведение указателя и поддерживают фокус, клавиатуру и специальные возможности. Однако такое поведение не всегда желательно.
Давайте рассмотрим экран сведений об изображении. Фон должен быть полупрозрачным, и пользователь должен иметь возможность закрыть экран с подробной информацией, нажав на него:
В этом случае фон не должен иметь никаких визуальных индикаторов взаимодействия, не должен реагировать на наведение указателя, не должен быть фокусируемым и должен реагировать на события клавиатуры и специальные возможности иначе, чем обычная composable-функция. Вместо того чтобы пытаться адаптировать поведение clickable, можно перейти на более низкий уровень абстракции и напрямую использовать модификатор pointerInput в сочетании с методом detectTapGestures:
@Composable private fun Scrim(onClose: () -> Unit, modifier: Modifier = Modifier) { val strClose = stringResource(R.string.close) Box( modifier // handle pointer input .pointerInput(onClose) { detectTapGestures { onClose() } } // handle accessibility services .semantics(mergeDescendants = true) { contentDescription = strClose onClick { onClose() true } } // handle physical keyboard input .onKeyEvent { if (it.key == Key.Escape) { onClose() true } else { false } } // draw scrim .background(Color.DarkGray.copy(alpha = 0.75f)) ) }
В качестве ключа модификатора pointerInput вы передаете лямбда-функцию onClose. Это автоматически повторно выполнит лямбда-функцию, чтобы при нажатии на затемненный экран вызывался правильный обратный вызов.
Нажмите дважды, чтобы увеличить
Иногда в clickable и combinedClickable недостаточно информации, чтобы правильно отреагировать на взаимодействие. Например, композируемым функциям может потребоваться доступ к позиции в границах композируемой функции, где произошло взаимодействие.
Давайте снова посмотрим на экран сведений об изображении. Рекомендуем сделать так, чтобы изображение можно было увеличить, дважды нажав на него:
Как видно на видео, увеличение происходит в месте нажатия. Результат будет разным, если увеличить левую или правую часть изображения. Мы можем использовать модификатор pointerInput в сочетании с detectTapGestures, чтобы включить позицию нажатия в расчет:
var zoomed by remember { mutableStateOf(false) } var zoomOffset by remember { mutableStateOf(Offset.Zero) } Image( painter = rememberAsyncImagePainter(model = photo.highResUrl), contentDescription = null, modifier = modifier .pointerInput(Unit) { detectTapGestures( onDoubleTap = { tapOffset -> zoomOffset = if (zoomed) Offset.Zero else calculateOffset(tapOffset, size) zoomed = !zoomed } ) } .graphicsLayer { scaleX = if (zoomed) 2f else 1f scaleY = if (zoomed) 2f else 1f translationX = zoomOffset.x translationY = zoomOffset.y } )
Рекомендуем
- Примечание. Текст ссылки показывается, когда JavaScript отключен.
- Жесты
- Material Design 2 в Compose
- Kotlin для Jetpack Compose