Controla el orden de recorrido

De forma predeterminada, se implementa el comportamiento del lector de pantalla de accesibilidad en una app de Compose. en el orden de lectura esperado, que suele ser de izquierda a derecha y, luego, de arriba a abajo. Sin embargo, hay algunos tipos de diseños de apps en los que el algoritmo no puede determinar el orden de lectura real sin sugerencias adicionales. En las aplicaciones basadas en vistas, puedes hacer lo siguiente: corrige estos problemas con las propiedades traversalBefore y traversalAfter. A partir de Compose 1.5, Compose proporciona una API igual de flexible, pero con un nuevo modelo conceptual.

isTraversalGroup y traversalIndex son propiedades semánticas que te permiten controlar la accesibilidad y el orden de enfoque de TalkBack en situaciones en las que la el algoritmo de ordenamiento predeterminado no es apropiado. isTraversalGroup identifica grupos semánticamente importantes, mientras que traversalIndex ajusta el orden de elementos individuales dentro de esos grupos. Puedes usar solo isTraversalGroup, o con traversalIndex para una mayor personalización.

Usa isTraversalGroup y traversalIndex en tu para controlar el orden de recorrido del lector de pantalla.

Agrupa elementos con isTraversalGroup

isTraversalGroup es una propiedad booleana que define si una semántica es un grupo de recorrido. Este tipo de nodo es aquel cuya función es entregar como límite o borde en la organización de los elementos secundarios del nodo.

Configurar isTraversalGroup = true en un nodo significa que todos los elementos secundarios de ese nodo antes de pasar a otros elementos. Puedes configurar isTraversalGroup en nodos enfocables de lectores que no son de pantalla, como Columnas, Filas o Cuadros.

En el siguiente ejemplo, se usa isTraversalGroup. Emite cuatro elementos de texto. El dos elementos de la izquierda pertenecen a un elemento CardBox, mientras que los dos elementos de la derecha pertenecen a otro elemento 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
        )
    }
}

El código produce un resultado similar al siguiente:

Diseño con dos columnas de texto, en la columna izquierda se lee “This”
  "en la columna de la izquierda" y la columna de la derecha, que dice "Esta oración está a la derecha".
Figura 1: Un diseño con dos oraciones (una a la izquierda) y uno en la de la derecha).

Como no se estableció una semántica, el comportamiento predeterminado del lector de pantalla es para recorrer los elementos de izquierda a derecha y de arriba abajo. Por este motivo De forma predeterminada, TalkBack lee los fragmentos de las oraciones en el orden incorrecto:

"Esta oración está en" → "Esta oración es" → "la columna de la izquierda". → "en la bien".

Para ordenar los fragmentos correctamente, modifica el fragmento original para establecer De isTraversalGroup a 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 }
        )
    }
}

Como isTraversalGroup se establece específicamente en cada CardBox, la CardBox se aplican límites al ordenar sus elementos. En este caso, el lado izquierdo CardBox se lee primero y, luego, la CardBox derecha.

Ahora, TalkBack lee los fragmentos de oraciones en el orden correcto:

"Esta oración está en" → "la columna de la izquierda". → "Esta oración es" → "en la bien".

Cómo personalizar aún más el orden de recorrido

traversalIndex es una propiedad de número de punto flotante que te permite personalizar TalkBack orden transversal. Si agrupar elementos no es suficiente para que TalkBack funcionan correctamente, usa traversalIndex junto con isTraversalGroup para personalizar aún más el orden del lector de pantalla.

La propiedad traversalIndex tiene las siguientes características:

  • Los elementos con valores de traversalIndex más bajos se priorizan primero.
  • Pueden ser positivas o negativas.
  • El valor predeterminado es 0f.
  • Solo afecta a los nodos que se pueden enfocar en el lector de pantalla, como elementos en pantalla, como el texto o los botones. Por ejemplo, configurar solo traversalIndex en una columna no tienen efecto, a menos que la columna también tenga configurado isTraversalGroup.

