Durchlaufreihenfolge ändern

Die Durchlaufreihenfolge ist die Reihenfolge, in der Bedienungshilfen die UI-Elemente durchlaufen. In einer App zum Verfassen von Inhalten sind die Elemente in der erwarteten Lesereihenfolge angeordnet, also normalerweise von links nach rechts und dann von oben nach unten. Es gibt jedoch einige Szenarien, in denen Compose möglicherweise zusätzliche Hinweise benötigt, um die richtige Lesereihenfolge zu ermitteln.

isTraversalGroup und traversalIndex sind semantische Properties, mit denen Sie die Durchlaufreihenfolge für Dienste zur Barrierefreiheit in Szenarien beeinflussen können, in denen der Standardsortierungsalgorithmus von Compose nicht ausreicht. Mit isTraversalGroup werden semantisch wichtige Gruppen identifiziert, die angepasst werden müssen, während traversalIndex die Reihenfolge der einzelnen Elemente innerhalb dieser Gruppen anpasst. Sie können isTraversalGroup allein verwenden, um anzugeben, dass alle Elemente innerhalb einer Gruppe gemeinsam ausgewählt werden sollen, oder zusammen mit traversalIndex für weitere Anpassungen.

Verwenden Sie isTraversalGroup und traversalIndex in Ihrer App, um die Reihenfolge der Screenreader-Navigation zu steuern.

Elemente für die Navigation gruppieren

isTraversalGroup ist ein boolescher Wert, der angibt, ob ein Semantikknoten eine Durchlaufgruppe ist. Dieser Knotentyp dient als Begrenzung oder Grenze bei der Organisation der untergeordneten Knoten.

Wenn Sie isTraversalGroup = true für einen Knoten festlegen, werden alle untergeordneten Elemente dieses Knotens besucht, bevor andere Elemente aufgerufen werden. Sie können isTraversalGroup auf Knoten festlegen, die nicht vom Screenreader fokussiert werden können, z. B. auf Spalten, Zeilen oder Felder.

Im folgenden Beispiel wird isTraversalGroup verwendet. Es gibt vier Textelemente aus. Die beiden linken Elemente gehören zu einem CardBox-Element, während die beiden rechten Elemente zu einem anderen CardBox-Element gehören:

// 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
        )
    }
}

Der Code erzeugt eine Ausgabe, die in etwa so aussieht:

Layout mit zwei Textspalten, in der linken Spalte steht „Dieser Satz ist in der linken Spalte“ und in der rechten Spalte „Dieser Satz ist rechts“.
Abbildung 1 Ein Layout mit zwei Sätzen (einer in der linken und einer in der rechten Spalte).

Da keine Semantik festgelegt wurde, durchläuft der Screenreader die 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 befindet sich in“ → „Dieser Satz ist“ → „der linken Spalte“. → „auf der rechten Seite“.

Ändern Sie das ursprüngliche Snippet, um die Fragmente richtig anzuordnen. Legen Sie dazu isTraversalGroup auf true fest:

@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 festgelegt ist, werden die CardBox-Grenzen beim Sortieren der Elemente angewendet. In diesem Fall wird zuerst das linke CardBox und dann das rechte CardBox gelesen.

Jetzt liest TalkBack die Satzfragmente in der richtigen Reihenfolge vor:

„Dieser Satz steht in“ → „der linken Spalte“. → „Dieser Satz ist“ → „richtig“.

Durchlaufreihenfolge anpassen

traversalIndex ist eine Float-Property, mit der Sie die TalkBack-Durchlaufreihenfolge anpassen können. Wenn das Gruppieren von Elementen nicht ausreicht, damit TalkBack richtig funktioniert, können Sie die Reihenfolge des Screenreaders mit traversalIndex in Verbindung mit isTraversalGroup weiter anpassen.

