Modifier l'ordre de balayage

L'ordre de balayage est l'ordre dans lequel les services d'accessibilité parcourent les éléments de l'interface utilisateur. Dans une application Compose, les éléments sont disposés dans l'ordre de lecture attendu, qui est généralement de gauche à droite, puis de haut en bas. Toutefois, dans certains cas, Compose peut avoir besoin d'indications supplémentaires pour déterminer l'ordre de lecture correct.

isTraversalGroup et traversalIndex sont des propriétés sémantiques qui vous permettent d'influencer l'ordre de balayage des services d'accessibilité dans les scénarios où l'algorithme de tri par défaut de Compose n'est pas suffisant. isTraversalGroup identifie les groupes sémantiquement importants qui nécessitent une personnalisation, tandis que traversalIndex ajuste l'ordre des éléments individuels au sein de ces groupes. Vous pouvez utiliser isTraversalGroup seul pour indiquer que tous les éléments d'un groupe doivent être sélectionnés ensemble, ou avec traversalIndex pour une personnalisation plus poussée.

Utilisez isTraversalGroup et traversalIndex dans votre application pour contrôler l'ordre de balayage du lecteur d'écran.

Regrouper des éléments pour la traversée

isTraversalGroup est une propriété booléenne qui définit si un nœud semantics est un groupe de parcours. Ce type de nœud sert de limite ou de bordure pour organiser les nœuds enfants.

La définition de isTraversalGroup = true sur un nœud signifie que tous les enfants de ce nœud sont visités avant de passer à d'autres éléments. Vous pouvez définir isTraversalGroup sur des nœuds sélectionnables qui ne sont pas des lecteurs d'écran, tels que des colonnes, des lignes ou des cases.

L'exemple suivant utilise isTraversalGroup. Il émet quatre éléments de texte. Les deux éléments de gauche appartiennent à un élément CardBox, tandis que les deux éléments de droite appartiennent à un autre élément CardBox :

// CardBox() function takes in top and bottom sample text.
@Composable
fun CardBox(
    topSampleText: String,
    bottomSampleText: String,
    modifier: Modifier = Modifier
) {
    Box(modifier) {
        Column {
            Text(topSampleText)
            Text(bottomSampleText)
        }
    }
}

@Composable
fun TraversalGroupDemo() {
    val topSampleText1 = "This sentence is in "
    val bottomSampleText1 = "the left column."
    val topSampleText2 = "This sentence is "
    val bottomSampleText2 = "on the right."
    Row {
        CardBox(
            topSampleText1,
            bottomSampleText1
        )
        CardBox(
            topSampleText2,
            bottomSampleText2
        )
    }
}

Le code produit un résultat semblable à celui-ci :

Mise en page avec deux colonnes de texte, la colonne de gauche indiquant "Cette phrase se trouve dans la colonne de gauche" et la colonne de droite indiquant "Cette phrase se trouve dans la colonne de droite".
Figure 1. Mise en page avec deux phrases (une dans la colonne de gauche et une dans la colonne de droite).

Comme aucune sémantique n'a été définie, le comportement par défaut du lecteur d'écran consiste à parcourir les éléments de gauche à droite et de haut en bas. En raison de ce comportement par défaut, TalkBack lit les fragments de phrase dans le mauvais ordre :

"This sentence is in" → "This sentence is" → "the left column." → "sur la droite".

Pour classer correctement les fragments, modifiez l'extrait d'origine pour définir isTraversalGroup sur true :

@Composable
fun TraversalGroupDemo2() {
    val topSampleText1 = "This sentence is in "
    val bottomSampleText1 = "the left column."
    val topSampleText2 = "This sentence is"
    val bottomSampleText2 = "on the right."
    Row {
        CardBox(
//      1,
            topSampleText1,
            bottomSampleText1,
            Modifier.semantics { isTraversalGroup = true }
        )
        CardBox(
//      2,
            topSampleText2,
            bottomSampleText2,
            Modifier.semantics { isTraversalGroup = true }
        )
    }
}

Étant donné que isTraversalGroup est défini spécifiquement sur chaque CardBox, les limites de CardBox s'appliquent lors du tri de leurs éléments. Dans ce cas, le CardBox de gauche est lu en premier, suivi du CardBox de droite.

TalkBack lit désormais les fragments de phrase dans le bon ordre :

"Cette phrase se trouve dans" → "la colonne de gauche." → "This sentence is" → "on the right."

Personnaliser l'ordre de balayage

traversalIndex est une propriété flottante qui vous permet de personnaliser l'ordre de balayage de TalkBack. Si le regroupement d'éléments ne suffit pas pour que TalkBack fonctionne correctement, utilisez traversalIndex conjointement avec isTraversalGroup pour personnaliser davantage l'ordre du lecteur d'écran.

