Standardmäßig wird das Screenreader-Verhalten des Screenreaders für Bedienungshilfen in einer App zum Schreiben in der erwarteten Lesereihenfolge implementiert, also von links nach rechts und dann von oben nach unten.
Es gibt jedoch einige Arten von App-Layouts, bei denen der Algorithmus die tatsächliche Lesereihenfolge nicht ohne zusätzliche Hinweise ermitteln kann. In aufrufbasierten Apps können Sie solche Probleme mit den Attributen traversalBefore
und traversalAfter
beheben.
Ab Compose 1.5 stellt Compose eine ebenso flexible API bereit, jedoch mit einem neuen konzeptionellen Modell.
isTraversalGroup
und traversalIndex
sind semantische Eigenschaften, mit denen Sie die Bedienungshilfen und die TalkBack-Fokusreihenfolge in Szenarien steuern können, in denen der Standardsortieralgorithmus nicht geeignet ist. isTraversalGroup
identifiziert semantisch wichtige Gruppen, während traversalIndex
die Reihenfolge einzelner Elemente innerhalb dieser Gruppen anpasst. Sie können isTraversalGroup
allein oder zur weiteren Anpassung mit traversalIndex
verwenden.
Verwenden Sie isTraversalGroup
und traversalIndex
in Ihrer App, um die Durchlaufreihenfolge des Screenreaders festzulegen.
Elemente mit isTraversalGroup
gruppieren
isTraversalGroup
ist ein boolesches Attribut, das definiert, ob ein Knoten Semantik eine Durchlaufgruppe ist. Dieser Knotentyp dient dazu, bei der Organisation der untergeordneten Knoten eine Begrenzung oder einen Rahmen zu bilden.
Wenn Sie isTraversalGroup = true
für einen Knoten festlegen, werden alle untergeordneten Elemente dieses Knotens besucht, bevor zu anderen Elementen gewechselt wird. Sie können isTraversalGroup
auf Knoten festlegen, die nicht für den Screenreader fokussierbar sind, z. B. Spalten, Zeilen oder Felder.
Im folgenden Beispiel wird isTraversalGroup
verwendet. Sie gibt vier Textelemente aus. Die beiden Elemente links gehören zu einem CardBox
-Unterelement, die beiden Elemente rechts gehören zu einem anderen CardBox
-Element:
// 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 ) } }
Die Ausgabe des Codes sieht in etwa so aus:
Da keine Semantik festgelegt wurde, durchsucht der Screenreader Elemente standardmäßig von links nach rechts und von oben nach unten. Aufgrund dieser Standardeinstellung liest TalkBack die Satzfragmente in der falschen Reihenfolge vor:
„Dieser Satz ist in“ → „Dieser Satz ist“ → „die linke Spalte“. → „auf der rechten Seite“.
Damit die Fragmente richtig sortiert werden, ändern Sie das ursprüngliche Snippet so, dass isTraversalGroup
auf true
gesetzt wird:
@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 } ) } }
Da isTraversalGroup
für jede CardBox
explizit festgelegt wird, gelten beim Sortieren der Elemente die CardBox
-Grenzen. In diesem Fall wird zuerst die linke CardBox
gelesen, gefolgt vom rechten CardBox
.
Jetzt liest TalkBack die Satzfragmente in der richtigen Reihenfolge vor:
„Dieser Satz ist in“ → „die linke Spalte“. → „Dieser Satz steht“ → „auf der rechten Seite“.
Durchquerungsreihenfolge weiter anpassen
traversalIndex
ist eine Gleitkommazahl, mit der Sie die Durchlaufreihenfolge von TalkBack anpassen können. Wenn das Gruppieren von Elementen nicht ausreicht, damit TalkBack ordnungsgemäß funktioniert, verwenden Sie traversalIndex
in Verbindung mit isTraversalGroup
, um die Reihenfolge des Screenreaders weiter anzupassen.
Das Attribut traversalIndex
hat die folgenden Eigenschaften:
- Elemente mit niedrigeren
traversalIndex
-Werten werden zuerst priorisiert. - Kann positiv oder negativ sein.
- Der Standardwert ist
0f
. - Betrifft nur Knoten, die für Screenreader fokussiert werden können, z. B. Bildschirmelemente wie Text oder Schaltflächen. Wenn Sie beispielsweise nur
traversalIndex
für eine Spalte festlegen, hat dies keine Auswirkungen, es sei denn, für die Spalte ist auchisTraversalGroup
festgelegt.
Das folgende Beispiel zeigt, wie Sie traversalIndex
und isTraversalGroup
zusammen verwenden können.
Beispiel: Durchlaufendes Zifferblatt
Ein Zifferblatt ist ein gängiges Szenario, in dem die Standarddurchlaufreihenfolge nicht funktioniert. Das Beispiel in diesem Abschnitt ist eine Zeitauswahl, mit der ein Nutzer durch die Zahlen auf einer Uhranzeige blättern und Ziffern für die Stunden- und Minuten-Zeitabschnitte auswählen kann.
Im folgenden vereinfachten Snippet gibt es eine CircularLayout
, in der 12 Zahlen gezeichnet sind, von denen 12 beginnt und sich im Uhrzeigersinn um den Kreis bewegt:
@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()) } }
Da das Ziffernblatt mit der Standardreihenfolge von links nach rechts und von oben nach unten nicht logisch gelesen wird, liest TalkBack die Zahlen in falscher Reihenfolge vor. Um dies zu beheben, verwenden Sie den sich erhöhenden Zählerwert, wie im folgenden Snippet gezeigt:
@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()) } }
Damit die Durchlaufreihenfolge richtig festgelegt werden kann, müssen Sie CircularLayout
als Durchlaufgruppe festlegen und isTraversalGroup = true
festlegen. Wenn dann jeder Uhrtext in das Layout gezeichnet wird, setze die entsprechende traversalIndex
auf den Zählerwert.
Da der Zählerwert kontinuierlich erhöht wird, ist der traversalIndex
jedes Taktwerts größer, wenn dem Bildschirm Zahlen hinzugefügt werden. Der Taktwert 0 hat einen traversalIndex
von 0 und der Taktwert 1 hat eine traversalIndex
von 1.
Auf diese Weise wird die Reihenfolge festgelegt, in der TalkBack vorliest. Jetzt werden die Zahlen in CircularLayout
in der erwarteten Reihenfolge gelesen.
Da die festgelegten traversalIndexes
nur relativ zu anderen Indexen innerhalb derselben Gruppierung sind, wurde die restliche Bildschirmreihenfolge beibehalten. Mit anderen Worten: Die im vorherigen Code-Snippet gezeigten semantischen Änderungen ändern nur die Reihenfolge innerhalb des Ziffernblatts, für das isTraversalGroup = true
festgelegt ist.
Auch wenn Sie die CircularLayout's
-Semantik auf isTraversalGroup =
true
festlegen, gelten die Änderungen an traversalIndex
weiterhin. Ohne CircularLayout
zum Binden werden die zwölf Ziffern des Ziffernblatts jedoch zuletzt gelesen, nachdem alle anderen Elemente auf dem Bildschirm aufgerufen wurden. Das liegt daran, dass alle anderen Elemente die Standard-traversalIndex
von 0f
haben und die Uhrtextelemente nach allen anderen 0f
-Elementen gelesen werden.
Beispiel: Durchlaufreihenfolge für unverankerte Aktionsschaltfläche anpassen
In diesem Beispiel steuern traversalIndex
und isTraversalGroup
die Durchlaufreihenfolge einer unverankerten Material Design-Aktionsschaltfläche (UAS). Grundlage dieses Beispiels ist das folgende Layout:
Standardmäßig hat das Layout in diesem Beispiel die folgende TalkBack-Reihenfolge:
Obere App-Leiste → Beispieltexte 0 bis 6 → unverankerte Aktionsschaltfläche (FAB) → Untere App-Leiste
Vielleicht möchten Sie, dass sich der Screenreader zuerst auf den FAB konzentriert. So legen Sie eine traversalIndex
für ein Material-Element wie einen FAB fest:
@Composable fun FloatingBox() { Box(modifier = Modifier.semantics { isTraversalGroup = true; traversalIndex = -1f }) { FloatingActionButton(onClick = {}) { Icon(imageVector = Icons.Default.Add, contentDescription = "fab icon") } } }
Wenn in diesem Snippet ein Feld erstellt wird, bei dem isTraversalGroup
auf true
gesetzt ist und ein traversalIndex
für dasselbe Feld festgelegt wird (-1f
ist niedriger als der Standardwert von 0f
), wird das unverankerte Feld vor allen anderen Bildschirmelementen angezeigt.
Als Nächstes können Sie den unverankerten Kasten und andere Elemente in einem Gerüst platzieren, das ein Material Design-Layout implementiert:
@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 interagiert in folgender Reihenfolge mit den Elementen:
FAB → Obere App-Leiste → Beispieltexte 0 bis 6 → Untere App-Leiste
Weitere Informationen
- Barrierefreiheit:Diese grundlegenden Konzepte und Techniken, die bei der Entwicklung von Android-Apps üblich sind
- Accessible Apps erstellen:Wichtige Schritte, mit denen Sie die Barrierefreiheit Ihrer App verbessern können
- Prinzipien zur Verbesserung der Barrierefreiheit von Apps:Wichtige Prinzipien, die Sie bei der Verbesserung der Barrierefreiheit Ihrer App beachten sollten
- Test auf Barrierefreiheit:Testprinzipien und -tools für Android-Bedienungshilfen