De nombreux composables sont compatibles avec les gestes d'appui ou les clics, et incluent un lambda onClick. Par exemple, vous pouvez créer un Surface cliquable qui inclut tous les comportements Material Design appropriés pour l'interaction avec les surfaces :
Surface(onClick = { /* handle click */ }) { Text("Click me!", Modifier.padding(24.dp)) }
Toutefois, les clics ne sont pas le seul moyen pour un utilisateur d'interagir avec des composables. Cette page se concentre sur les gestes impliquant un seul pointeur, dont la position n'est pas importante pour la gestion de cet événement. Le tableau suivant liste ces types de gestes :
Geste |
Description |
Appuyez (ou cliquez) |
Le pointeur descend, puis remonte |
Appuyer deux fois |
Le pointeur descend, monte, descend, monte |
Appuyer de manière prolongée |
Le pointeur descend et reste enfoncé plus longtemps |
Presse |
Le pointeur descend |
Répondre à un appui ou à un clic
clickable est un modificateur couramment utilisé qui permet à un composable de réagir aux appuis ou aux clics. Ce modificateur ajoute également des fonctionnalités supplémentaires, telles que la prise en charge de la mise au point, du pointage de la souris et du stylet, ainsi qu'une indication visuelle personnalisable lorsque l'utilisateur appuie sur l'écran. Le modificateur répond aux "clics" au sens le plus large du terme, non seulement avec la souris ou le doigt, mais aussi aux événements de clic via une saisie au clavier ou lors de l'utilisation des services d'accessibilité.
Imaginez une grille d'images, où une image s'affiche en plein écran lorsqu'un utilisateur clique dessus :
Vous pouvez ajouter le modificateur clickable à chaque élément de la grille pour implémenter ce comportement :
@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 } ) } }
Le modificateur clickable ajoute également un comportement supplémentaire :
interactionSourceetindication, qui dessinent un effet d'ondulation par défaut lorsqu'un utilisateur appuie sur le composable. Découvrez comment les personnaliser sur la page Gérer les interactions des utilisateurs.- Permet aux services d'accessibilité d'interagir avec l'élément en définissant les informations sémantiques.
- Permet d'interagir avec un clavier ou un joystick en permettant de sélectionner un élément et d'appuyer sur
Enterou sur le centre du pavé directionnel pour interagir. - Rendez l'élément sensible au pointage pour qu'il réagisse au passage de la souris ou du stylet.
Appuyer de manière prolongée pour afficher un menu contextuel
combinedClickable vous permet d'ajouter un comportement d'appui double ou d'appui de manière prolongée en plus du comportement de clic normal. Vous pouvez utiliser combinedClickable pour afficher un menu contextuel lorsqu'un utilisateur appuie de manière prolongée sur une image de la grille :
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 } ) }
Nous vous recommandons d'inclure un retour haptique lorsque l'utilisateur appuie de manière prolongée sur des éléments. C'est pourquoi l'extrait de code inclut l'appel performHapticFeedback.
Ignorer un composable en appuyant sur un scrim
Dans les exemples ci-dessus, clickable et combinedClickable ajoutent des fonctionnalités utiles à vos composables. Ils affichent une indication visuelle lors de l'interaction, répondent au pointage et incluent la prise en charge de la sélection, du clavier et de l'accessibilité. Toutefois, ce comportement supplémentaire n'est pas toujours souhaitable.
Examinons l'écran des détails de l'image. L'arrière-plan doit être semi-transparent, et l'utilisateur doit pouvoir appuyer dessus pour fermer l'écran d'informations :
Dans ce cas, l'arrière-plan ne doit pas comporter d'indication visuelle lors de l'interaction, ne doit pas répondre au pointage, ne doit pas être sélectionnable, et sa réponse aux événements de clavier et d'accessibilité doit être différente de celle d'un composable classique. Au lieu d'essayer d'adapter le comportement de clickable, vous pouvez passer à un niveau d'abstraction inférieur et utiliser directement le modificateur pointerInput en combinaison avec la méthode 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)) ) }
Vous transmettez le lambda onClose comme clé du modificateur pointerInput. Cela réexécute automatiquement le lambda, ce qui garantit que le bon rappel est appelé lorsque l'utilisateur appuie sur le scrim.
Appuyer deux fois pour zoomer
Il arrive que clickable et combinedClickable ne contiennent pas suffisamment d'informations pour répondre correctement à l'interaction. Par exemple, les composables peuvent avoir besoin d'accéder à la position dans les limites du composable où l'interaction a eu lieu.
Revenons à l'écran des détails de l'image. Une bonne pratique consiste à permettre de zoomer sur l'image en appuyant deux fois dessus :
Comme vous pouvez le voir dans la vidéo, le zoom avant s'effectue autour de la position de l'événement de pression. Le résultat est différent lorsque nous zoomons sur la partie gauche de l'image par rapport à la partie droite. Nous pouvons utiliser le modificateur pointerInput en combinaison avec detectTapGestures pour intégrer la position de l'appui dans notre calcul :
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 } )
Recommandations personnalisées
- Remarque : Le texte du lien s'affiche lorsque JavaScript est désactivé
- Comprendre les gestes
- Material Design 2 dans Compose
- Kotlin pour Jetpack Compose