Wiele funkcji kompozycyjnych ma wbudowaną obsługę kliknięć i zawiera funkcję lambda onClick. Możesz na przykład utworzyć klikalny element Surface, który obejmuje wszystkie zachowania Material Design odpowiednie do interakcji z powierzchniami:
Surface(onClick = { /* handle click */ }) { Text("Click me!", Modifier.padding(24.dp)) }
Kliknięcia nie są jednak jedynym sposobem interakcji użytkownika z komponentami. Na tej stronie skupiamy się na gestach, które obejmują pojedynczy wskaźnik, którego pozycja nie jest istotna dla obsługi danego zdarzenia. W tabeli poniżej znajdziesz listę tych typów gestów:
Gest |
Opis |
Kliknij |
Wskaźnik przesuwa się w dół, a potem w górę |
Dwukrotne dotknięcie |
Wskaźnik przesuwa się w dół, w górę, w dół, w górę |
Długie naciśnięcie |
Wskaźnik przesuwa się w dół i jest przytrzymywany przez dłuższy czas |
Prasa |
Wskaźnik przesuwa się w dół |
Reagowanie na kliknięcie
clickable to często używany modyfikator, który sprawia, że funkcja kompozycyjna reaguje na kliknięcia. Ten modyfikator dodaje też dodatkowe funkcje, takie jak obsługa zaznaczenia, najechania myszą i rysikiem oraz dostosowywany wskaźnik wizualny po naciśnięciu. Modyfikator reaguje na „kliknięcia” w najszerszym znaczeniu tego słowa – nie tylko myszą lub palcem, ale też na zdarzenia kliknięcia za pomocą klawiatury lub usług ułatwień dostępu.
Wyobraź sobie siatkę obrazów, w której obraz wyświetla się na pełnym ekranie, gdy użytkownik go kliknie:
Aby zaimplementować to zachowanie, możesz dodać modyfikator clickable do każdego elementu w siatce:
@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 zachowanie:
interactionSourceiindication, które domyślnie rysują efekt fali, gdy użytkownik kliknie element kompozycyjny. Dowiedz się, jak je dostosować, na stronie Obsługa interakcji użytkownika.- Umożliwia usługom ułatwień dostępu interakcję z elementem przez ustawienie informacji semantycznych.
- Obsługuje interakcje za pomocą klawiatury lub joysticka, umożliwiając zaznaczanie i naciskanie przycisku
Enterlub środka pada kierunkowego. - Ustaw element jako możliwy do najechania, aby reagował na najechanie myszą lub rysikiem.
Przytrzymaj, aby wyświetlić menu kontekstowe.
combinedClickable umożliwia dodanie działania po dwukrotnym kliknięciu lub przytrzymaniu, oprócz działania po zwykłym kliknięciu. Możesz użyć combinedClickable, aby wyświetlić menu kontekstowe, gdy użytkownik naciśnie i przytrzyma obraz w siatce:
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ędnienie reakcji haptycznej, gdy użytkownik przytrzymuje elementy. Dlatego fragment kodu zawiera wywołanie performHapticFeedback.
Zamykanie elementu kompozycyjnego przez kliknięcie tła
W powyższych przykładach clickable i combinedClickable dodają przydatne funkcje do funkcji kompozycyjnych. Wizualnie sygnalizują interakcję, reagują na najechanie kursorem i obsługują zaznaczenie, klawiaturę oraz ułatwienia dostępu. Jednak to 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ść kliknięcia go, aby zamknąć ekran szczegółów:
W takim przypadku tło nie powinno mieć żadnych wizualnych oznaczeń interakcji, nie powinno reagować na najechanie kursorem, nie powinno być możliwe do zaznaczenia, a jego reakcja na zdarzenia klawiatury i ułatwień dostępu powinna różnić się od reakcji typowego elementu kompozycyjnego. 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:
@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 funkcję lambda onClose. Spowoduje to automatyczne ponowne wykonanie funkcji lambda, dzięki czemu po kliknięciu przez użytkownika tła zostanie wywołane odpowiednie wywołanie zwrotne.
Kliknij dwukrotnie, aby powiększyć obraz
Czasami clickable i combinedClickable nie zawierają wystarczającej ilości informacji, aby prawidłowo odpowiedzieć na interakcję. Na przykład funkcje kompozycyjne mogą potrzebować dostępu do pozycji w granicach funkcji kompozycyjnej, w której miała miejsce interakcja.
Spójrzmy jeszcze raz na ekran ze szczegółami obrazu. Sprawdzoną metodą jest umożliwienie powiększenia obrazu przez dwukrotne kliknięcie:
Jak widać na filmie, powiększenie następuje wokół pozycji zdarzenia dotknięcia. Wynik jest inny, gdy powiększymy lewą część obrazu, a inny, gdy powiększymy prawą. Możemy użyć modyfikatora pointerInput w połączeniu z detectTapGestures, aby uwzględnić pozycję kliknięcia w naszych obliczeniach:
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 } )
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy język JavaScript jest wyłączony.
- Informacje o gestach
- Material Design 2 w Compose
- Kotlin w Jetpack Compose