Ordine di attraversamento del controllo

Per impostazione predefinita, viene implementato il comportamento dello screen reader di accessibilità in un'app Compose nell'ordine di lettura previsto, che solitamente è da sinistra a destra e poi dall'alto verso il basso. Tuttavia, esistono alcuni tipi di layout delle app in cui l'algoritmo non può determinare l'ordine di lettura effettivo senza ulteriori suggerimenti. Nelle app basate sulle visualizzazioni, puoi: risolvi questi problemi usando le proprietà traversalBefore e traversalAfter. A partire da Compose 1.5, Compose offre un'API altrettanto flessibile, ma con un nuovo modello concettuale.

isTraversalGroup e traversalIndex sono proprietà semantiche che consentono di controllare l'accessibilità e l'ordine di messa a fuoco di TalkBack negli scenari in cui l'algoritmo di ordinamento predefinito non è appropriato. isTraversalGroup identifica importanti dal punto di vista semantico, mentre traversalIndex regola l'ordine i singoli elementi all'interno di questi gruppi. Puoi usare isTraversalGroup da solo, o con traversalIndex per un'ulteriore personalizzazione.

Utilizza isTraversalGroup e traversalIndex in per controllare l'ordine di attraversamento dello screen reader.

Raggruppa gli elementi con isTraversalGroup

isTraversalGroup è una proprietà booleana che definisce se una semantica Node è un gruppo di attraversamento. Questo tipo di nodo è quello la cui funzione è servire come confine nell'organizzazione degli elementi secondari del nodo.

L'impostazione di isTraversalGroup = true su un nodo significa che tutti i relativi figli vengono visitate prima di passare ad altri elementi. Puoi impostare isTraversalGroup su nodi non attivabili per gli screen reader, come Colonne, Righe o Riquadri.

L'esempio seguente utilizza isTraversalGroup. Emette quattro elementi di testo. La i due elementi a sinistra appartengono a un elemento CardBox, mentre i due elementi a destra appartengono a un altro 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
        )
    }
}

Il codice produce un output simile al seguente:

Layout con due colonne di testo, con la colonna sinistra con la dicitura "Questo
  la frase è nella colonna a sinistra" e nella colonna di destra "Questa frase è a destra".
Figura 1. Un layout con due frasi (una a sinistra colonna e uno nella colonna di destra).

Poiché non è stata impostata alcuna semantica, il comportamento predefinito dello screen reader è per attraversare gli elementi da sinistra a destra e dall'alto verso il basso. Per questo motivo per impostazione predefinita, TalkBack legge i frammenti della frase nell'ordine sbagliato:

"Questa frase è in" → "Questa frase è" → "la colonna a sinistra." → "sul a destra".

Per ordinare correttamente i frammenti, modifica lo snippet originale in modo da impostare Da 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 }
        )
    }
}

Poiché il valore isTraversalGroup viene impostato specificamente su ogni CardBox, il valore di CardBox i confini vengono applicati durante l'ordinamento degli elementi. In questo caso, il riquadro a sinistra Viene letto per primo CardBox, seguito da CardBox a destra.

A questo punto, TalkBack legge i frammenti della frase nell'ordine corretto:

"Questa frase è in" → "la colonna a sinistra." → "Questa frase è" → "sul a destra".

Personalizzare ulteriormente l'ordine di attraversamento

traversalIndex è una proprietà mobile che ti consente di personalizzare TalkBack ordine di attraversamento. Se raggruppare gli elementi non è sufficiente per consentire a TalkBack funzionare correttamente, usa traversalIndex insieme a isTraversalGroup per personalizzare ulteriormente l'ordine dello screen reader.

La proprietà traversalIndex ha le seguenti caratteristiche:

  • Gli elementi con valori traversalIndex più bassi hanno la priorità per primi.
  • Può essere positivo o negativo.
  • Il valore predefinito è 0f.
  • Interessa solo i nodi attivabili per lo screen reader, come gli elementi sullo schermo come testo o pulsanti. Ad esempio, se imposti solo traversalIndex in una colonna, non hanno effetto, a meno che sulla colonna non sia impostato anche il valore isTraversalGroup.