Die traversalIndex-Property hat die folgenden Eigenschaften:

  • Elemente mit niedrigeren traversalIndex-Werten haben Vorrang.
  • Kann positiv oder negativ sein.
  • Der Standardwert ist 0f.
  • Damit der Navigationsindex das Navigationsverhalten beeinflussen kann, muss er auf einer Komponente festgelegt werden, die von Bedienungshilfen ausgewählt und fokussiert werden kann, z. B. Bildschirmelemente wie Text oder Schaltflächen.
    • Wenn Sie nur traversalIndex für eine Column aktivieren, hat das keine Auswirkungen, es sei denn, für die Spalte ist auch isTraversalGroup aktiviert.

Das folgende Beispiel zeigt, wie Sie traversalIndex und isTraversalGroup zusammen verwenden können.

Ein Zifferblatt ist ein gängiges Szenario, in dem die Standardreihenfolge nicht funktioniert. Das Beispiel in diesem Abschnitt ist eine Zeitauswahl, bei der Nutzer die Ziffern auf einem Zifferblatt durchlaufen und Ziffern für die Stunden- und Minutenfelder auswählen können.

Ein Zifferblatt mit einer Uhrzeitauswahl darüber.
Abbildung 2. Ein Bild eines Zifferblatts.

Im folgenden vereinfachten Snippet ist ein CircularLayout zu sehen, in dem 12 Zahlen gezeichnet sind, beginnend mit der 12 und im Uhrzeigersinn um den Kreis herum:

@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 Zifferblatt nicht logisch in der Standardreihenfolge von links nach rechts und von oben nach unten gelesen wird, liest TalkBack die Zahlen in der falschen Reihenfolge vor. Verwenden Sie dazu den inkrementellen 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())
    }
}

Um die Durchlaufreihenfolge richtig festzulegen, müssen Sie zuerst CircularLayout zu einer Durchlaufgruppe machen und isTraversalGroup = true festlegen. Wenn Sie dann den Text für jede Uhr in das Layout zeichnen, setzen Sie die entsprechende traversalIndex auf den Zählerwert.

Da der Zählerwert kontinuierlich zunimmt, ist der traversalIndex-Wert jedes Zifferblatts höher, je mehr Ziffern auf dem Bildschirm angezeigt werden. Der Zifferblattwert 0 hat den traversalIndex-Wert 0 und der Zifferblattwert 1 den traversalIndex-Wert 1. So wird die Reihenfolge festgelegt, in der TalkBack sie vorliest. Jetzt werden die Zahlen innerhalb des CircularLayout in der erwarteten Reihenfolge gelesen.

Da die festgelegten traversalIndexes nur relativ zu anderen Indexen innerhalb derselben Gruppierung sind, wurde die restliche Bildschirmanordnung beibehalten. Mit anderen Worten: Die semantischen Änderungen im vorherigen Code-Snippet ändern nur die Reihenfolge innerhalb des Zifferblatts, für das isTraversalGroup = true festgelegt ist.

Auch wenn Sie die CircularLayout's-Semantik nicht auf isTraversalGroup = true festlegen, werden die traversalIndex-Änderungen angewendet. Ohne CircularLayout zum Binden werden die zwölf Ziffern des Zifferblatts jedoch zuletzt gelesen, nachdem alle anderen Elemente auf dem Bildschirm besucht wurden. Das liegt daran, dass alle anderen Elemente eine StandardtraversalIndex von 0f haben und die Textelemente der Uhr nach allen anderen 0f-Elementen gelesen werden.

Überlegungen zu APIs

Beachten Sie bei der Verwendung der APIs für die Navigation Folgendes:

  • isTraversalGroup = true sollte auf dem übergeordneten Element festgelegt werden, das die gruppierten Elemente enthält.
  • traversalIndex sollte auf einer untergeordneten Komponente festgelegt werden, die Semantik enthält und von Bedienungshilfen ausgewählt wird.
  • Achten Sie darauf, dass sich alle untersuchten Elemente auf derselben zIndex-Ebene befinden, da sich dies auch auf die Semantik und die Durchlaufreihenfolge auswirkt.
  • Achten Sie darauf, dass keine Semantik unnötig zusammengeführt wird, da sich dies auf die Komponenten auswirken kann, auf die Durchlaufindizes angewendet werden.