Muitos elementos que podem ser compostos têm suporte integrado a toques ou cliques e incluem uma
lambda onClick
. Por exemplo, você pode criar um Surface
clicável que
inclua todo o comportamento do Material Design adequado para interação com plataformas:
Surface(onClick = { /* handle click */ }) { Text("Click me!", Modifier.padding(24.dp)) }
Mas os cliques não são a única maneira de um usuário interagir com 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 abaixo lista esses tipos de gestos:
Gesto |
Description |
Toque (ou clique) |
O ponteiro desce e depois para cima |
Tocar duas vezes |
O ponteiro desce, para cima, para baixo e para cima |
manter pressionado |
O ponteiro se desce e é mantido por mais tempo |
Imprensa |
O ponteiro se desce |
Responder a toque ou clique
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 compatibilidade com
foco, passagem do mouse e 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 mouse ou dedo, mas também com eventos de clique usando a entrada do teclado ou
o uso de serviços de acessibilidade.
Imagine uma grade de imagens, em que uma delas aparece em tela cheia quando um usuário clica nela:
Você pode adicionar o modificador clickable
a cada item da 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 outros comportamentos:
interactionSource
eindication
, 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.
- Tem suporte à interação por teclado ou joystick, permitindo o foco e pressionando
Enter
ou o centro do botão direcional para interagir. - Deixe o elemento flutuante para que ele responda ao cursor do mouse ou da stylus sobre ele.
Toque e mantenha pressionado para mostrar um menu de contexto contextual
A combinedClickable
permite adicionar o comportamento de tocar duas vezes ou tocar e manter pressionado,
além do comportamento normal de clique. Você pode usar combinedClickable
para mostrar um
menu de contexto quando um usuário toca e mantém pressionada 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 um retorno tátil quando o usuário
mantiver pressionado os elementos. É por isso que o snippet inclui a
invocação performHapticFeedback
.
Toque em um scrim para dispensar um elemento combinável
Nos exemplos acima, clickable
e combinedClickable
adicionam funcionalidades
úteis aos elementos combináveis. Eles mostram uma indicação visual sobre a 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 segundo plano 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 precisa ter nenhuma indicação visual sobre
a interação, não pode responder ao movimento do cursor, não pode ser focalizável e a
resposta dele aos eventos de teclado e acessibilidade é diferente da resposta de um elemento
combinável típico. Em vez de tentar adaptar o comportamento do clickable
, você pode usar uma lista suspensa
para um nível de abstração mais baixo e usar diretamente o modificador pointerInput
em combinação com o método 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)) ) }
Como chave do modificador pointerInput
, transmita a lambda onClose
. Isso
executa automaticamente o lambda, garantindo que o callback correto seja chamado
quando o usuário toca no scrim.
Toque duas vezes para aplicar zoom
Às vezes, clickable
e combinedClickable
não incluem informações suficientes
para responder à interação da maneira correta. Por exemplo, elementos combináveis podem
precisar de acesso à posição dentro dos limites do elemento em que a interação
ocorreu.
Vamos analisar a tela de detalhes da imagem de novo. Uma prática recomendada é permitir aumentar o zoom na imagem tocando duas vezes:
Como mostrado no vídeo, o zoom é aumentado na 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 detectTapGestures
para incorporar a posição de toque no
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.
- Entender os gestos
- Material Design 2 no Compose
- Kotlin para Jetpack Compose