Contrôler l'ordre de balayage

Par défaut, le comportement du lecteur d'écran d'accessibilité dans une application Compose est implémenté dans l'ordre de lecture prévu, généralement de gauche à droite, puis de haut en bas. Toutefois, il existe certains types de mises en page d'applications pour lesquelles l'algorithme ne peut pas déterminer l'ordre de lecture réel sans autres conseils. Dans les applications basées sur les vues, vous pouvez : Corrigez ces problèmes à l'aide des propriétés traversalBefore et traversalAfter. À partir de Compose 1.5, Compose fournit une API tout aussi flexible, mais avec un nouveau modèle conceptuel.

isTraversalGroup et traversalIndex sont des propriétés sémantiques qui vous permettent de contrôler l'accessibilité et l'ordre de sélection de TalkBack dans les cas où l'algorithme de tri par défaut n'est pas approprié. isTraversalGroup identifie d'un point de vue sémantique, tandis que traversalIndex ajuste l'ordre des éléments individuels au sein de ces groupes. Vous pouvez utiliser isTraversalGroup seul, ou avec traversalIndex pour une personnalisation plus poussée.

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

Regrouper les éléments avec isTraversalGroup

isTraversalGroup est une propriété booléenne qui définit si une sémantique est un groupe de balayage. Ce type de nœud est celui dont la fonction est de servir comme une limite ou une bordure pour organiser les enfants du nœud.

Si vous définissez isTraversalGroup = true sur un nœud, cela signifie que tous les enfants de ce nœud avant de passer à d'autres éléments. Vous pouvez définir isTraversalGroup sur les nœuds sélectionnables hors lecteur d'écran, tels que les colonnes, les lignes ou les cases.

L'exemple suivant utilise isTraversalGroup. Il émet quatre éléments de texte. La les deux éléments de gauche appartiennent à un seul é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, avec la colonne de gauche indiquant "Ceci
  la phrase est dans la colonne de gauche et la colonne de droite indiquant 
« Cette phrase est à droite ».
Figure 1. Une mise en page avec deux phrases (une à 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 est pour balayer les éléments de gauche à droite et de haut en bas. Pour cette raison par défaut, TalkBack lit les fragments de phrases dans le mauvais ordre:

"Cette phrase est en" → "Cette phrase est" → "la colonne de gauche." → "sur le à droite. »

Pour classer correctement les fragments, modifiez l'extrait d'origine pour définir isTraversalGroup à 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 }
        )
    }
}

Comme isTraversalGroup est défini spécifiquement sur chaque CardBox, le CardBox les limites s’appliquent lors du tri de leurs éléments. Dans ce cas, la partie gauche CardBox est lu en premier, suivi du bon CardBox.

À présent, TalkBack lit les fragments de phrases dans le bon ordre:

"Cette phrase est en" → "la colonne de gauche." → "Cette phrase est" → "sur le à droite. »

Personnaliser davantage l'ordre de balayage

traversalIndex est une propriété flottante qui vous permet de personnaliser TalkBack l'ordre de passage. Si le fait de regrouper des éléments ne suffit pas pour que TalkBack fonctionne correctement, utilisez traversalIndex conjointement avec isTraversalGroup pour personnaliser davantage l'ordre des lecteurs d'écran.

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

  • Les éléments dont les valeurs traversalIndex sont inférieures sont prioritaires.
  • Peut être positif ou négatif.
  • La valeur par défaut est 0f.
  • N'affecte que les nœuds sélectionnables pour les lecteurs d'écran, tels que les éléments à l'écran tels que du texte ou des boutons. Par exemple, ne définir que traversalIndex sur une colonne n'ont aucun effet, sauf si isTraversalGroup est également défini sur la colonne.

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

Exemple: Changer de clock face

Un affichage heure et statistiques est un scénario courant dans lequel l'ordre de balayage standard travail. L'exemple de cette section est un sélecteur de temps, où l'utilisateur peut balayer faire défiler les nombres sur un affichage heure et statistiques et sélectionner les chiffres de l'heure et des minutes emplacements.

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

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

