Многие составные элементы имеют встроенную поддержку касаний или щелчков и включают лямбду onClick
. Например, вы можете создать кликабельную Surface
, включающую все поведение Material Design, подходящее для взаимодействия с поверхностями:
Surface(onClick = { /* handle click */ }) {
Text("Click me!", Modifier.padding(24.dp))
}
Но клики — не единственный способ взаимодействия пользователя с составными объектами. На этой странице основное внимание уделяется жестам, включающим один указатель, где положение этого указателя не имеет значения для обработки этого события. В следующей таблице перечислены эти типы жестов:
Жест | Описание |
Коснитесь (или щелкните) | Указатель опускается, а затем поднимается |
Двойное нажатие | Указатель движется вниз, вверх, вниз, вверх |
Длительное нажатие | Указатель опускается вниз и удерживается дольше |
Нажимать | Указатель опускается |
Реагировать на касание или щелчок
clickable
— это часто используемый модификатор, который заставляет компонуемый объект реагировать на нажатия или щелчки. Этот модификатор также добавляет дополнительные функции, такие как поддержка фокусировки, наведения мыши и стилуса, а также настраиваемая визуальная индикация при нажатии. Модификатор реагирует на «клики» в самом широком смысле этого слова — не только мышью или пальцем, но и на события щелчков при вводе с клавиатуры или при использовании служб специальных возможностей.
Представьте себе сетку изображений, в которой изображение отображается в полноэкранном режиме, когда пользователь нажимает на него:
Вы можете добавить 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
добавляют полезную функциональность вашим составным объектам. Они отображают визуальную индикацию взаимодействия, реагируют на наведение курсора и включают поддержку фокусировки, клавиатуры и специальных возможностей. Но такое дополнительное поведение не всегда желательно.
Давайте посмотрим на экран детализации изображения. Фон должен быть полупрозрачным, и пользователь должен иметь возможность коснуться этого фона, чтобы закрыть экран с подробными сведениями:
В этом случае этот фон не должен иметь каких-либо визуальных указаний на взаимодействие, не должен реагировать на наведение курсора, не должен быть фокусируемым, а его реакция на события клавиатуры и специальных возможностей отличается от реакции типичного составного объекта. Вместо того, чтобы пытаться адаптировать clickable
поведение, вы можете перейти на более низкий уровень абстракции и напрямую использовать модификатор pointerInput
в сочетании с методом detectTapGestures
:
@OptIn(ExperimentalComposeUiApi::class)
@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
}
)
Пока рекомендаций нет.
Попытайтесь войти в свой аккаунт Google.