Lorsque les services d'accessibilité parcourent les éléments à l'écran, il est important que ces éléments soient regroupés, séparés ou même masqués avec la précision appropriée. Si chaque composable de bas niveau à l'écran est mis en surbrillance indépendamment, les utilisateurs devront beaucoup interagir pour se déplacer dans l'écran. Si les éléments sont fusionnés de manière excessive, les utilisateurs pourraient ne pas comprendre quels éléments vont ensemble. Si certains éléments à l'écran sont purement décoratifs, ils peuvent être masqués pour les services d'accessibilité. Dans ce cas, vous pouvez utiliser les API Compose pour fusionner, effacer et masquer la sémantique.
Sémantique de fusion
Lorsque vous appliquez un modificateur clickable à un composable parent, Compose fusionne automatiquement tous les éléments enfants qu'il contient. Pour comprendre comment les composants interactifs Compose Material et Foundation utilisent les stratégies de fusion par défaut, consultez la section Éléments interactifs.
Il est courant qu'un composant soit constitué de plusieurs composables. Ces composables peuvent former un groupe logique et chacun peut contenir des informations importantes, mais vous souhaitez peut-être que les services d'accessibilité les considèrent comme un seul élément.
Par exemple, imaginez un composable qui affiche l'avatar d'un utilisateur, son nom et d'autres informations supplémentaires :
Vous pouvez autoriser Compose à fusionner ces éléments à l'aide du paramètre mergeDescendants dans le modificateur de sémantique. De cette façon, les services d'accessibilité traitent le composant comme une seule entité, et toutes les propriétés sémantiques des descendants sont fusionnées :
@Composable private fun PostMetadata(metadata: Metadata) { // Merge elements below for accessibility purposes Row(modifier = Modifier.semantics(mergeDescendants = true) {}) { Image( imageVector = Icons.Filled.AccountCircle, contentDescription = null // decorative ) Column { Text(metadata.author.name) Text("${metadata.date} • ${metadata.readTimeMinutes} min read") } } }
Les services d'accessibilité se concentrent désormais sur l'ensemble du conteneur et fusionnent son contenu :
Chaque propriété sémantique a une stratégie de fusion définie. Par exemple, la propriété ContentDescription ajoute toutes les valeurs descendantes ContentDescription à une liste. Vous pouvez vérifier la stratégie de fusion d'une propriété sémantique en regardant son implémentation mergePolicy dans SemanticsProperties.kt.
Les propriétés peuvent prendre la valeur parent ou enfant, fusionner les valeurs dans une liste ou une chaîne, ne pas autoriser la fusion du tout et générer une exception à la place, ou toute autre stratégie de fusion personnalisée.
Dans d'autres scénarios, vous vous attendez à ce que la sémantique enfant soit fusionnée avec une sémantique parente, mais ce n'est pas le cas. Dans l'exemple suivant, nous avons un élément de liste parent clickable avec des éléments enfants, et nous pouvons nous attendre à ce que le parent les fusionne tous :
@Composable private fun ArticleListItem( openArticle: () -> Unit, addToBookmarks: () -> Unit, ) { Row(modifier = Modifier.clickable { openArticle() }) { // Merges with parent clickable: Icon( painter = painterResource(R.drawable.ic_logo), contentDescription = "Article thumbnail" ) ArticleDetails() // Defies the merge due to its own clickable: BookmarkButton(onClick = addToBookmarks) } }
Lorsque l'utilisateur appuie sur l'élément clickable Row, l'article s'ouvre. Il contient un BookmarkButton permettant d'ajouter l'article aux favoris. Ce bouton imbriqué s'affiche comme non fusionné, tandis que le reste du contenu enfant de la ligne est fusionné :
Row. L'arborescence non fusionnée contient des nœuds distincts pour chaque composable Text.Certains composables ne sont pas automatiquement fusionnés sous un parent, par conception. Un parent ne peut pas fusionner ses enfants lorsque ceux-ci fusionnent également, soit en définissant explicitement mergeDescendants = true, soit en étant des composants qui fusionnent eux-mêmes, comme des boutons ou des éléments cliquables. Savoir comment certaines API fusionnent ou empêchent la fusion peut vous aider à déboguer certains comportements potentiellement inattendus.
Utilisez la fusion lorsque les éléments enfants constituent un groupe logique et pertinent sous leur parent. Toutefois, si les enfants imbriqués nécessitent un ajustement manuel ou la suppression de leur propre sémantique, d'autres API peuvent mieux répondre à vos besoins (par exemple, clearAndSetSemantics).
Effacer et définir la sémantique
Si les informations sémantiques doivent être complètement effacées ou écrasées, l'API clearAndSetSemantics est une API puissante à utiliser.
Lorsqu'un composant doit effacer sa propre sémantique et celle de ses descendants, utilisez cette API avec un lambda vide. Lorsque sa sémantique doit être écrasée, incluez votre nouveau contenu dans le lambda.
Notez que lorsque vous effacez avec un lambda vide, la sémantique effacée n'est envoyée à aucun consommateur qui utilise ces informations, comme l'accessibilité, la saisie automatique ou les tests. Lorsque vous écrasez du contenu avec clearAndSetSemantics{/*semantic information*/}, la nouvelle sémantique remplace toute la sémantique précédente de l'élément et de ses descendants.
Voici un exemple de composant d'activation/désactivation personnalisé, représenté par une ligne interactive avec une icône et du texte :
// Developer might intend this to be a toggleable. // Using `clearAndSetSemantics`, on the Row, a clickable modifier is applied, // a custom description is set, and a Role is applied. @Composable fun FavoriteToggle() { val checked = remember { mutableStateOf(true) } Row( modifier = Modifier .toggleable( value = checked.value, onValueChange = { checked.value = it } ) .clearAndSetSemantics { stateDescription = if (checked.value) "Favorited" else "Not favorited" toggleableState = ToggleableState(checked.value) role = Role.Switch }, ) { Icon( imageVector = Icons.Default.Favorite, contentDescription = null // not needed here ) Text("Favorite?") } }
Bien que l'icône et le texte contiennent des informations sémantiques, ils n'indiquent pas ensemble que ce composant est un bouton d'activation/désactivation. La fusion n'est pas suffisante, car vous devez fournir des informations supplémentaires sur le composant.
Étant donné que l'extrait de code ci-dessus crée un composant d'activation/désactivation personnalisé, vous devez ajouter la fonctionnalité d'activation/désactivation, ainsi que les sémantiques stateDescription, toggleableState et role. De cette façon, l'état du composant et l'action associée sont disponibles. Par exemple, TalkBack annonce "Appuyez deux fois pour activer/désactiver" au lieu de "Appuyez deux fois pour activer".
En effaçant la sémantique d'origine et en définissant une nouvelle sémantique plus descriptive, les services d'accessibilité peuvent désormais voir qu'il s'agit d'un composant activable/désactivable qui peut changer d'état.
Lorsque vous utilisez clearAndSetSemantics, tenez compte des points suivants :
- Étant donné que les services ne reçoivent aucune information lorsque cette API est définie, il est préférable de l'utiliser avec parcimonie.
- Les informations sémantiques peuvent potentiellement être utilisées par les agents IA et des services similaires pour comprendre l'écran. Elles ne doivent donc être effacées que si nécessaire.
- Vous pouvez définir une sémantique personnalisée dans la fonction lambda de l'API.
- L'ordre des modificateurs est important : cette API efface toutes les sémantiques qui se trouvent après l'endroit où elle est appliquée, quelles que soient les autres stratégies de fusion.
Masquer la sémantique
Dans certains cas, il n'est pas nécessaire d'envoyer des éléments aux services d'accessibilité. Par exemple, les informations supplémentaires peuvent être redondantes pour l'accessibilité, ou purement visuelles et non interactives. Dans ce cas, vous pouvez masquer des éléments avec l'API hideFromAccessibility.
Les exemples suivants présentent des composants qui peuvent devoir être masqués : un filigrane redondant qui s'étend sur un composant et un caractère utilisé pour séparer les informations de manière décorative.
@Composable fun WatermarkExample( watermarkText: String, content: @Composable () -> Unit, ) { Box { WatermarkedContent() // Mark the watermark as hidden to accessibility services. WatermarkText( text = watermarkText, color = Color.Gray.copy(alpha = 0.5f), modifier = Modifier .align(Alignment.BottomEnd) .semantics { hideFromAccessibility() } ) } } @Composable fun DecorativeExample() { Text( modifier = Modifier.semantics { hideFromAccessibility() }, text = "A dot character that is used to decoratively separate information, like •" ) }
L'utilisation de hideFromAccessibility ici garantit que le filigrane et la décoration sont masqués pour les services d'accessibilité, mais qu'ils conservent leur sémantique pour d'autres cas d'utilisation, comme les tests.
Répartition des cas d'utilisation
Voici un résumé des cas d'utilisation pour comprendre comment différencier clairement les API précédentes :
- Lorsque le contenu n'est pas destiné à être utilisé par les services d'accessibilité :
- Utilisez
hideFromAccessibilitylorsque le contenu est potentiellement décoratif ou redondant, mais qu'il doit tout de même être testé. - Utilisez
clearAndSetSemantics{}avec un lambda vide lorsque la sémantique parent et enfant doit être effacée pour tous les services. - Utilisez
clearAndSetSemantics{/*content*/}avec du contenu dans le lambda lorsque la sémantique d'un composant doit être définie manuellement.
- Utilisez
- Lorsque le contenu doit être traité comme une seule entité et que toutes les informations de ses enfants doivent être complètes :
- Utilisez la fusion des descendants sémantiques.
Recommandations personnalisées
- Remarque : Le texte du lien s'affiche lorsque JavaScript est désactivé
- Accessibilité dans Compose
- [Material Design 2 dans Compose][19]
- Tester votre mise en page Compose