Molti composabili supportano i tocchi o i clic e includono un lambdaonClick
. Ad esempio, puoi creare un Surface
selezionabile che include tutti i comportamenti di Material Design appropriati per l'interazione con le piattaforme:
Surface(onClick = { /* handle click */ }) { Text("Click me!", Modifier.padding(24.dp)) }
Tuttavia, i clic non sono l'unico modo in cui un utente può interagire con i composabili. Questa pagina si concentra sui gesti che coinvolgono un singolo cursore, la cui posizione non è significativa per la gestione dell'evento. La tabella seguente elenca questi tipi di gesti:
Gesto |
Descrizione |
Tocca (o fai clic) |
Il cursore scende e poi sale |
Tocca due volte |
Il cursore va giù, su, giù, su |
Pressione lunga |
Il cursore scende e viene tenuto premuto per più tempo |
Stampa |
Il cursore scende |
Rispondere a tocchi o clic
clickable
è un modificatore di uso comune che fa sì che un composable reagisca a tocchi o clic. Questo modificatore aggiunge anche funzionalità aggiuntive, come il supporto per lo stato attivo, il passaggio del mouse e dello stilo e un'indicazione visiva personalizzabile quando viene premuto. Il modificatore risponde ai "clic" nel senso più ampio del termine, non solo con il mouse o il dito, ma anche con gli eventi di clic tramite l'input da tastiera o quando si utilizzano i servizi di accessibilità.
Immagina una griglia di immagini in cui un'immagine viene visualizzata a schermo intero quando un utente fa clic su di essa:
Per implementare questo comportamento, puoi aggiungere il modificatore clickable
a ogni elemento della griglia:
@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 } ) } }
Il modificatore clickable
aggiunge anche un comportamento aggiuntivo:
interactionSource
eindication
, che disegnano un'eco per impostazione predefinita quando un utente tocca il composable. Scopri come personalizzarli nella pagina Gestione delle interazioni con gli utenti.- Consente ai servizi di accessibilità di interagire con l'elemento impostando le informazioni sulla semantica.
- Supporta l'interazione con la tastiera o il joystick consentendo di attivare lo stato attivo e di premere
Enter
o il centro del D-pad per interagire. - Rendi l'elemento attivabile con il passaggio del mouse, in modo che risponda al passaggio del mouse o dello stilo.
Premi a lungo per visualizzare un menu contestuale
combinedClickable
ti consente di aggiungere il comportamento di doppio tocco o pressione prolungata oltre al normale comportamento di clic. Puoi utilizzare combinedClickable
per mostrare un menu contestuale quando un utente tocca e tiene premuta un'immagine della griglia:
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 } ) }
Come best practice, ti consigliamo di includere il feedback aptico quando l'utente preme a lungo gli elementi, motivo per cui lo snippet include l'invocazione performHapticFeedback
.
Chiudere un composable toccando una schermata semitrasparente
Negli esempi precedenti, clickable
e combinedClickable
aggiungono funzionalità utili ai tuoi composabili. Mostrano un'indicazione visiva sull'interazione, reagiscono al passaggio del mouse e includono il supporto di attivazione, tastiera e accessibilità. Tuttavia, questo comportamento aggiuntivo non è sempre auspicabile.
Diamo un'occhiata alla schermata dei dettagli dell'immagine. Lo sfondo deve essere semitrasparente e l'utente deve essere in grado di toccarlo per chiudere la schermata dei dettagli:
In questo caso, lo sfondo non deve avere alcuna indicazione visiva sull'interazione, non deve rispondere al passaggio del mouse, non deve essere attivabile e la sua risposta agli eventi della tastiera e dell'accessibilità è diversa da quella di un tipico composable. Anziché provare ad adattare il comportamento di clickable
, puoi passare a un livello di astrazione inferiore e utilizzare direttamente il modificatore pointerInput
in combinazione con il metodo 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)) ) }
Come chiave del modificatore pointerInput
, passi la lambda onClose
. In questo modo, viene eseguita nuovamente automaticamente la funzione lambda, assicurandosi che venga chiamato il callback corretto quando l'utente tocca la schermata scrim.
Tocca due volte per aumentare lo zoom
A volte clickable
e combinedClickable
non includono informazioni sufficienti per rispondere all'interazione nel modo corretto. Ad esempio, i composabili potrebbero dover accedere alla posizione all'interno dei relativi limiti in cui si è verificata l'interazione.
Esamineremo di nuovo la schermata dei dettagli dell'immagine. Una best practice è consentire di aumentare lo zoom sull'immagine facendo doppio tocco:
Come puoi vedere nel video, lo zoom avviene intorno alla posizione dell'evento di tocco. Il risultato è diverso se aumentiamo lo zoom sulla parte sinistra dell'immagine rispetto alla parte destra. Possiamo utilizzare il modificatore pointerInput
in combinazione con detectTapGestures
per incorporare la posizione del tocco nel calcolo:
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 } )
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Informazioni sui gesti
- Material Design 2 in Scrivi
- Kotlin per Jetpack Compose