Dotknij i naciśnij

Wiele elementów kompozycyjnych ma wbudowaną obsługę kliknięć i kliknięć oraz zawiera lambda onClick. Możesz na przykład utworzyć klikalny element Surface, który będzie uwzględniał wszystkie zachowania stylu Material Design związane z interakcjami z powierzchniami:

Surface(onClick = { /* handle click */ }) {
    Text("Click me!", Modifier.padding(24.dp))
}

Jednak kliknięcia to nie jedyny sposób, w jaki użytkownik może wchodzić w interakcję z kompozycją. Ta strona dotyczy gestów obejmujących jeden wskaźnik, którego pozycja nie ma znaczenia dla obsługi danego zdarzenia. W tabeli poniżej znajdziesz listę tych typów gestów:

Gest

Description

Dotknij (lub kliknij)

Wskaźnik maleje, a potem w górę

Dwukrotne dotknięcie

Wskaźnik maleje, w górę, w dół, w górę

Długie naciśnięcie

Wskaźnik maleje i jest utrzymywany przez dłuższy czas

Prasa

Wskaźnik maleje

Reagowanie na kliknięcie

clickable to powszechnie stosowany modyfikator, który sprawia, że funkcja kompozycyjna reaguje na kliknięcia. Ten modyfikator ma też dodatkowe funkcje, takie jak obsługa kursora myszy, najeżdżania kursorem myszy i rysikiem oraz dostosowywane wskaźniki wizualne po kliknięciu. Modyfikator reaguje na „kliknięcia” w najszerszym znaczeniu słowa – nie tylko myszką lub palcem, ale także zdarzeniami kliknięcia za pomocą klawiatury lub usług ułatwień dostępu.

Wyobraź sobie siatkę obrazów, w których obraz wyświetla się na pełnym ekranie, gdy użytkownik go kliknie:

Aby zastosować to działanie, do każdego elementu w siatce możesz dodać modyfikator clickable:

@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 }
        )
    }
}

Modyfikator clickable dodaje też dodatkowe działanie:

  • interactionSource i indication, które domyślnie rysują falę, gdy użytkownik kliknie element kompozycyjny. Na stronie Obsługa interakcji użytkowników dowiesz się, jak dostosować te parametry.
  • Umożliwia usługom ułatwień dostępu wchodzenie w interakcję z elementem przez ustawienie informacji semantycznych.
  • Umożliwia interakcję z klawiaturą lub joystickiem przez naciśnięcie i przytrzymanie przycisku Enter lub środkowej części pada kierunkowego.
  • Zadbaj o to, aby element reagował na najeżdżanie na niego myszą lub rysikiem.

Przytrzymaj, aby wyświetlić menu kontekstowe

combinedClickable umożliwia dodanie do normalnego działania polegającego na dwukrotnym kliknięciu lub przytrzymaniu przycisku. Korzystając z combinedClickable, możesz wyświetlać menu kontekstowe, gdy użytkownik dotknie i przytrzyma obraz siatki:

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 }
    )
}

Sprawdzoną metodą jest uwzględnianie reakcji na dotyk, gdy użytkownik przytrzyma i przytrzyma element. Dlatego fragment kodu zawiera wywołanie performHapticFeedback.

Aby zamknąć kompozycję, kliknij ekran

W powyższych przykładach znaczniki clickable i combinedClickable dodają do elementów kompozycyjnych przydatne funkcje. Zawierają wizualne wskazówki dotyczące interakcji, reagują na najechanie kursorem, a także fokus, klawiaturę i ułatwienia dostępu. Jednak takie dodatkowe zachowanie nie zawsze jest pożądane.

Przyjrzyjmy się ekranowi ze szczegółami obrazu. Tło powinno być półprzezroczyste, a użytkownik powinien mieć możliwość jego dotknięcia, by zamknąć ekran szczegółów:

W tym przypadku to tło nie powinno mieć żadnego wizualnego wskaźnika interakcji, nie powinno reagować na najechanie ani zaznaczyć, a jego reakcja na zdarzenia związane z klawiaturą i ułatwieniami dostępu różni się od typowej funkcji kompozycyjnej. Zamiast próbować dostosować działanie clickable, możesz przejść na niższy poziom abstrakcji i bezpośrednio użyć modyfikatora pointerInput w połączeniu z metodą 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))
    )
}

Jako klucz modyfikatora pointerInput przekazujesz lambda onClose. Spowoduje to automatyczne ponowne wykonanie funkcji lambda, a po kliknięciu przez użytkownika nastąpi wywołanie odpowiedniego wywołania zwrotnego.

Kliknij dwukrotnie, aby powiększyć

Czasami clickable i combinedClickable zawierają za mało informacji, aby prawidłowo zareagować na interakcję. Na przykład kompozycje mogą wymagać dostępu do pozycji w granicach elementu kompozycyjnego, w której miała miejsce interakcja.

Ponownie przyjrzyjmy się ekranowi z informacjami o obrazie. Sprawdzoną metodą jest możliwość powiększenia obrazu przez dwukrotne kliknięcie:

Jak widać na filmie, powiększanie odbywa się w miejscu kliknięcia. Wynik wygląda inaczej, gdy powiększymy lewą część obrazu, a inną po prawej. Możemy użyć modyfikatora pointerInput w połączeniu z elementem detectTapGestures, aby w obliczeniach uwzględnić pozycję kliknięcia:

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
        }
)