Muitos elementos de composição têm suporte integrado para toques ou cliques e incluem uma
lambda onClick. Por exemplo, é possível criar um Surface clicável que
inclua todo o comportamento do Material Design adequado para interação com superfícies:
Surface(onClick = { /* handle click */ }) { Text("Click me!", Modifier.padding(24.dp)) }
No entanto, os cliques não são a única maneira de um usuário interagir com elementos que podem ser compostos. Esta página se concentra em gestos que envolvem um único ponteiro, em que a posição dele não é significativa para o processamento do evento. A tabela a seguir lista esses tipos de gestos:
Gesto |
Descrição |
Toque (ou clique) em |
O ponteiro desce e depois sobe |
Tocar duas vezes |
O ponteiro vai para baixo, para cima, para baixo, para cima |
Manter pressionado |
O ponteiro desce e é mantido por mais tempo |
Imprensa |
O ponteiro desce |
Responder ao 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 para
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
apenas com o mouse ou o dedo, mas também a eventos de clique por entrada de teclado ou ao
usar serviços de acessibilidade.
Imagine uma grade de imagens, em que uma imagem é mostrada em tela cheia quando um usuário clica nela:
É possível adicionar o modificador clickable a cada item na grade para implementar esse
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 outro comportamento:
interactionSourceeindication, que desenham uma ondulação por padrão quando um usuário toca no elemento combinável. Saiba como personalizá-los na página Como processar interações do usuário.- Permite que os serviços de acessibilidade interajam com o elemento definindo as informações de semântica.
- Oferece suporte à interação com teclado ou joystick, permitindo o foco e o pressionamento de
Enterou do centro do botão direcional para interagir. - Torne o elemento flutuante para que ele responda ao mouse ou à stylus passando por cima dele.
Toque e pressione 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. Use combinedClickable para mostrar um
menu de contexto quando um usuário tocar e manter pressionada uma imagem da 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, inclua uma resposta tátil quando o usuário
tocar e manter pressionados os elementos. Por isso, o snippet inclui a
invocação performHapticFeedback.
Dispensar um elemento combinável tocando em uma tela de fundo
Nos exemplos acima, clickable e combinedClickable adicionam funcionalidades
úteis aos elementos combináveis. Eles mostram uma indicação visual na interação,
respondem ao passar o cursor e incluem suporte para foco, teclado e acessibilidade. No entanto,
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 precisa conseguir tocar nele para dispensar a tela de detalhes:
Nesse caso, o plano de fundo não pode ter nenhuma indicação visual na
interação, não pode responder ao passar o cursor, não pode ser focalizável e a
resposta a eventos de teclado e acessibilidade é diferente da de um
combinável típico. Em vez de tentar adaptar o comportamento de clickable, você pode usar um nível de abstração mais baixo 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 chave do modificador pointerInput, transmita a lambda onClose. Isso
reexecuta automaticamente o lambda, garantindo que o callback correto seja chamado
quando o usuário tocar na tela.
Toque duas vezes para aumentar o zoom
Às vezes, clickable e combinedClickable não incluem informações suficientes
para responder à interação da maneira correta. Por exemplo, os elementos combináveis podem
precisar de acesso à posição dentro dos limites do elemento combinável em que a interação
ocorreu.
Vamos analisar a tela de detalhes da imagem novamente. Uma prática recomendada é permitir que o usuário amplie a imagem com um toque duplo:
Como você pode ver no vídeo, o zoom ocorre ao redor da posição do evento de
toque. O resultado é diferente quando ampliamos a parte esquerda da imagem
em comparação com a parte direita. Podemos usar o modificador pointerInput em combinação
com o detectTapGestures para incorporar a posição do toque ao nosso
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 } )
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Noções básicas sobre gestos
- Material Design 2 no Compose
- Kotlin para Jetpack Compose