L'esempio seguente mostra come utilizzare traversalIndex e isTraversalGroup insieme.

Esempio: spostamento quadrante orologio

Un quadrante orologio è uno scenario comune in cui l'ordine di attraversamento standard non al lavoro. L'esempio in questa sezione è un selettore di orario, in cui un utente può attraverso i numeri su un quadrante orologio e seleziona le cifre per l'ora e i minuti slot machine.

Un quadrante orologio con un selettore ora sopra.
Figura 2. L'immagine di un quadrante orologio.

Nel seguente snippet semplificato è presente un elemento CircularLayout in cui 12 vengono tracciati numeri che iniziano con 12 e si spostano in senso orario intorno al cerchio:

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

Poiché il quadrante orologio non viene letto logicamente con i valori predefiniti da sinistra a destra e dall'alto verso il basso, TalkBack legge i numeri in ordine non corretto. Per rettificare utilizza il valore incrementale del contatore, come mostrato nello snippet seguente:

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

Per impostare correttamente l'ordine di attraversamento, per prima cosa imposta CircularLayout come gruppo di attraversamento e imposta isTraversalGroup = true. Poi, poiché il testo di ogni orologio viene disegnato sul layout, imposta il valore traversalIndex corrispondente sul contatore valore.

Dato che il valore del contatore aumenta continuamente, il valore di ogni orologio traversalIndex è maggiore quando vengono aggiunti numeri allo schermo: il valore dell'orologio è 0 ha un traversalIndex pari a 0 e il valore dell'orologio 1 ha un traversalIndex di 1. In questo modo, viene impostato l'ordine in cui TalkBack le legge. I numeri all'interno di CircularLayout vengono letti nell'ordine previsto.

Poiché i traversalIndexes che sono stati impostati sono relativi solo ad altri indici all'interno dello stesso raggruppamento, il resto dell'ordinamento delle schermate è vengono conservati. In altre parole, la semantica mostrata nel codice precedente modificare solo l'ordine all'interno del quadrante orologio che ha isTraversalGroup = true impostato.

Tieni presente che, senza impostare la semantica di CircularLayout's su isTraversalGroup = true, le modifiche relative a traversalIndex continuano a essere applicate. Tuttavia, senza CircularLayout per associarli, vengono lette le dodici cifre del quadrante orologio dopo aver visitato tutti gli altri elementi sullo schermo. Ciò si verifica perché tutti gli altri elementi hanno un valore predefinito per traversalIndex di 0f, mentre gli elementi di testo dell'orologio vengono letti dopo tutti gli altri elementi 0f.

Esempio: personalizza l'ordine di attraversamento per il pulsante di azione mobile

In questo esempio, traversalIndex e isTraversalGroup controllano il ordine di attraversamento di un pulsante di azione mobile (FAB) di Material Design. Le basi di questo esempio è il seguente layout:

Un layout con una barra delle app in alto, testo di esempio, un pulsante di azione mobile e
  una barra delle app in basso.
Figura 3. Layout con una barra delle app in alto, testo di esempio, un pulsante di azione mobile e una barra delle app in basso.

Per impostazione predefinita, il layout in questo esempio ha il seguente ordine di TalkBack:

Barra delle app in alto → Testi di esempio da 0 a 6 → pulsante di azione mobile (FAB) → In basso Barra delle app

Potresti volere che lo screen reader si concentri innanzitutto sul FAB. Per impostare un traversalIndex su un elemento Material, ad esempio un FAB:

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

In questo snippet, la creazione di un riquadro isTraversalGroup impostata su true e con traversalIndex nella stessa casella (-1f è inferiore al valore predefinito di 0f) significa che la casella mobile viene prima di tutti gli altri elementi sullo schermo.

Successivamente, puoi inserire la scatola mobile e altri elementi in uno scaffold, implementa un layout 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 interagisce con gli elementi nel seguente ordine:

FAB → Barra delle app in alto → Testi di esempio da 0 a 6 → Barra delle app in basso

Risorse aggiuntive