Modifier l'ordre de balayage

L'ordre de navigation est l'ordre dans lequel les services d'accessibilité naviguent dans les éléments d'interface utilisateur. Dans une application Compose, les éléments sont organisé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'indices 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 parcours 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 doivent être personnalisés, tandis que traversalIndex ajuste l'ordre des éléments individuels 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 de sémantique est un groupe de parcours. Ce type de nœud a pour fonction de servir de limite ou de bordure pour organiser les enfants du nœud.

Définir 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 les nœuds non sélectionnables par le lecteur d'écran, tels que les colonnes, les lignes ou les 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 "Cette phrase se trouve à 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 paramètre par défaut, TalkBack lit les fragments de phrase dans le mauvais ordre:

"Cette phrase se trouve dans" → "Cette phrase est" → "la colonne de gauche". → "à droite".

Pour organiser correctement les fragments, modifiez l'extrait d'origine afin de 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 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". → "Cette phrase est" → "correcte."

Personnaliser l'ordre de balayage

traversalIndex est une propriété float qui vous permet de personnaliser l'ordre de balayage de TalkBack. Si le regroupement d'éléments n'est pas suffisant pour que TalkBack fonctionne correctement, utilisez traversalIndex 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'indice de balayage influence le comportement de balayage, il doit être défini sur un composant qui sera sélectionnable et sélectionnable par les services d'accessibilité, tels que les éléments à l'écran tels que le texte ou les boutons.
    • Définir uniquement traversalIndex, par exemple, 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 est un scénario courant où l'ordre de balayage standard ne fonctionne pas. L'exemple de cette section est un sélecteur d'heure, dans lequel un utilisateur peut parcourir les chiffres d'un cadran et sélectionner des chiffres pour les heures et les minutes.

Affichage heure et statistiques avec un sélecteur d'heure au-dessus.
Figure 2. Image d'un cadran.

Dans l'extrait de code simplifié suivant, un CircularLayout dans lequel 12 nombres sont dessinés, en commençant par 12 et en tournant 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())
    }
}

Étant donné que la clock face n'est pas lue de manière logique avec l'ordre par défaut de gauche à droite et de haut en bas, TalkBack lit les chiffres dans le désordre. Pour y remédier, utilisez la valeur du compteur incrémentiel, comme illustré 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 parcours, commencez par faire du CircularLayout un groupe de parcours et définissez isTraversalGroup = true. Ensuite, à mesure que chaque texte de l'horloge est dessiné sur la mise en page, définissez son traversalIndex correspondant sur la valeur du compteur.

Étant donné que 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. L'ordre dans lequel TalkBack les lit est ainsi défini. Maintenant, les nombres dans CircularLayout sont lus dans l'ordre attendu.

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

Notez que sans définir la sémantique CircularLayout's sur isTraversalGroup = true, les modifications traversalIndex s'appliquent toujours. Toutefois, sans 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é consultés. Cela se produit, car tous les autres éléments ont une 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 d'exploration.
  • Assurez-vous qu'aucune sémantique n'est fusionnée inutilement, car cela peut affecter les composants auxquels les indices de parcours sont appliqués.