Sémantique dans Compose

Une composition décrit l'interface utilisateur de votre application et est générée en exécutant composables. La composition est une arborescence composée des éléments suivants : composables qui décrivent votre UI.

À côté de la composition figure une arborescence parallèle, appelée sémantique arbre. Cette arborescence décrit votre UI d'une manière alternative compréhensible pour les services d'accessibilité et les tests d'infrastructure. 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 sur la façon de dessiner vos composables, mais aussi sur la signification sémantique de vos composables ;

Hiérarchie classique d'une UI et son arborescence sémantique
Figure 1 Hiérarchie classique d'une UI et son arborescence 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, pour 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é :

Un composable d'agenda personnalisé avec des éléments "jour" sélectionnables
Figure 2 Un composable d'agenda personnalisé avec des éléments "jour" sélectionnables.

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 des informations sur le contenu du composable et la sélection de l'utilisateur dans le calendrier. 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 annonce "Agenda" ou, légèrement mieux, "Agenda d'avril" et l'utilisateur se demander quel jour a été sélectionné. Pour rendre ce composable plus accessibles, vous devrez 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, Text contient une propriété sémantique text, car c'est la signification de ce composable. Un élément Icon contient une propriété contentDescription (s'il est défini par (développeur) qui transmet dans un texte la signification de Icon. Composables et modificateurs qui reposent sur la base de Compose bibliothèque définissent déjà les propriétés pertinentes pour vous. Vous pouvez également définir ou remplacez les propriétés vous-même avec les options semantics et modificateurs clearAndSetSemantics. Par exemple, ajoutez des paramètres personnalisés actions d'accessibilité à un nœud, fournissent un autre état description d'un élément activable, ou indiquer qu'un certain texte doit être considéré comme un en-tête.

Pour visualiser l'arborescence sémantique, utilisez l'outil d'inspection de la mise en page ou utilisez le printToLog() dans les tests. Cela permet d'afficher Arborescence sémantique 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 Switch Voici comment il se présente :

Figure 3. Un interrupteur à l'état "Activé" et "Désactivé" l'état actuel...

Pour décrire la signification de cet élément, vous pouvez dire ceci: "Ceci est un bouton bascule, qui se trouve dans son état "Activé" de l'état. Vous pouvez cliquer dessus d'interagir avec elle."

C'est précisément ce à quoi servent les propriétés sémantiques. Le nœud sémantique de cet élément Switch (Bouton bascule) contient les propriétés suivantes, telles qu'elles sont représentées par la Outil d'inspection de la mise en page:

Outil d'inspection de la mise en page affichant les propriétés sémantiques d'un composable Switch (Bouton bascule)
Figure 4. L'outil d'inspection de la mise en page affiche les propriétés sémantiques d'un composable "Switch" (Bouton bascule).

Role indique le type d'élément. L'StateDescription explique comment l'option "Activé" doit être référencé. Par défaut, il s'agit d'une version localisée avec le mot "Activé", mais vous pouvez préciser sur le contexte. ToggleableState correspond à l'état actuel du commutateur. La La propriété OnClick fait référence à la méthode utilisée pour interagir avec cet élément. Pour pour obtenir la liste complète des propriétés sémantiques, consultez la 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 permet à l'utilisateur d'interagir facilement avec elle. Pour le composable "Switch" (Bouton bascule), TalkBack peut dites : "Activé, Bouton bascule appuyez deux fois pour activer/désactiver". L'utilisateur peut appuyer deux fois sur son à l'é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'aucune propriété sémantique n'est définie pour un composable, 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. Ainsi, vous pouvez raisonner sur un ensemble de nœuds dans son ensemble, au lieu de traiter chaque un 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 type de composable. Vous pouvez raisonner sur un bouton comme élément unique, 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. Le bouton se présente sous la forme d'un nœud feuille unique dans l'arborescence:

Représentation sémantique à feuille unique fusionnée
Figure 5. Représentation sémantique à feuille unique fusionnée.

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, Button le composable utilise le modificateur clickable en interne, qui inclut cette Modificateur semantics. Par conséquent, les nœuds descendants du bouton sont fusionnés. Lisez la documentation sur l'accessibilité pour savoir quand modifier 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 arbres

L'arbre sémantique est en fait deux arbres différents. La sémantique fusionnée tree, 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, 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 du 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 dans les exemples précédents, l'arborescence fusionnée est consignée. Pour imprimer l'arborescence non fusionnée Définissez plutôt 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 la sémantique fusionnée et la sémantique non fusionnée en sélectionnant l'option de votre choix dans le filtre de la vue:

Options d'affichage de l'outil d'inspection de la mise en page permettant d'afficher à la fois l'arborescence sémantique fusionnée et l'arborescence sémantique non fusionnée
Figure 6 Options d'affichage de l'outil d'inspection de la mise en page permettant d'afficher à la fois l'arborescence sémantique fusionnée et l'arborescence sémantique non fusionné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 :

Propriétés sémantiques fusionnées et définies
Figure 7 Propriétés sémantiques fusionnées et définies.

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 comme suit:

composeTestRule.onNodeWithText("Like").performClick()

Remplacez ce comportement en définissant le paramètre useUnmergedTree de à true, comme avec 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 ContentDescription descendantes à une liste. Vérifier la stratégie de fusion d'une propriété sémantique en vérifiant sa Implémentation de mergePolicy dans SemanticsProperties.kt. Les établissements peuvent prend la valeur parent ou enfant, les fusionner dans une liste n'autorise pas la fusion et génère une exception à la place, ou toute autre une 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. Examinons un exemple:

Élément de liste avec une image, du texte et une icône de favori
Figure 8 Élément de liste avec une image, du texte et une icône de favori.

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. À l'intérieur de l'élément de liste, il y a un bouton pour ajouter l'article aux favoris, ce qui élément cliquable imbriqué, de sorte que le bouton s'affiche séparément dans le arborescence fusionnée. Le reste du contenu de la ligne est fusionné :

L'arborescence fusionnée contient plusieurs textes d'une liste au sein du nœud "Ligne". L'arborescence non fusionnée contient des nœuds distincts pour chaque composable Text.
Figure 9 L'arborescence fusionnée contient plusieurs textes d'une liste au sein du nœud "Ligne". L'arborescence non fusionnée contient des nœuds distincts pour chaque composable Text.

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. Cela est particulièrement pertinent lorsque vous créez vos propres composants personnalisés. Si vous ne définissez pas et le comportement de fusion, il est possible que votre application ne soit pas accessible et que les tests se comportent différemment de ce à quoi vous vous attendiez. Pour en savoir plus sur certains cas d'utilisation courants où vous devez adapter l'arborescence sémantique, consultez les documentation. Pour en savoir plus sur les tests, consultez la section Tests guide de démarrage.

Ressources supplémentaires