Kontroluj kolejność przechodzenia

Domyślnie zaimplementowane jest działanie czytnika ekranu ułatwień dostępu w aplikacji Compose w oczekiwanej kolejności czytania, czyli zwykle od lewej do prawej, a potem od góry do dołu. Istnieją jednak typy układów aplikacji, w których algorytm nie jest w stanie określić, w rzeczywistości, bez dodatkowych wskazówek. W aplikacjach opartych na widokach możesz: rozwiąż takie problemy za pomocą właściwości traversalBefore i traversalAfter. Od wersji Compose 1.5 dostępna jest równie elastyczny interfejs API, ale nowym modelem koncepcyjnym.

isTraversalGroup i traversalIndex to właściwości semantyczne, które umożliwiają sterowanie ułatwieniami dostępu i kolejności zaznaczenia w TalkBack domyślny algorytm sortowania jest nieodpowiedni. isTraversalGroup identyfikuje grup o znaczeniu semantycznym, natomiast traversalIndex dostosowuje kolejność poszczególnych elementów w tych grupach. Możesz używać tylko nazwy isTraversalGroup, lub za pomocą traversalIndex, aby dostosować je do własnych potrzeb.

Korzystaj z usług isTraversalGroup i traversalIndex w do sterowania kolejnością przechodzenia między czytnikami ekranu.

Grupuj elementy za pomocą funkcji isTraversalGroup

isTraversalGroup to właściwość logiczna, która określa, czy semantyka węzeł to grupa przemierzania. Ten typ węzła to węzeł, którego funkcja ma służyć jako granicę lub obramowania przy organizacji elementów podrzędnych węzła.

Ustawienie isTraversalGroup = true w węźle oznacza, że wszystkie jego elementy podrzędne są odwiedzane przed przeniesieniem do innych. Możesz ustawić isTraversalGroup na węzły, których nie można zaznaczyć w czytniku ekranu, takie jak kolumny, wiersze czy ramki.

W przykładzie poniżej użyto elementu isTraversalGroup. Emisja czterech elementów tekstowych. 2 lewe elementy należą do jednego elementu CardBox, a 2 prawe należą do innego elementu 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
        )
    }
}

Kod generuje dane wyjściowe podobne do tych:

Układ z dwiema kolumnami tekstu, w lewej kolumnie „To”
  jest w lewej kolumnie” a w prawej kolumnie: „To zdanie jest po prawej stronie”.
Rysunek 1. układ z 2 zdaniami (jedno po lewej stronie, i drugą w prawej kolumnie).

Ponieważ nie ustawiono semantyki, czytnik ekranu działa domyślnie możesz przechodzić między elementami z lewej strony na prawą i z góry na dół. Z tego powodu domyślnie, TalkBack odczytuje fragmenty zdań w niewłaściwej kolejności:

„Język tego zdania” → „To zdanie to” → „lewa kolumna”. → „w w prawo”.

Aby poprawnie uporządkować fragmenty, zmień pierwotny fragment kodu na Od isTraversalGroup do 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 }
        )
    }
}

Parametr isTraversalGroup jest ustawiany oddzielnie dla każdego tagu CardBox, dlatego CardBox podczas sortowania elementów są stosowane granice. W tym przypadku lewa strona Najpierw odczytywane jest pole CardBox, a następnie następuje jego CardBox w prawo.

Teraz TalkBack odczytuje fragmenty zdań we właściwej kolejności:

„Język tego zdania” → „lewa kolumna”. → „To zdanie to” → „w w prawo”.

Dalsze dostosowanie kolejności przemierzania

traversalIndex to właściwość zmiennoprzecinkowa, która pozwala dostosować TalkBack kolejność przemierzania. Jeśli zgrupowanie elementów nie jest wystarczające, aby TalkBack działa prawidłowo, używaj traversalIndex w połączeniu z isTraversalGroup, aby jeszcze lepiej dostosować kolejność czytników ekranu.

Właściwość traversalIndex ma te cechy:

  • Elementy o niższych wartościach traversalIndex mają wyższy priorytet.
  • Może być dodatnia lub ujemna.
  • Wartość domyślna to 0f.
  • Dotyczy wyłącznie węzłów, dla których można wybrać czytnik ekranu, takich jak elementy na ekranie, takie jak tekst lub przyciski. Na przykład ustawienie w kolumnie tylko atrybutu traversalIndex sprawi, że nie ma żadnego efektu, chyba że kolumna ma również ustawioną wartość isTraversalGroup.

