Une composition décrit l'interface utilisateur de votre application. Elle est générée par l'exécution de composables. La composition est une arborescence composée de composables qui décrivent 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é utilisent l'arborescence pour décrire l'application aux utilisateurs ayant un besoin spécifique. Le framework de test utilise l'arborescence pour interagir avec votre application et effectuer des assertions à son sujet. L'arborescence sémantique ne contient pas d'informations sur la manière de dessiner vos composables, mais sur leur signification 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. Toutefois, lorsque vous ajoutez des composables personnalisés de bas niveau, vous devez fournir manuellement sa sémantique. 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
.
Si vous ne faites rien d'autre, les services d'accessibilité ne recevront pas suffisamment d'informations sur le contenu du composable et sur 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 annoncera "Agenda" ou, un peu mieux, "Agenda d'avril", et l'utilisateur se demandera quel jour a été sélectionné. Pour rendre ce composable plus accessible, vous devez ajouter manuellement des informations sémantiques.
Propriétés sémantiques
Tous les nœuds de l'arborescence d'UI ayant une incidence sémantique disposent d'un nœud parallèle dans l'arborescence sémantique. Le nœud de l'arborescence sémantique contient les propriétés qui ont 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 Icon
. Les composables et les modificateurs créés sur la bibliothèque de base Compose définissent déjà les propriétés pertinentes pour vous. Vous pouvez également définir ou remplacer les propriétés vous-même avec les modificateurs semantics
et clearAndSetSemantics
. Par exemple, ajoutez des actions d'accessibilité personnalisées à un nœud, fournissez une autre description d'état pour un élément activable ou indiquez qu'un composable de texte spécifique doit être considéré comme un titre.
Pour visualiser l'arborescence sémantique, utilisez l'outil d'inspection de la mise en page ou la méthode printToLog()
dans les tests. L'arborescence sémantique actuelle s'affiche dans Logcat.
class MyComposeTest { @get:Rule val composeTestRule = createComposeRule() @Test fun MyTest() { // Start the app composeTestRule.setContent { MyTheme { Text("Hello world!") } } // Log the full semantics tree composeTestRule.onRoot().printToLog("MY TAG") } }
Le résultat est le suivant :
Printing with useUnmergedTree = 'false'
Node #1 at (l=0.0, t=63.0, r=221.0, b=120.0)px
|-Node #2 at (l=0.0, t=63.0, r=221.0, b=120.0)px
Text = '[Hello world!]'
Actions = [GetTextLayoutResult]
Réfléchissez à la manière dont les propriétés sémantiques transmettent la signification d'un composable. Envisagez d'utiliser un Switch
. Voici comment il se présente :
Pour décrire la signification de cet élément, vous pouvez dire ce qui suit: "Il s'agit d'un bouton bascule, qui est un élément activable à l'état activé. Vous pouvez cliquer dessus pour interagir."
C'est précisément ce à quoi servent les propriétés sémantiques. Le nœud sémantique de cet élément Switch contient les propriétés suivantes, telles qu'elles sont visualisées avec l'outil d'inspection de la mise en page:
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 cela peut être plus spécifique (par exemple, "Enabled") en fonction du contexte. ToggleableState
correspond à l'état actuel du commutateur. La propriété OnClick
fait référence à la méthode utilisée pour interagir avec cet élément. 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
.
Le suivi des propriétés sémantiques de chaque composable dans votre application offre tout un éventail de possibilités puissantes. Voici quelques exemples :
- TalkBack utilise les propriétés pour lire à voix haute ce qui est affiché à l'écran et permettre à l'utilisateur d'interagir en douceur avec celui-ci. Pour le composable "Switch" (Bouton bascule), TalkBack peut dire: "On; Switch; double tap to switch". 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. Voici un exemple de test pour le bouton bascule :
val mySwitch = SemanticsMatcher.expectValue( SemanticsProperties.Role, Role.Switch ) composeTestRule.onNode(mySwitch) .performClick() .assertIsOff()
Arborescence sémantique 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'une propriété sémantique n'est pas 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 dans son ensemble, 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 ce composable. Vous pouvez considérer 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 dans quels cas vous devez 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 les arborescences
L'arborescence sémantique est en fait constitué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 tous les nœuds intacts. 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 à la fois l'arborescence sémantique fusionnée et l'arborescence sémantique non fusionnée en sélectionnant celle que vous préférez dans le filtre de vue:
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 pourquoi vous pouvez interagir avec un Button
en faisant correspondre le texte qu'il contient:
composeTestRule.onNodeWithText("Like").performClick()
Remplacez ce comportement en définissant le paramètre useUnmergedTree
des outils de mise en correspondance sur true
, comme pour onRoot
.
Comportement de la fusion
Lorsqu'un composable indique que ses descendants doivent être fusionnés, comment cette fusion se produit-elle exactement ?
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. Vérifiez la stratégie de fusion d'une propriété sémantique en vérifiant son implémentation de mergePolicy
dans SemanticsProperties.kt
. Les propriétés peuvent prendre en compte la valeur parente 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.
Il est important de noter que les descendants qui ont eux-mêmes défini mergeDescendants
= true
ne sont pas inclus dans la fusion. Prenons un exemple:
Voici un élément de liste cliquable. Lorsque l'utilisateur appuie sur la ligne, il accède à la page de détails de l'article et peut le lire. Dans l'élément de liste, un bouton permet d'ajouter l'article aux favoris. Il constitue un élément cliquable imbriqué. Le bouton s'affiche donc séparément dans l'arborescence fusionnée. Le reste du contenu de la ligne est fusionné :
Adapter l'arborescence sémantique
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 important lorsque vous créez vos propres composants personnalisés. Si vous ne définissez pas les propriétés et le comportement de fusion appropriés, votre application risque de ne pas être accessible et les tests peuvent se comporter différemment de ce à quoi vous vous attendiez. Pour en savoir plus sur certains cas d'utilisation courants dans lesquels vous devez adapter l'arborescence sémantique, consultez la documentation sur l'accessibilité. Si vous souhaitez en savoir plus sur les tests, consultez le guide des tests.
Ressources supplémentaires
- Accessibilité:concepts et techniques essentiels communs à tout le développement d'applications Android
- Créer des applications accessibles:étapes clés à suivre pour rendre votre application plus accessible
- Principes pour améliorer l'accessibilité des applications:principes clés à garder à l'esprit lorsque vous cherchez à rendre votre application plus accessible.
- Testing for Accessibility (Tests d'accessibilité) : principes et outils de test pour l'accessibilité sur Android.
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