Tocca e premi

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 e indication, 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
        }
)