Per impostazione predefinita, il comportamento dello screen reader di accessibilità in un'app Compose viene implementato
nell'ordine di lettura previsto, che di solito è da sinistra a destra e poi dall'alto verso il basso.
Tuttavia, per alcuni tipi di layout di app l'algoritmo non riesce a determinare l'ordine di lettura effettivo senza suggerimenti aggiuntivi. Nelle app basate sulle visualizzazioni, puoi
risolvere questi problemi utilizzando le proprietà traversalBefore
e traversalAfter
.
A partire da Compose 1.5, Compose fornisce 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 impostazione dello stato attivo di TalkBack negli scenari in cui
l'algoritmo di ordinamento predefinito non è appropriato. isTraversalGroup
identifica
i gruppi semanticamente importanti, mentre traversalIndex
regola l'ordine dei
singoli elementi all'interno di questi gruppi. Puoi utilizzare isTraversalGroup
da solo
o con traversalIndex
per un'ulteriore personalizzazione.
Utilizza isTraversalGroup
e traversalIndex
nella tua
app per controllare l'ordine di attraversamento dello screen reader.
Raggruppa elementi con isTraversalGroup
isTraversalGroup
è una proprietà booleana che definisce se un nodo semantica è un gruppo di attraversamento. Questo tipo di nodo ha la funzione di servire come confine o confine nell'organizzazione degli elementi figlio del nodo.
L'impostazione di isTraversalGroup = true
su un nodo fa sì che tutti i nodi secondari vengano visitati prima di essere spostati ad altri elementi. Puoi impostare isTraversalGroup
su nodi non attivabili per screen reader, come Colonne, Righe o Caselle.
L'esempio seguente utilizza isTraversalGroup
. Emette quattro elementi di testo. 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:
Poiché non è stata impostata alcuna semantica, il comportamento predefinito dello screen reader è quello di attraversare gli elementi da sinistra a destra e dall'alto verso il basso. Per via di questa impostazione predefinita, TalkBack legge i frammenti di frase nell'ordine sbagliato:
"Questa frase è in" → "Questa frase è" → "colonna sinistra". → "a destra".
Per ordinare correttamente i frammenti, modifica lo snippet originale in modo da impostare isTraversalGroup
su 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é isTraversalGroup
è impostato specificamente su ogni CardBox
, i confini di CardBox
vengono applicati quando si ordinano gli elementi. In questo caso, viene letto per primo il valore CardBox
sinistro, seguito dal CardBox
destro.
Ora TalkBack legge i frammenti di frase nell'ordine corretto:
"Questa frase è in" → "la colonna di sinistra". → "Questa frase è" → "a destra".
Personalizza ulteriormente l'ordine di attraversamento
traversalIndex
è una proprietà float che consente di personalizzare l'ordine di attraversamento di TalkBack. Se il raggruppamento degli elementi non è sufficiente per il corretto funzionamento di TalkBack, usa traversalIndex
insieme a isTraversalGroup
per personalizzare ulteriormente l'ordinamento dello screen reader.
La proprietà traversalIndex
ha le seguenti caratteristiche:
- Gli elementi con valori
traversalIndex
più bassi hanno la priorità per primo. - Può essere positiva o negativa.
- Il valore predefinito è
0f
. - Interessa solo i nodi attivabili per lo screen reader, ad esempio gli elementi sullo schermo come testo o pulsanti. Ad esempio, l'impostazione di solo
traversalIndex
in una colonna non avrebbe alcun effetto, a meno che anche per la colonna sia impostatoisTraversalGroup
.
L'esempio seguente mostra come utilizzare traversalIndex
e
isTraversalGroup
insieme.
Esempio: Esplora quadrante orologio
Un quadrante orologio è uno scenario comune in cui l'ordinamento di attraversamento standard non funziona. L'esempio in questa sezione è un selettore dell'ora, in cui un utente può scorrere i numeri su un quadrante orologio e selezionare le cifre per le fasce orarie e minuti.
Nel seguente snippet semplificato, è presente un elemento CircularLayout
in cui vengono tracciati 12 numeri, a partire dal numero 12 che si sposta 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 l'ordinamento predefinito da sinistra a destra e dall'alto verso il basso, TalkBack legge i numeri in ordine sbagliato. Per risolvere questo problema, utilizza il valore del contatore incrementale, 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, devi prima rendere CircularLayout
un gruppo di attraversamento e impostare isTraversalGroup = true
. Quindi, man mano che il testo di ogni orologio viene disegnato sul layout, imposta il valore traversalIndex
corrispondente sul valore del contatore.
Poiché il valore del contatore aumenta continuamente, il valore traversalIndex
di ogni valore dell'orologio è maggiore man mano che vengono aggiunti numeri alla schermata: il valore dell'orologio 0 ha un traversalIndex
pari a 0 e il valore dell'orologio 1 ha un valore traversalIndex
pari a 1.
In questo modo viene impostato l'ordine in cui TalkBack legge le letture. Ora i numeri all'interno di CircularLayout
vengono letti nell'ordine previsto.
Poiché i valori traversalIndexes
che sono stati impostati sono relativi solo ad altri indici nello stesso raggruppamento, il resto dell'ordine delle schermate è stato conservato. In altre parole, i cambiamenti semantici mostrati nello snippet di codice precedente modificano solo l'ordine all'interno del quadrante orologio che ha impostato isTraversalGroup = true
.
Tieni presente che, senza impostare la semantica di CircularLayout's
su isTraversalGroup =
true
, le modifiche di traversalIndex
vengono comunque applicate. Tuttavia, senza l'CircularLayout
per associarli, le dodici cifre del quadrante orologio vengono lette per ultime, dopo che tutti gli altri elementi sullo schermo sono stati visitati. Questo accade
perché tutti gli altri elementi hanno un valore predefinito di traversalIndex
di 0f
e gli
elementi di testo dell'orologio vengono letti dopo tutti gli altri elementi 0f
.
Esempio: personalizzare l'ordine di attraversamento per il pulsante di azione mobile
In questo esempio, traversalIndex
e isTraversalGroup
controllano l'ordine di attraversamento di un pulsante di azione mobile (FAB) di Material Design. La base di questo esempio è il seguente layout:
Per impostazione predefinita, il layout di questo esempio ha il seguente ordine di TalkBack:
Barra dell'app in alto → Testi di esempio da 0 a 6 → Pulsante di azione mobile (FAB) → Barra dell'app in basso
È consigliabile che lo screen reader si concentri prima sul FAB. Per impostare traversalIndex
su un elemento Material come 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, se crei una casella con isTraversalGroup
impostato su true
e l'impostazione di traversalIndex
per la stessa casella (-1f
è inferiore al valore predefinito di 0f
), la casella mobile viene visualizzata prima di tutti gli altri elementi sullo schermo.
Quindi, puoi posizionare la scatola mobile e altri elementi in un'impalcatura, in modo da implementare un layout di 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 dell'app superiore → Testi di esempio da 0 a 6 → Barra dell'app in basso
Risorse aggiuntive
- Accessibilità: concetti e tecniche essenziali comuni a tutto lo sviluppo di app per Android
- Crea app accessibili: passaggi chiave che puoi intraprendere per rendere la tua app più accessibile
- Principi per migliorare l'accessibilità delle app. Principi chiave da tenere presenti quando si lavora per rendere l'app più accessibile.
- Test per l'accessibilità: Principi e strumenti di test relativi all'accessibilità di Android