Outre les informations principales d'un composable, comme une chaîne de texte d'un composable Text, il peut être utile de disposer d'informations supplémentaires sur les éléments d'interface utilisateur.
Les informations sur la signification et le rôle d'un composant dans Compose sont appelées sémantique. Elles permettent de fournir un contexte supplémentaire sur les composables à des services tels que l'accessibilité, la saisie automatique et les tests. Par exemple, une icône d'appareil photo peut n'être qu'une image, mais sa signification sémantique peut être "Prendre une photo".
En combinant la sémantique appropriée avec les API Compose appropriées, vous fournissez autant d'informations que possible sur votre composant aux services d'accessibilité, qui décident ensuite de la manière de le représenter pour l'utilisateur.
Les API Material, Compose UI et Foundation sont dotées d'une sémantique intégrée qui suit leur rôle et leur fonction spécifiques. Toutefois, vous pouvez également modifier cette sémantique pour les API existantes ou en définir de nouvelles pour les composants personnalisés, en fonction de vos besoins spécifiques.
Propriétés sémantiques
Les propriétés sémantiques expriment la signification du composable correspondant. Par exemple, le composable Text contient une propriété sémantique text, car il s'agit de la signification de ce composable. Un Icon contient une propriété contentDescription (si elle est définie par le développeur) qui indique dans le texte la signification de l'icône.
Découvrez comment les propriétés sémantiques expriment la signification d'un composable. Prenons l'exemple d'un Switch. Voici comment il se présente :
Switch à l'état "Activé" ou "Désactivé".Voici comment vous pouvez décrire la signification de cet élément : "Il s'agit d'un bouton bascule, qui est un élément à activer dont l'état actuel est "Activé". Vous pouvez interagir avec lui en cliquant dessus."
C'est précisément ce à quoi servent les propriétés sémantiques. Le nœud sémantique de cet élément de bouton bascule contient les propriétés suivantes, telles qu'elles sont visualisées avec l'outil d'inspection de la mise en page :
Switch.Le Role indique le type d'élément. StateDescription décrit comment l'état "activé" doit être référencé. Par défaut, il s'agit d'une version localisée du mot "On", mais vous pouvez lui donner un nom plus spécifique (p. ex. "Activé") selon le contexte. ToggleableState est l'état actuel du bouton bascule. La propriété OnClick fait référence à la méthode utilisée pour interagir avec cet élément.
Le suivi des propriétés sémantiques de chaque composable dans votre application offre tout un éventail de possibilités puissantes :
- Les services d'accessibilité utilisent les propriétés pour représenter l'UI affichée à l'écran et permettre aux utilisateurs d'interagir avec elle. Pour le composable Switch, TalkBack peut annoncer : "On" (activer), "Switch" (basculer), "Double tap to toggle" (double appui pour activer/désactiver). L'utilisateur peut appuyer deux fois sur son écran pour désactiver le bouton.
-
Le framework de test utilise les propriétés pour rechercher des nœuds, interagir avec eux et effectuer des assertions :
val mySwitch = SemanticsMatcher.expectValue( SemanticsProperties.Role, Role.Switch ) composeTestRule.onNode(mySwitch) .performClick() .assertIsOff()
Les composables et les modificateurs construits sur la bibliothèque de base Compose définissent déjà les propriétés pertinentes pour vous par défaut. Vous pouvez également modifier ces propriétés manuellement pour améliorer la compatibilité avec l'accessibilité dans des cas d'utilisation spécifiques, ou modifier la stratégie de fusion ou d'effacement de vos composables.
Pour signaler le type de contenu spécifique de votre composant aux services d'accessibilité, vous pouvez appliquer différentes sémantiques. Ces ajouts s'appuieront sur les informations sémantiques principales en place et aideront les services d'accessibilité à affiner la façon dont votre composant est représenté, annoncé ou utilisé.
Pour obtenir la liste complète des propriétés sémantiques, consultez l'objet SemanticsProperties. Pour obtenir la liste complète des actions d'accessibilité possibles, consultez l'objet SemanticsActions.
Titres
Les applications contiennent souvent des écrans avec du contenu riche en texte, comme des articles longs ou des pages d'actualités, qui sont généralement divisés en différentes sous-sections avec des titres :
Les utilisateurs ayant des besoins spécifiques en termes d'accessibilité peuvent avoir du mal à naviguer facilement sur ce type d'écran. Pour améliorer l'expérience de navigation, certains services d'accessibilité permettent de naviguer plus facilement directement entre les sections ou les titres. Pour ce faire, indiquez que votre composant est un heading en définissant sa propriété sémantique :
@Composable private fun Subsection(text: String) { Text( text = text, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.semantics { heading() } ) }
Alertes et pop-ups
Si votre composant est une alerte ou un pop-up, comme un Snackbar, vous pouvez signaler aux services d'accessibilité qu'une nouvelle structure ou des mises à jour du contenu peuvent être transmises aux utilisateurs.
Les composants de type alerte peuvent être marqués avec la propriété sémantique liveRegion. Cela permet aux services d'accessibilité d'informer automatiquement l'utilisateur des modifications apportées à ce composant ou à ses enfants :
PopupAlert( message = "You have a new message", modifier = Modifier.semantics { liveRegion = LiveRegionMode.Polite } )
Vous devez utiliser liveRegionMode.Polite dans la plupart des cas où l'attention des utilisateurs ne doit être attirée que brièvement sur des alertes ou des modifications importantes du contenu à l'écran.
Vous devez utiliser liveRegion.Assertive avec parcimonie pour éviter les commentaires perturbateurs.
Il doit être utilisé lorsqu'il est crucial que les utilisateurs soient informés d'un contenu urgent :
PopupAlert( message = "Emergency alert incoming", modifier = Modifier.semantics { liveRegion = LiveRegionMode.Assertive } )
Les régions dynamiques ne doivent pas être utilisées pour les contenus qui sont mis à jour fréquemment, comme les comptes à rebours, afin d'éviter de submerger les utilisateurs de commentaires constants.
Composants de type fenêtre
Les composants personnalisés de type fenêtre, semblables à ModalBottomSheet, nécessitent des signaux supplémentaires pour les différencier du contenu environnant. Pour ce faire, vous pouvez utiliser la sémantique paneTitle afin que les modifications pertinentes de la fenêtre ou du volet soient représentées de manière appropriée par les services d'accessibilité, ainsi que ses principales informations sémantiques :
ShareSheet( message = "Choose how to share this photo", modifier = Modifier .fillMaxWidth() .align(Alignment.TopCenter) .semantics { paneTitle = "New bottom sheet" } )
Pour en savoir plus, découvrez comment Material 3 utilise paneTitle pour ses composants.
Composants d'erreur
Pour d'autres types de contenu, tels que les composants de type erreur, vous pouvez développer les informations sémantiques principales pour les utilisateurs ayant des besoins d'accessibilité.
Lorsque vous définissez des états d'erreur, vous pouvez informer les services d'accessibilité de sa sémantique error et fournir des messages d'erreur détaillés.
Dans cet exemple, TalkBack lit les informations textuelles de l'erreur principale, suivies d'un message développé supplémentaire :
Error( errorText = "Fields cannot be empty", modifier = Modifier .semantics { error("Please add both email and password") } )
Composants de suivi de la progression
Pour les composants personnalisés qui suivent la progression, vous pouvez informer les utilisateurs des changements de progression, y compris la valeur de progression actuelle, sa plage et la taille de pas. Pour ce faire, utilisez la sémantique progressBarRangeInfo. Cela permet de s'assurer que les services d'accessibilité sont informés des changements de progression et peuvent mettre à jour les utilisateurs en conséquence. Différentes technologies d'assistance peuvent également avoir des façons uniques d'indiquer une progression croissante ou décroissante.
ProgressInfoBar( modifier = Modifier .semantics { progressBarRangeInfo = ProgressBarRangeInfo( current = progress, range = 0F..1F ) } )
Informations sur la liste et les éléments
Dans les listes et les grilles personnalisées comportant de nombreux éléments, il peut être utile que les services d'accessibilité reçoivent également des informations plus détaillées, comme le nombre total d'éléments et d'index.
En utilisant respectivement les sémantiques collectionInfo et collectionItemInfo sur la liste et les éléments de cette longue liste, les services d'accessibilité peuvent informer les utilisateurs de l'index de l'élément auquel ils se trouvent par rapport à la collection totale, en plus des informations sémantiques textuelles :
MilkyWayList( modifier = Modifier .semantics { collectionInfo = CollectionInfo( rowCount = milkyWay.count(), columnCount = 1 ) } ) { milkyWay.forEachIndexed { index, text -> Text( text = text, modifier = Modifier.semantics { collectionItemInfo = CollectionItemInfo(index, 0, 0, 0) } ) } }
Description de l'état
Un composable peut définir une stateDescription pour la sémantique utilisée par le framework Android afin de lire l'état dans lequel se trouve le composable. Par exemple, un composable activable peut être à l'état "coché" ou "décoché". Dans certains cas, vous pouvez remplacer les étiquettes de description d'état par défaut utilisées par Compose. Pour ce faire, spécifiez explicitement les étiquettes de description d'état avant de définir un composable comme activable :
@Composable private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) { val stateSubscribed = stringResource(R.string.subscribed) val stateNotSubscribed = stringResource(R.string.not_subscribed) Row( modifier = Modifier .semantics { // Set any explicit semantic properties stateDescription = if (selected) stateSubscribed else stateNotSubscribed } .toggleable( value = selected, onValueChange = { onToggle() } ) ) { /* ... */ } }
Actions personnalisées
Les actions personnalisées peuvent être utilisées pour des gestes plus complexes sur l'écran tactile, comme balayer pour ignorer ou glisser-déposer, car ils peuvent être difficiles à réaliser pour les utilisateurs souffrant de déficiences motrices ou d'autres handicaps.
Pour rendre le geste de balayage pour ignorer plus accessible, vous pouvez l'associer à une action personnalisée, en transmettant l'action et le libellé d'abandon :
SwipeToDismissBox( modifier = Modifier.semantics { // Represents the swipe to dismiss for accessibility customActions = listOf( CustomAccessibilityAction( label = "Remove article from list", action = { removeArticle() true } ) ) }, state = rememberSwipeToDismissBoxState(), backgroundContent = {} ) { ArticleListItem() }
Un service d'accessibilité comme TalkBack met alors en surbrillance le composant et indique que d'autres actions sont disponibles dans son menu, représentant l'action de balayage pour ignorer :
Un autre cas d'utilisation des actions personnalisées concerne les longues listes d'éléments pour lesquels d'autres actions sont disponibles, car il peut être fastidieux pour les utilisateurs de parcourir chaque action pour chaque élément individuellement :
Pour améliorer l'expérience de navigation, ce qui est particulièrement utile pour les technologies d'assistance basées sur l'interaction comme Switch Access ou Voice Access, vous pouvez utiliser des actions personnalisées sur le conteneur pour déplacer les actions d'un parcours individuel vers un menu d'actions distinct :
ArticleListItemRow( modifier = Modifier .semantics { customActions = listOf( CustomAccessibilityAction( label = "Open article", action = { openArticle() true } ), CustomAccessibilityAction( label = "Add to bookmarks", action = { addToBookmarks() true } ), ) } ) { Article( modifier = Modifier.clearAndSetSemantics { }, onClick = openArticle, ) BookmarkButton( modifier = Modifier.clearAndSetSemantics { }, onClick = addToBookmarks, ) }
Dans ce cas, veillez à effacer manuellement la sémantique des enfants d'origine avec le modificateur clearAndSetSemantics, car vous les déplacez vers des actions personnalisées.
Prenons l'exemple de Switch Access. Son menu s'ouvre lorsque vous sélectionnez le conteneur et liste les actions imbriquées disponibles :
Arborescence sémantique
Une composition décrit l'interface utilisateur (UI) de votre application et est générée par l'exécution des composables. La composition est une arborescence de composables qui décrit votre interface utilisateur.
À côté de la composition se trouve une arborescence parallèle, appelée arborescence sémantique. Cette arborescence décrit votre UI d'une manière alternative compréhensible pour les services d'accessibilité et pour le framework de test. Les services d'accessibilité l'utilisent pour décrire l'application aux utilisateurs ayant des besoins spécifiques. Le framework de test utilise l'arborescence pour interagir avec votre application et effectuer des assertions. L'arborescence sémantique ne contient pas d'informations sur la façon de dessiner vos composables, mais sur leur incidence sémantique.
Si votre application inclut des composables et des modificateurs tirés des bibliothèques Foundation et Material de Compose, l'arborescence sémantique est remplie et générée automatiquement. Cependant, lorsque vous ajoutez des composables personnalisés de bas niveau, vous devez fournir la sémantique manuellement. Votre arborescence pourrait ne pas refléter correctement ou complètement la signification des éléments affichés. Dans ce cas, vous pouvez adapter l'arborescence.
Prenons l'exemple de ce composable d'agenda personnalisé :
Dans cet exemple, l'intégralité de l'agenda est implémentée en tant que composable unique de bas niveau, en utilisant le composable Layout et en dessinant directement sur Canvas.
Sans action supplémentaire de votre part, les services d'accessibilité ne recevront pas suffisamment d'informations sur le contenu du composable et la sélection de l'utilisateur dans l'agenda. Par exemple, si un utilisateur clique sur le jour "17", le framework d'accessibilité reçoit uniquement les informations de description pour la commande d'agenda entière. Dans ce cas, le service d'accessibilité TalkBack se contente d'annoncer "Agenda" ou, un peu mieux, "Agenda d'avril", et l'utilisateur se demande quel jour a été sélectionné. Pour rendre ce composable plus accessible, vous devez ajouter manuellement des informations sémantiques.
Arborescence fusionnée et non fusionnée
Comme mentionné précédemment, chaque composable de l'arborescence de l'UI peut avoir des propriétés sémantiques définies sur zéro ou plus. Lorsqu'aucune propriété sémantique n'est définie pour un composable, il n'est pas inclus dans l'arborescence sémantique. Ainsi, l'arborescence sémantique ne contient que les nœuds qui ont une incidence sémantique. Cependant, pour indiquer la signification correcte des informations affichées à l'écran, il est parfois utile de fusionner certaines sous-arborescences de nœuds et de les traiter comme un seul et même élément. De cette façon, vous pouvez traiter un ensemble de nœuds comme un tout, au lieu de traiter chaque nœud descendant individuellement. En règle générale, chaque nœud de cette arborescence représente un élément sélectionnable lorsque vous utilisez des services d'accessibilité.
Button est un exemple de composable. Vous pouvez utiliser un bouton comme un seul élément, même s'il peut contenir plusieurs nœuds enfants :
Button(onClick = { /*TODO*/ }) { Icon( imageVector = Icons.Filled.Favorite, contentDescription = null ) Spacer(Modifier.size(ButtonDefaults.IconSpacing)) Text("Like") }
Dans l'arborescence sémantique, les propriétés des descendants du bouton sont fusionnées et le bouton est présenté comme un nœud feuille unique dans l'arborescence :
Les composables et les modificateurs peuvent indiquer qu'ils souhaitent fusionner les propriétés sémantiques de leurs descendants en appelant Modifier.semantics
(mergeDescendants = true) {}. Si vous définissez cette propriété sur true, vous indiquez que les propriétés sémantiques doivent être fusionnées. Dans l'exemple Button, le composable Button utilise le modificateur clickable en interne qui inclut ce modificateur semantics. Par conséquent, les nœuds descendants du bouton sont fusionnés.
Lisez la documentation sur l'accessibilité pour savoir quand modifier le comportement de fusion dans votre composable.
Plusieurs modificateurs et composables des bibliothèques Foundation et Material Compose disposent de cette propriété. Par exemple, les modificateurs clickable et toggleable fusionnent automatiquement leurs descendants. De plus, le composable ListItem fusionnera ses descendants.
Inspecter l'arborescence
L'arborescence sémantique est en fait composée de deux arbres différents. Il existe une arborescence sémantique fusionnée qui fusionne les nœuds descendants lorsque mergeDescendants est défini sur true.
Il existe également une arborescence sémantique non fusionnée, qui n'applique pas la fusion, mais conserve chaque nœud intact. Les services d'accessibilité utilisent l'arborescence non fusionnée et appliquent leurs propres algorithmes de fusion, en tenant compte de la propriété mergeDescendants. Le framework de test utilise l'arborescence fusionnée par défaut.
Vous pouvez inspecter ces deux arborescences avec la méthode printToLog(). Par défaut, et comme dans les exemples précédents, l'arborescence fusionnée est consignée. Pour imprimer l'arborescence non fusionnée à la place, définissez le paramètre useUnmergedTree de l'outil de mise en correspondance onRoot() sur true :
composeTestRule.onRoot(useUnmergedTree = true).printToLog("MY TAG")
L'outil d'inspection de la mise en page vous permet d'afficher l'arborescence sémantique fusionnée et l'arborescence sémantique non fusionnée en sélectionnant celle que vous préférez dans la vue filtrée :
Pour chaque nœud de votre arborescence, l'outil d'inspection de mise en page affiche la sémantique fusionnée et la sémantique définie sur ce nœud dans le panneau des propriétés :
Par défaut, les outils de mise en correspondance du framework de test utilisent l'arborescence sémantique fusionnée.
C'est la raison pour laquelle vous pouvez interagir avec un Button en faisant correspondre le texte qu'il contient :
composeTestRule.onNodeWithText("Like").performClick()
Vous pouvez ignorer ce comportement en définissant le paramètre useUnmergedTree des outils de mise en correspondance sur true, comme nous l'avons déjà fait avec onRoot.
Adapter l'arborescence
Comme indiqué précédemment, vous pouvez remplacer ou effacer certaines propriétés sémantiques, ou modifier le comportement de fusion de l'arborescence. C'est particulièrement utile lorsque vous créez vos propres composants personnalisés. Si vous ne définissez pas les bonnes propriétés et le bon comportement de fusion, votre application risque de ne pas être accessible et les tests pourraient produire un résultat différent. Pour en savoir plus sur les tests, consultez le guide des tests.
Recommandations personnalisées
- Remarque : Le texte du lien s'affiche lorsque JavaScript est désactivé
- Accessibilité dans Compose
- Material Design 2 dans Compose
- Tester votre mise en page Compose