Poniższy przykład pokazuje, jak za pomocą parametru traversalIndex i isTraversalGroup razem.

Przykład: Przesuwanie tarczy zegara

Tarcza zegara to typowy scenariusz, w którym standardowa kolejność przemierzania nie w naszej pracy. Przykład w tej sekcji to selektor czasu, przez który użytkownik może przeglądać poprzez liczby na tarczy zegara i wybierz cyfry godziny i minuty przedziały czasu.

Tarcza zegara z selektorem czasu nad nią.
Rysunek 2. Obraz tarczy zegara.

W podanym poniżej uproszczonym fragmencie występuje element CircularLayout, w którym 12 są rysowane liczby, rozpoczynając od 12 i przesuwające się w prawo po okręgu:

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

Ponieważ tarcza nie jest odczytywana logicznie przy domyślnych ustawieniach w kolejności od góry do dołu, TalkBack odczytuje liczby w niewłaściwej kolejności. Aby to naprawić użyj rosnącej wartości licznika, jak pokazano w tym fragmencie:

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

Aby prawidłowo ustawić kolejność przechodzenia między elementami, element CircularLayout grupy przemierzania i ustaw isTraversalGroup = true. Potem, w miarę jak każdy tekst na zegarze narysowanych w układzie, ustaw odpowiadające mu traversalIndex na licznik .

Ponieważ wartość licznika stale rośnie, każda wartość zegara Kolumna traversalIndex jest większa, ponieważ na ekranie pojawiają się liczby – wartość zegara to 0. ma traversalIndex o wartości 0, a wartość zegara 1 ma wartość traversalIndex równą 1. Dzięki temu jest ustawiona kolejność ich odczytywania przez TalkBack. Liczby wewnątrz ciągu CircularLayout są odczytywane w oczekiwanej kolejności.

Ponieważ wartości traversalIndexes, które zostały ustawione, odnoszą się tylko do innych indeksów w tej samej grupie, reszta kolejności ekranów została i ochrony danych. Inaczej mówiąc, zmiany semantyczne widoczne w poprzednim kodzie zmienić tylko kolejność w obrębie tarczy zegara, która ma Ustawiono isTraversalGroup = true.

Pamiętaj, że jeśli semantyka CircularLayout's nie zostanie ustawiona na isTraversalGroup = true, zmiany typu traversalIndex będą nadal obowiązywać. Jednak bez stosowania CircularLayout, aby je powiązać, odczytywane jest 12 cyfr tarczy zegara ostatni, po odwiedzeniu wszystkich pozostałych elementów na ekranie. Dzieje się tak bo wszystkie pozostałe elementy mają domyślną wartość traversalIndex o wartości 0f, a Elementy tekstowe zegara są odczytywane po wszystkich pozostałych elementach 0f.

Przykład: dostosowanie kolejności przechodzenia między elementami pływającego przycisku polecenia

W tym przykładzie traversalIndex i isTraversalGroup określają kolejność przechodzenia między pływającym przyciskiem polecenia Material Design. Podstawa w tym przykładzie:

układ z górnym paskiem aplikacji, przykładowym tekstem, pływającym przyciskiem polecenia;
  na dolnym pasku aplikacji.
Rysunek 3. Układ z górnym paskiem aplikacji, przykładowym tekstem, pływającym przyciskiem polecenia i dolny pasek aplikacji.

Domyślnie układ w tym przykładzie ma następującą kolejność w TalkBack:

Górny pasek aplikacji → Przykładowe teksty od 0 do 6 → pływający przycisk polecenia (FAB) → Dół Pasek aplikacji

Czytnik ekranu może w pierwszej kolejności skupić się na przycisku PPP. Aby ustawić traversalIndex w elemencie Material Design, np. przycisku PPP, wykonaj te czynności:

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

W tym fragmencie kodu utworzenie pola z atrybutem W zakresie isTraversalGroup ustawiono formułę true i ustawienie traversalIndex w tym samym polu (Wartość -1f jest mniejsza od domyślnej wartości 0f) oznacza, że pływająca ramka znajduje się przed wszystkimi innymi elementami ekranowymi.

Następnie możesz umieścić pływające pole i inne elementy w rusztowaniu, implementuje układ 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 współdziała z elementami w tej kolejności:

FAB → górny pasek aplikacji → przykładowe teksty 0–6 → dolny pasek aplikacji

Dodatkowe materiały