Toque e pressione

Muitos elementos combináveis têm suporte integrado a toques ou cliques e incluem um Lambda onClick. Por exemplo, é possível criar um Surface clicável que inclui todo o comportamento do Material Design apropriado para a interação com superfícies:

Surface(onClick = { /* handle click */ }) {
    Text("Click me!", Modifier.padding(24.dp))
}

No entanto, cliques não são a única maneira de interagir com elementos combináveis. Esta página concentra-se em gestos que envolvem um único ponteiro, em que a posição esse ponteiro não é significativo para o tratamento desse evento. O seguinte lista estes tipos de gestos:

Gesto

Descrição

Toque (ou clique)

O ponteiro cai e depois para cima

Tocar duas vezes

O ponteiro vai para baixo, para cima, para baixo, para cima

Manter pressionado

O ponteiro cai e é mantido por mais tempo

Imprensa

O ponteiro aperta

Responder a um toque ou clique

clickable é um modificador usado com frequência que faz um elemento combinável reagir a toques ou cliques. Esse modificador também adiciona outros recursos, como suporte a foco, passagem do mouse e da stylus e uma indicação visual personalizável quando pressionado. O modificador responde a "cliques", no sentido mais amplo da palavra, não somente com o mouse ou dedo, mas também em eventos de clique por meio da entrada do teclado ou quando usar serviços de acessibilidade.

Imagine uma grade de imagens, em que uma imagem aparece em tela cheia quando um usuário clicar nele:

Você pode adicionar o modificador clickable a cada item na grade para implementar essa comportamento:

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

O modificador clickable também adiciona outros comportamentos:

  • interactionSource e indication, que desenham uma ondulação por padrão quando uma o usuário toca no elemento combinável. Aprenda a personalizá-los no artigo Como lidar com o de interações.
  • Permite que os serviços de acessibilidade interajam com o elemento configurando o informações semânticas.
  • Oferece suporte à interação com teclado ou joystick ao permitir foco e pressionar Enter ou o centro do botão direcional para interagir.
  • Tornar o elemento passível de passagem de cursor para que ele responda ao cursor do mouse ou da stylus sobre ele.

Toque e mantenha pressionado para mostrar um menu de contexto

O combinedClickable permite adicionar o comportamento de tocar duas vezes ou tocar e manter pressionado além do comportamento de clique normal. Você pode usar combinedClickable para mostrar no menu de contexto quando um usuário toca e mantém uma imagem de grade:

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

Como prática recomendada, você deve incluir o retorno tátil quando o usuário toca e mantém os elementos pressionados, e é por isso que o snippet inclui o Invocação performHapticFeedback.

Dispensar um elemento combinável tocando em um scrim

Nos exemplos acima, clickable e combinedClickable adicionam valores úteis aos elementos combináveis. Eles mostram uma indicação visual sobre a interação, respondem ao movimento do cursor e incluem foco, teclado e suporte à acessibilidade. Mas esse comportamento extra nem sempre é desejável.

Vamos analisar a tela de detalhes da imagem. O plano de fundo precisa ser semitransparente e o usuário poderá tocar nesse plano de fundo para dispensar a tela de detalhes:

Nesse caso, esse fundo não deve ter nenhuma indicação visual sobre de entrada, não deve responder ao passar o cursor, não deve ser focalizável e sua resposta a eventos de teclado e acessibilidade diferente de uma resposta combinável. Em vez de tentar adaptar o comportamento da clickable, você pode soltar para um nível de abstração inferior e usar diretamente o modificador pointerInput em combinação com o método 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))
    )
}

Como a chave do modificador pointerInput, transmita a lambda onClose. Isso executa novamente a lambda de maneira automática, garantindo que o callback certo seja chamado. quando o usuário toca no scrim.

Toque duas vezes para ampliar

Às vezes, clickable e combinedClickable não incluem informações suficientes de responder à interação da maneira correta. Por exemplo, os elementos combináveis podem precisam de acesso à posição dentro dos limites do elemento combinável em que a interação o incidente.

Vamos analisar novamente a tela de detalhes da imagem. Uma prática recomendada é torná-la possível aumentar o zoom na imagem tocando duas vezes:

Como você pode ver no vídeo, o zoom ocorre próximo à posição do toque evento. O resultado é diferente quando aumentamos o zoom na parte esquerda da imagem em vez da parte certa. Podemos usar o modificador pointerInput em combinação com o detectTapGestures para incorporar a posição de toque à nossa cálculo:

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