Коснитесь и нажмите

Многие составные элементы имеют встроенную поддержку касаний или щелчков и включают лямбду 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 :

@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
        }
)

{% дословно %} {% дословно %} {% дословно %} {% дословно %}