Urutan traversal kontrol

Secara default, perilaku pembaca layar aksesibilitas di aplikasi Compose diterapkan dalam urutan membaca yang diharapkan, yang biasanya dari kiri ke kanan, lalu dari atas ke bawah. Namun, ada beberapa jenis tata letak aplikasi yang tidak dapat ditentukan oleh algoritma urutan pembacaan yang sebenarnya tanpa petunjuk tambahan. Di aplikasi berbasis tampilan, Anda dapat memperbaiki masalah tersebut menggunakan properti traversalBefore dan traversalAfter. Mulai Compose 1.5, Compose menyediakan API yang sama fleksibelnya, tetapi dengan sebuah model konseptual yang baru.

isTraversalGroup dan traversalIndex adalah properti semantik yang memungkinkan Anda mengontrol aksesibilitas dan urutan fokus TalkBack dalam skenario algoritma penyortiran secara default adalah tidak sesuai. isTraversalGroup mengidentifikasi grup penting secara semantik, sedangkan traversalIndex menyesuaikan urutan elemen individu dalam kelompok-kelompok tersebut. Anda hanya dapat menggunakan isTraversalGroup, atau dengan traversalIndex untuk penyesuaian lebih lanjut.

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

Mengelompokkan elemen dengan isTraversalGroup

isTraversalGroup adalah properti boolean yang menentukan apakah semantik node adalah grup traversal. Jenis {i>node<i} ini adalah {i>node<i} yang fungsinya akan melayani sebagai pembatas atau pembatas dalam mengatur turunan node.

Menetapkan isTraversalGroup = true pada node berarti bahwa semua turunan dari node tersebut dikunjungi sebelum pindah ke elemen lain. Anda dapat menyetel isTraversalGroup di node yang dapat difokuskan pembaca non-layar, seperti Kolom, Baris, atau Kotak.

Contoh berikut menggunakan isTraversalGroup. Kode ini memancarkan empat elemen teks. Tujuan dua elemen kiri adalah milik satu elemen CardBox, sedangkan dua elemen kanan termasuk dalam elemen CardBox lain:

// 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 ini menghasilkan output yang mirip dengan berikut ini:

Tata letak dengan dua kolom teks, dengan kolom kiri bertuliskan &#39;This&#39;
  kalimat ini ada di kolom sebelah kiri dan kolom kanan yang bertuliskan &#39;Kalimat ini ada di sebelah kanan.&#39;
Gambar 1. Tata letak dengan dua kalimat (satu di sebelah kiri dan satu di kolom sebelah kanan).

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

"Kalimat ini dalam" → "Kalimat ini" → "kolom kiri". → "di benar."

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, CardBox batas-batas berlaku saat menyortir elemen-elemennya. Dalam hal ini, kolom kiri CardBox dibaca terlebih dahulu, diikuti dengan CardBox yang tepat.

Kini, TalkBack membacakan fragmen kalimat dalam urutan yang benar:

"Kalimat ini dalam" → "kolom kiri". → "Kalimat ini" → "di benar."

Sesuaikan urutan traversal lebih lanjut

traversalIndex adalah properti float yang memungkinkan Anda menyesuaikan TalkBack urutan lintasan. Jika mengelompokkan elemen bersama-sama tidak cukup bagi TalkBack untuk berfungsi dengan benar, gunakan traversalIndex bersama isTraversalGroup untuk menyesuaikan pengurutan pembaca layar lebih lanjut.

Properti traversalIndex memiliki karakteristik berikut:

  • Elemen dengan nilai traversalIndex yang lebih rendah akan diprioritaskan terlebih dahulu.
  • Bisa positif atau negatif.
  • Nilai default-nya adalah 0f.
  • Hanya memengaruhi node yang dapat difokuskan pembaca layar, seperti elemen di layar seperti teks atau tombol. Misalnya, menetapkan hanya traversalIndex pada kolom akan tidak berpengaruh, kecuali jika kolom memiliki isTraversalGroup yang ditetapkan di dalamnya.

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

Contoh: Tampilan jam Traverse

Tampilan jam adalah skenario umum di mana urutan traversal standar tidak Anda. Contoh di bagian ini adalah pemilih waktu, di mana pengguna dapat melewati melihat angka pada tampilan jam dan pilih digit untuk jam dan menit slot waktu.

Tampilan jam dengan pemilih waktu di atasnya.
Gambar 2. Gambar tampilan jam.

Dalam cuplikan yang disederhanakan berikut, terdapat CircularLayout dengan 12 angka akan digambar, dimulai dengan 12 dan bergerak searah jarum jam di sekitar 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 default{i> <i}kiri-ke-kanan dan dengan pengurutan dari atas ke bawah, TalkBack membacakan angka secara tidak berurutan. Untuk meralat gunakan nilai penghitung yang bertambah, seperti 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 pengurutan traversal dengan benar, pertama-tama buat CircularLayout sebagai traversal group dan setel isTraversalGroup = true. Kemudian, karena setiap teks jam digambar ke tata letak, tetapkan traversalIndex yang sesuai ke penghitung dengan sejumlah nilai.

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

Karena traversalIndexes yang telah ditetapkan hanya berhubungan dengan indeks dalam pengelompokan yang sama, urutan layar lainnya telah dipertahankan. Dengan kata lain, perubahan semantik yang ditunjukkan dalam kode sebelumnya hanya mengubah urutan dalam tampilan jam yang memiliki isTraversalGroup = true disetel.

Perlu diketahui bahwa tanpa menetapkan semantik CircularLayout's ke isTraversalGroup = true, perubahan traversalIndex tetap 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.

Contoh: Menyesuaikan urutan traversal untuk tombol tindakan mengambang

Dalam contoh ini, traversalIndex dan isTraversalGroup mengontrol pengurutan traversal tombol tindakan mengambang (FAB) Desain Material. Dasar dari contoh ini adalah tata letak berikut:

Tata letak dengan panel aplikasi atas, teks contoh, tombol aksi mengambang, dan
  panel aplikasi bawah.
Gambar 3. Tata letak dengan panel aplikasi atas, teks contoh, tombol aksi mengambang, dan panel aplikasi bawah.

Secara default, tata letak dalam contoh ini memiliki urutan TalkBack berikut:

Panel Aplikasi Atas → Contoh teks 0 sampai 6 → tombol tindakan mengambang (FAB) → Bawah Panel Aplikasi

Anda mungkin ingin pembaca layar fokus terlebih dahulu pada FAB. Untuk menyetel traversalIndex pada elemen Material seperti FAB, lakukan hal berikut:

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

Dalam cuplikan ini, membuat kotak dengan isTraversalGroup ditetapkan ke true dan menyetel traversalIndex di kotak yang sama (-1f lebih rendah dari nilai default 0f) berarti kotak mengambang ditempatkan sebelum semua elemen lain di layar.

Selanjutnya, Anda dapat meletakkan kotak mengambang dan elemen lainnya ke dalam perancah, yang mengimplementasikan tata letak Desain Material:

@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 berinteraksi dengan elemen dalam urutan berikut:

FAB → Panel Aplikasi Atas → Teks contoh 0 sampai 6 → Panel Aplikasi Bawah

Referensi lainnya