En el siguiente ejemplo, se muestra cómo puedes usar traversalIndex y isTraversalGroup juntos.

Ejemplo: Desviar la cara de reloj

Una carátula es un escenario común donde el orden de recorrido estándar no el trabajo. El ejemplo de esta sección es un selector de hora, en el que un usuario puede por los números de una cara de reloj y seleccionar dígitos para la hora y los minutos las ranuras restantes.

Una cara de reloj con un selector de hora encima.
Figura 2: Imagen de una cara de reloj.

En el siguiente fragmento simplificado, hay un CircularLayout en el que 12 se dibujan números que comienzan con 12 y se mueven en el sentido de las manecillas del reloj alrededor del círculo:

@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())
    }
}

Como la cara de reloj no se lee lógicamente con la opción predeterminada de izquierda a derecha y en orden descendente, TalkBack lee los números de forma desordenada. Para rectificar Para esto, utiliza el valor del contador creciente, como se muestra en el siguiente fragmento:

@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())
    }
}

Para establecer correctamente el orden de recorrido, primero debes hacer que CircularLayout sea un elemento grupo de recorrido y configura isTraversalGroup = true. Luego, como cada texto de reloj se dibujado en el diseño, establece su traversalIndex correspondiente en el contador valor.

Debido a que el valor del contador aumenta continuamente, el valor de traversalIndex es más grande a medida que se agregan números a la pantalla (el valor de reloj es 0). tiene un traversalIndex de 0, y el valor de reloj 1 tiene un traversalIndex de 1. De esta manera, se establece el orden en el que TalkBack lee las lecturas. Ahora bien, los números dentro de CircularLayout se leen en el orden esperado.

Como los traversalIndexes que se configuraron solo son relativos a otros dentro de la misma agrupación, el resto del orden de la pantalla se y cómo se preservan. En otras palabras, los cambios semánticos que se muestran en el código anterior solo modifican el orden dentro de la cara de reloj que tiene Se estableció isTraversalGroup = true.

Ten en cuenta que, si no se establece la semántica de CircularLayout's en isTraversalGroup = true, se seguirán aplicando los cambios de traversalIndex. Sin embargo, sin las CircularLayout para vincularlos, se leen los doce dígitos de la cara de reloj. por último, después de haber visitado todos los demás elementos de la pantalla. Esto ocurre porque todos los demás elementos tienen un traversalIndex predeterminado de 0f, y el los elementos de texto del reloj se leen después de todos los demás elementos 0f.

Ejemplo: Personaliza el orden de recorrido para el botón de acción flotante

En este ejemplo, traversalIndex y isTraversalGroup controlan la Orden de recorrido de un botón de acción flotante (BAF) de Material Design. La base de este ejemplo es el siguiente diseño:

Un diseño con una barra superior de la app, texto de ejemplo, un botón de acción flotante y
  una barra inferior de la aplicación.
Figura 3: Diseño con una barra superior de la app, texto de ejemplo, un botón de acción flotante y una barra inferior.

De forma predeterminada, el diseño de este ejemplo tiene el siguiente orden de TalkBack:

Barra superior de la app → Textos de ejemplo 0 a 6 → botón de acción flotante (BAF) → Parte inferior Barra de la app

Es posible que quieras que el lector de pantalla se enfoque primero en el BAF. Para establecer un traversalIndex en un elemento de Material, como un BAF, haz lo siguiente:

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

En este fragmento, la creación de un cuadro con Se estableció isTraversalGroup en true y se estableció traversalIndex en el mismo cuadro (-1f es menor que el valor predeterminado de 0f) significa que el cuadro flotante aparece antes que los demás elementos en pantalla.

Luego, puedes colocar la caja flotante y otros elementos en un andamiaje, implementa un diseño de 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 interactúa con los elementos en el siguiente orden:

BAF → Barra superior de la app → Textos de ejemplo 0 a 6 → Barra inferior de la app

Recursos adicionales