@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 l'affichage heure et statistiques n'est pas lu de manière logique avec les dans l'ordre de haut en bas, TalkBack lit les chiffres dans le désordre. Pour rectifier utilisez la valeur du compteur incrémentiel, 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, définissez d'abord CircularLayout de balayage et de définir isTraversalGroup = true. Ensuite, à mesure que chaque texte d'horloge est dessiné sur la mise en page, définissez le traversalIndex correspondant sur le compteur .

Comme la valeur du compteur augmente continuellement, chaque valeur d'horloge traversalIndex est plus grand lorsque des nombres sont ajoutés à l'écran — la valeur d'horloge 0 a une traversalIndex de 0, et la valeur d'horloge 1 a une traversalIndex de 1. De cette manière, l'ordre dans lequel TalkBack les lit est défini. Maintenant, les chiffres dans CircularLayout sont lus dans l'ordre attendu.

Étant donné que les traversalIndexes définis ne sont liés qu'à d'autres des index au sein du même groupe, le reste de l'ordre des écrans préservées. En d'autres termes, les changements sémantiques indiqués dans le code précédent ne modifie que l'ordre dans l'affichage heure et statistiques qui comporte isTraversalGroup = true défini.

Notez que 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 de l'affichage heure et statistiques sont lus en dernier, après que tous les autres éléments de l'écran ont été visités. Cela se produit car tous les autres éléments ont une traversalIndex par défaut de 0f, et Les éléments de texte d'horloge sont lus après tous les autres éléments 0f.

Exemple: Personnaliser l'ordre de balayage pour le bouton d'action flottant

Dans cet exemple, traversalIndex et isTraversalGroup contrôlent l'ordre de balayage d'un bouton d'action flottant (FAB) Material Design. La base de cet exemple est la mise en page suivante:

Une mise en page avec une barre d'application supérieure, un exemple de texte, un bouton d'action flottant et
  une barre d'application inférieure.
Figure 3. Mise en page avec une barre d'application supérieure, un exemple de texte, un bouton d'action flottant, et une barre d'application inférieure.

Par défaut, la mise en page de cet exemple présente l'ordre TalkBack suivant:

Barre d'application supérieure → Exemples de textes 0 à 6 → Bouton d'action flottant (FAB) → Bas Barre d'application

Vous pouvez souhaiter que le lecteur d'écran sélectionne d'abord le bouton d'action flottant. Pour définir un traversalIndex sur un élément Material comme un bouton d'action flottant, procédez comme suit:

@Composable
fun FloatingBox() {
    Box(modifier = Modifier.semantics { isTraversalGroup = true; traversalIndex = -1f }) {
        FloatingActionButton(onClick = {}) {
            Icon(imageVector = Icons.Default.Add, contentDescription = "fab icon")
        }
    }
}

Dans cet extrait, la création d'une zone avec isTraversalGroup définie sur true et définie sur traversalIndex sur la même zone (-1f est inférieur à la valeur par défaut de 0f) signifie que la boîte flottante avant tous les autres éléments à l'écran.

Ensuite, vous pouvez placer la boîte flottante et d'autres éléments dans un échafaudage, qui met en œuvre une mise en page Material Design:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ColumnWithFABFirstDemo() {
    Scaffold(
        topBar = { TopAppBar(title = { Text("Top App Bar") }) },
        floatingActionButtonPosition = FabPosition.End,
        floatingActionButton = { FloatingBox() },
        content = { padding -> ContentColumn(padding = padding) },
        bottomBar = { BottomAppBar { Text("Bottom App Bar") } }
    )
}

TalkBack interagit avec les éléments dans l'ordre suivant:

Bouton d'action flottant → Barre d'application supérieure → Exemples de textes 0 à 6 → Barre d'application inférieure

Ressources supplémentaires