Mengubah urutan traversal

Urutan traversal adalah urutan layanan aksesibilitas dalam menavigasi elemen UI. Dalam aplikasi Compose, elemen disusun dalam urutan pembacaan yang diharapkan, yang biasanya dari kiri ke kanan, lalu dari atas ke bawah. Namun, ada beberapa skenario saat Compose mungkin memerlukan petunjuk tambahan untuk menentukan urutan pembacaan yang benar.

isTraversalGroup dan traversalIndex adalah properti semantik yang memungkinkan Anda memengaruhi urutan traversal untuk layanan aksesibilitas dalam skenario saat algoritma pengurutan default Compose tidak memadai. isTraversalGroup mengidentifikasi grup yang penting secara semantik yang memerlukan penyesuaian, sedangkan traversalIndex menyesuaikan urutan setiap elemen dalam grup tersebut. Anda dapat menggunakan isTraversalGroup saja untuk menandakan bahwa semua elemen dalam grup harus dipilih bersama, atau dengan traversalIndex untuk penyesuaian lebih lanjut.

Gunakan isTraversalGroup dan traversalIndex di aplikasi Anda untuk mengontrol urutan traversal pembaca layar.

Mengelompokkan elemen untuk penjelajahan

isTraversalGroup adalah properti boolean yang menentukan apakah node semantik adalah grup traversal. Jenis node ini berfungsi sebagai batas dalam mengatur turunan node.

Menetapkan isTraversalGroup = true pada node berarti semua turunan node tersebut akan dikunjungi sebelum berpindah ke elemen lain. Anda dapat menetapkan isTraversalGroup pada node yang dapat difokuskan non-pembaca layar, seperti kolom, baris, atau kotak.

Contoh berikut menggunakan isTraversalGroup. Fungsi ini memunculkan empat elemen teks. Dua elemen di sebelah kiri termasuk dalam satu elemen CardBox, sedangkan dua elemen di sebelah kanan termasuk dalam elemen CardBox lainnya:

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

Kode tersebut menghasilkan output yang mirip dengan berikut ini:

Tata letak dengan dua kolom teks, dengan kolom kiri bertuliskan 'This
  sentence is in the left column' dan kolom kanan bertuliskan 'This sentence is on the right.'
Gambar 1. Tata letak dengan dua kalimat (satu di kolom kiri dan satu di kolom kanan).

Karena tidak ada semantik yang ditetapkan, perilaku default pembaca layar adalah melintasi elemen dari kiri ke kanan dan atas ke bawah. Karena setelan default ini, TalkBack membacakan fragmen kalimat dalam urutan yang salah:

"This sentence is in" → "This sentence is" → "the left column." → "di sebelah kanan".

Untuk mengurutkan fragmen dengan benar, ubah cuplikan asli untuk menetapkan isTraversalGroup ke 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 }
        )
    }
}

Karena isTraversalGroup ditetapkan secara khusus pada setiap CardBox, batas CardBox berlaku saat mengurutkan elemennya. Dalam hal ini, CardBox kiri dibaca terlebih dahulu, diikuti dengan CardBox kanan.

Sekarang, TalkBack membacakan fragmen kalimat dalam urutan yang benar:

"Kalimat ini ada di" → "kolom kiri." → "Kalimat ini ada" → "di sebelah kanan".

Menyesuaikan urutan traversal

traversalIndex adalah properti float yang memungkinkan Anda menyesuaikan urutan traversal TalkBack. Jika pengelompokan elemen tidak cukup agar TalkBack berfungsi dengan benar, gunakan traversalIndex bersama dengan isTraversalGroup untuk menyesuaikan urutan pembaca layar lebih lanjut.

Properti traversalIndex memiliki karakteristik berikut:

  • Elemen dengan nilai traversalIndex yang lebih rendah akan diprioritaskan terlebih dahulu.
  • Dapat berupa nilai positif atau negatif.
  • Nilai default-nya adalah 0f.
  • Agar indeks traversal memengaruhi perilaku traversal, indeks tersebut harus ditetapkan pada komponen yang dapat dipilih dan difokuskan oleh layanan aksesibilitas, seperti elemen di layar seperti teks atau tombol.
    • Menetapkan hanya traversalIndex pada, misalnya, Column tidak akan berpengaruh, kecuali jika kolom tersebut juga memiliki isTraversalGroup yang ditetapkan.

Contoh berikut menunjukkan cara menggunakan traversalIndex dan isTraversalGroup bersama-sama.

Tampilan jam adalah skenario umum saat pengurutan traversal standar tidak berfungsi. Contoh di bagian ini adalah pemilih waktu, tempat pengguna dapat melihat angka pada tampilan jam dan memilih digit untuk slot jam dan menit.

Tampilan jam dengan alat pilih waktu di atasnya.
Gambar 2. Gambar tampilan jam.

Dalam cuplikan yang disederhanakan berikut, ada CircularLayout yang menggambar 12 angka, dimulai dengan 12 dan bergerak searah jarum jam di sekeliling lingkaran:

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

Karena tampilan jam tidak dibaca secara logis dengan urutan default dari kiri ke kanan dan dari atas ke bawah, TalkBack akan membacakan angka-angka tersebut secara tidak berurutan. Untuk memperbaikinya, gunakan nilai penghitung inkremental, seperti yang ditunjukkan dalam cuplikan berikut:

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

Untuk menetapkan urutan traversal dengan benar, jadikan CircularLayout sebagai grup traversal terlebih dahulu dan tetapkan isTraversalGroup = true. Kemudian, saat setiap teks jam digambar ke tata letak, tetapkan traversalIndex yang sesuai ke nilai penghitung.

Karena nilai penghitung terus meningkat, traversalIndex setiap nilai jam akan lebih besar seiring angka ditambahkan ke layar—nilai jam 0 memiliki traversalIndex 0, dan nilai jam 1 memiliki traversalIndex 1. Dengan cara ini, urutan pembacaan TalkBack akan ditetapkan. Sekarang, angka di dalam CircularLayout dibaca dalam urutan yang diharapkan.

Karena traversalIndexes yang telah ditetapkan hanya relatif terhadap indeks lain dalam pengelompokan yang sama, urutan layar lainnya tetap dipertahankan. Dengan kata lain, perubahan semantik yang ditampilkan dalam cuplikan kode sebelumnya hanya mengubah pengurutan dalam tampilan jam yang telah disetel isTraversalGroup = true.

Perhatikan bahwa, tanpa menyetel semantik CircularLayout's ke isTraversalGroup = true, perubahan traversalIndex masih berlaku. Namun, tanpa CircularLayout untuk mengikatnya, dua belas digit tampilan jam akan dibaca terakhir, setelah semua elemen lain di layar dikunjungi. Hal ini terjadi karena semua elemen lainnya memiliki traversalIndex default 0f, dan elemen teks jam dibaca setelah semua elemen 0f lainnya.

Pertimbangan API

Pertimbangkan hal berikut saat menggunakan API traversal:

  • isTraversalGroup = true harus ditetapkan pada induk yang berisi elemen yang dikelompokkan.
  • traversalIndex harus ditetapkan pada komponen turunan yang berisi semantik dan akan dipilih oleh layanan aksesibilitas.
  • Pastikan semua elemen yang Anda selidiki berada pada tingkat zIndex yang sama, karena hal ini juga memengaruhi semantik dan urutan traversal.
  • Pastikan tidak ada semantik yang digabungkan secara tidak perlu, karena hal ini dapat memengaruhi komponen yang menerapkan indeks traversal.