La propriété traversalIndex présente les caractéristiques suivantes :

  • Les éléments ayant des valeurs traversalIndex inférieures sont prioritaires.
  • Peut être positif ou négatif.
  • La valeur par défaut est 0f.
  • Pour que l'index de parcours influence le comportement de parcours, il doit être défini sur un composant qui peut être sélectionné et sélectionné par les services d'accessibilité, comme les éléments à l'écran tels que le texte ou les boutons.
    • Par exemple, définir uniquement traversalIndex sur un Column n'aurait aucun effet, sauf si la colonne est également définie sur isTraversalGroup.

L'exemple suivant montre comment utiliser traversalIndex et isTraversalGroup ensemble.

Un cadran d'horloge est un scénario courant dans lequel l'ordre de balayage standard ne fonctionne pas. L'exemple de cette section est un sélecteur d'heure, qui permet à l'utilisateur de parcourir les chiffres d'un cadran et de sélectionner des chiffres pour les emplacements des heures et des minutes.

Cadran d'horloge avec un sélecteur d'heure au-dessus.
Figure 2. Image d'un cadran d'horloge.

Dans l'extrait simplifié suivant, un CircularLayout contient 12 nombres, en commençant par 12 et en se déplaçant dans le sens des aiguilles d'une montre autour du cercle :

@Composable
fun ClockFaceDemo() {
    CircularLayout {
        repeat(12) { hour ->
            ClockText(hour)
        }
    }
}

@Composable
private fun ClockText(value: Int) {
    Box(modifier = Modifier) {
        Text((if (value == 0) 12 else value).toString())
    }
}

Comme le cadran de l'horloge ne se lit pas de manière logique de gauche à droite et de haut en bas, TalkBack lit les chiffres dans le désordre. Pour résoudre ce problème, utilisez la valeur du compteur d'incrémentation, comme indiqué dans l'extrait de code suivant :

@Composable
fun ClockFaceDemo() {
    CircularLayout(Modifier.semantics { isTraversalGroup = true }) {
        repeat(12) { hour ->
            ClockText(hour)
        }
    }
}

@Composable
private fun ClockText(value: Int) {
    Box(modifier = Modifier.semantics { this.traversalIndex = value.toFloat() }) {
        Text((if (value == 0) 12 else value).toString())
    }
}

Pour définir correctement l'ordre de balayage, commencez par faire de CircularLayout un groupe de balayage et définissez isTraversalGroup = true. Ensuite, lorsque chaque texte d'horloge est dessiné sur la mise en page, définissez son traversalIndex correspondant sur la valeur du compteur.

Comme la valeur du compteur augmente continuellement, la valeur traversalIndex de chaque valeur d'horloge est plus élevée à mesure que des chiffres sont ajoutés à l'écran : la valeur d'horloge 0 a une valeur traversalIndex de 0, et la valeur d'horloge 1 a une valeur traversalIndex de 1. De cette façon, l'ordre dans lequel TalkBack les lit est défini. Désormais, les chiffres à l'intérieur de CircularLayout sont lus dans l'ordre attendu.

Étant donné que les traversalIndexes définis ne sont relatifs qu'aux autres index du même groupe, l'ordre du reste de l'écran a été conservé. En d'autres termes, les modifications sémantiques présentées dans l'extrait de code précédent ne modifient que l'ordre dans le cadran d'horloge pour lequel isTraversalGroup = true est défini.

Notez que même si vous ne définissez pas la sémantique CircularLayout's sur isTraversalGroup = true, les modifications de traversalIndex s'appliquent quand même. Cependant, sans le CircularLayout pour les lier, les douze chiffres du cadran sont lus en dernier, après que tous les autres éléments de l'écran ont été parcourus. En effet, tous les autres éléments ont un traversalIndex par défaut de 0f, et les éléments de texte de l'horloge sont lus après tous les autres éléments 0f.

Remarques concernant l'API

Tenez compte des points suivants lorsque vous utilisez les API de parcours :

  • isTraversalGroup = true doit être défini sur le parent contenant les éléments groupés.
  • traversalIndex doit être défini sur un composant enfant qui contient des sémantiques et qui sera sélectionné par les services d'accessibilité.
  • Assurez-vous que tous les éléments que vous examinez se trouvent au même niveau zIndex, car cela affecte également la sémantique et l'ordre de balayage.
  • Assurez-vous qu'aucune sémantique n'est fusionnée inutilement, car cela peut affecter les composants auxquels les index de parcours sont appliqués.