Muitos elementos combináveis 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 os elementos combináveis. Esta página se concentra em gestos que envolvem um único ponteiro, em que a posição desse ponteiro não é significativa para o processamento desse evento. A tabela a seguir lista esses tipos de gestos:
Gesto |
Descrição |
Toque (ou clique) |
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 cursor desce. |
Responder a toques ou cliques
clickable
é um modificador usado com frequência que faz com que um elemento combinável reaja 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
apenas com o mouse ou o dedo, mas também com eventos de clique por entrada de teclado ou ao
usar serviços de acessibilidade.
Imagine uma grade de imagens, em que uma imagem aparece em tela cheia quando um usuário clicar 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:
interactionSource
eindication
, que desenham uma ondulação por padrão quando um usuário toca no elemento combinável. Saiba como personalizar essas informações na página Como lidar com interações do usuário.- Permite que os serviços de acessibilidade interajam com o elemento definindo as informações semânticas.
- Oferece suporte à interação com teclado ou joystick, permitindo o foco e a pressão
Enter
ou o centro do botão direcional para interagir. - Torne o elemento acionável para que ele responda ao mouse ou à stylus que passa por cima dele.
Toque e pressione para mostrar um menu de contexto contextual
combinedClickable
permite adicionar o comportamento de toque duplo ou pressionar e manter, além do comportamento de clique normal. É possível usar combinedClickable
para mostrar um
menu de contexto quando o usuário toca e pressiona 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, inclua o feedback tátil quando o usuário
pressionar um elemento por muito tempo. É por isso que o snippet inclui a
invocação performHapticFeedback
.
Dispensar um elemento combinável tocando em uma tela de bloqueio
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 foco, teclado e suporte à 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 poder tocar nele para dispensar a tela de detalhes:
Nesse caso, esse 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 focado e a
resposta dele a eventos de teclado e acessibilidade é diferente da de um
combinável típico. Em vez de tentar adaptar o comportamento clickable
, você pode ir
para 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 a chave do modificador pointerInput
, você transmite a lambda onClose
. Isso
reexecuta automaticamente o lambda, garantindo que o callback correto seja chamado
quando o usuário tocar na tela escura.
Toque duas vezes para aplicar 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 o zoom na imagem tocando duas vezes:
Como você pode ver no vídeo, o zoom ocorre em torno da posição do evento de toque. O resultado é diferente quando aumentamos o zoom na 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 de toque ao
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