Aksesibilitas dalam Compose

Aplikasi yang ditulis di Compose harus mendukung aksesibilitas bagi pengguna dengan kebutuhan yang berbeda. Layanan aksesibilitas digunakan untuk mengubah apa yang ditampilkan di layar ke format yang lebih sesuai bagi pengguna dengan kebutuhan tertentu. Untuk mendukung layanan aksesibilitas, aplikasi menggunakan API pada framework Android untuk mengekspos informasi semantik tentang elemen UI-nya. Framework Android kemudian akan menginformasikan layanan aksesibilitas tentang informasi semantik ini. Setiap layanan aksesibilitas dapat memilih cara terbaik untuk mendeskripsikan aplikasi kepada pengguna. Android menyediakan beberapa layanan aksesibilitas, termasuk Talkback dan Tombol Akses.

Semantik

Compose menggunakan properti semantik untuk meneruskan informasi ke layanan aksesibilitas. Properti Semantik memberikan informasi tentang elemen UI yang ditampilkan kepada pengguna. Sebagian besar composable bawaan seperti Text dan Button mengisi properti semantik ini dengan informasi yang diambil dari composable dan turunannya. Beberapa pengubah seperti toggleable dan clickable juga akan menetapkan properti semantik tertentu. Namun, terkadang framework membutuhkan lebih banyak informasi untuk memahami cara mendeskripsikan elemen UI kepada pengguna.

Dokumen ini menjelaskan berbagai situasi yang mengharuskan Anda menambahkan informasi tambahan ke composable secara eksplisit agar dapat dijelaskan dengan benar ke framework Android. Bagian ini juga menjelaskan cara mengganti informasi semantik sepenuhnya untuk composable tertentu. Ini mengasumsikan pemahaman dasar tentang aksesibilitas di Android.

Kasus penggunaan umum

Untuk membantu orang yang memerlukan aksesibilitas agar berhasil menggunakan aplikasi Anda, aplikasi Anda harus mengikuti praktik terbaik yang dijelaskan di halaman ini.

Mempertimbangkan ukuran target sentuh minimum

Elemen apa pun di layar yang dapat diklik, disentuh, atau berinteraksi dengan seseorang harus cukup besar untuk interaksi yang dapat diandalkan. Saat mengubah ukuran elemen ini, pastikan untuk menyetel ukuran minimum ke 48 dp agar dapat mengikuti Panduan Aksesibilitas Desain Material dengan benar.

Komponen material—seperti Checkbox, RadioButton, Switch, Slider, dan Surface—menyetel ukuran minimum ini secara internal, tetapi hanya saat komponen dapat menerima tindakan pengguna. Misalnya, jika Checkbox memiliki parameter onCheckedChange yang disetel ke nilai non-null, parameter tersebut akan menyertakan padding agar memiliki lebar dan tinggi minimal 48 dp.

@Composable
private fun CheckableCheckbox() {
    Checkbox(checked = true, onCheckedChange = {})
}

Jika parameter onCheckedChange disetel ke null, padding tidak disertakan karena komponen tidak dapat berinteraksi secara langsung.

@Composable
private fun NonClickableCheckbox() {
    Checkbox(checked = true, onCheckedChange = null)
}

Saat menerapkan kontrol pemilihan seperti Switch, RadioButton, atau Checkbox, Anda biasanya menaikkan perilaku yang dapat diklik ke container induk, menyetel callback klik pada composable menjadi null, dan menambahkan pengubah toggleable atau selectable ke composable induk.

@Composable
private fun CheckableRow() {
    MaterialTheme {
        var checked by remember { mutableStateOf(false) }
        Row(
            Modifier
                .toggleable(
                    value = checked,
                    role = Role.Checkbox,
                    onValueChange = { checked = !checked }
                )
                .padding(16.dp)
                .fillMaxWidth()
        ) {
            Text("Option", Modifier.weight(1f))
            Checkbox(checked = checked, onCheckedChange = null)
        }
    }
}

Jika ukuran composable yang dapat diklik lebih kecil dari ukuran target sentuh minimum, Compose masih akan meningkatkan ukuran target sentuh. Ini dilakukan dengan memperluas ukuran target sentuh di luar batas composable.

Pada contoh berikut, kami membuat Box yang dapat diklik dengan ukuran sangat kecil. Area target sentuh otomatis diperluas di luar batas Box, sehingga mengetuk di samping Box tetap akan memicu peristiwa klik.

@Composable
private fun SmallBox() {
    var clicked by remember { mutableStateOf(false) }
    Box(
        Modifier
            .size(100.dp)
            .background(if (clicked) Color.DarkGray else Color.LightGray)
    ) {
        Box(
            Modifier
                .align(Alignment.Center)
                .clickable { clicked = !clicked }
                .background(Color.Black)
                .size(1.dp)
        )
    }
}

Untuk mencegah kemungkinan tumpang tindih di antara area sentuh composable yang berbeda, Anda harus selalu menggunakan ukuran minimum yang cukup besar untuk composable. Dalam contoh yang kita gunakan, ini berarti menggunakan pengubah sizeIn untuk menyetel ukuran minimum kotak bagian dalam:

@Composable
private fun LargeBox() {
    var clicked by remember { mutableStateOf(false) }
    Box(
        Modifier
            .size(100.dp)
            .background(if (clicked) Color.DarkGray else Color.LightGray)
    ) {
        Box(
            Modifier
                .align(Alignment.Center)
                .clickable { clicked = !clicked }
                .background(Color.Black)
                .sizeIn(minWidth = 48.dp, minHeight = 48.dp)
        )
    }
}

Menambahkan label klik

Anda dapat menggunakan label klik untuk menambahkan makna semantik ke perilaku klik composable. Label klik menjelaskan apa yang akan terjadi saat pengguna berinteraksi dengan composable. Layanan aksesibilitas menggunakan label klik untuk membantu mendeskripsikan aplikasi kepada pengguna dengan kebutuhan tertentu.

Setel label klik dengan meneruskan parameter dalam pengubah clickable:

@Composable
private fun ArticleListItem(openArticle: () -> Unit) {
    Row(
        Modifier.clickable(
            // R.string.action_read_article = "read article"
            onClickLabel = stringResource(R.string.action_read_article),
            onClick = openArticle
        )
    ) {
        // ..
    }
}

Atau, jika tidak memiliki akses ke pengubah yang dapat diklik, Anda dapat menyetel label klik di pengubah semantik:

@Composable
private fun LowLevelClickLabel(openArticle: () -> Boolean) {
    // R.string.action_read_article = "read article"
    val readArticleLabel = stringResource(R.string.action_read_article)
    Canvas(
        Modifier.semantics {
            onClick(label = readArticleLabel, action = openArticle)
        }
    ) {
        // ..
    }
}

Menjelaskan elemen visual

Saat Anda menentukan composable Image atau Icon, tidak ada cara otomatis bagi framework Android untuk memahami apa yang sedang ditampilkan. Anda harus meneruskan deskripsi teks dari elemen visual.

Bayangkan layar tempat pengguna dapat berbagi halaman saat ini dengan teman. Layar ini berisi ikon berbagi yang dapat diklik:

Strip ikon yang dapat diklik, dengan

Berdasarkan ikon tersebut saja, framework Android tidak dapat mengetahui cara mendeskripsikannya kepada pengguna dengan gangguan penglihatan. Framework Android memerlukan deskripsi teks tambahan untuk ikonnya.

Parameter contentDescription digunakan untuk mendeskripsikan elemen visual. Anda harus menggunakan string yang dilokalkan, karena ini akan dikomunikasikan kepada pengguna.

@Composable
private fun ShareButton(onClick: () -> Unit) {
    IconButton(onClick = onClick) {
        Icon(
            imageVector = Icons.Filled.Share,
            contentDescription = stringResource(R.string.label_share)
        )
    }
}

Beberapa elemen visual hanyalah sebuah hiasan dan Anda mungkin tidak ingin menyampaikannya kepada pengguna. Saat menyetel parameter contentDescription ke null, Anda menunjukkan pada framework Android bahwa elemen ini tidak memiliki tindakan atau status terkait.

@Composable
private fun PostImage(post: Post, modifier: Modifier = Modifier) {
    val image = post.imageThumb ?: painterResource(R.drawable.placeholder_1_1)

    Image(
        painter = image,
        // Specify that this image has no semantic meaning
        contentDescription = null,
        modifier = modifier
            .size(40.dp, 40.dp)
            .clip(MaterialTheme.shapes.small)
    )
}

Andalah yang memutuskan apakah elemen visual tertentu memerlukan contentDescription. Tanyakan pada diri sendiri apakah elemen menyampaikan informasi yang diperlukan pengguna untuk menjalankan tugasnya. Jika tidak, sebaiknya jangan beri deskripsi.

Menggabungkan elemen

Layanan aksesibilitas seperti Talkback dan Tombol Akses memungkinkan pengguna memindahkan fokus ke seluruh elemen di layar. Elemen-elemen harus difokuskan pada tingkat perincian yang tepat. Jika setiap satu composable level rendah di layar Anda difokuskan secara independen, pengguna harus banyak berinteraksi untuk berpindah-pindah di layar. Jika elemen digabungkan dengan terlalu agresif, pengguna mungkin tidak memahami elemen mana yang menjadi satu.

Saat Anda menerapkan pengubah clickable ke composable, Compose akan menggabungkan semua elemen di dalamnya secara otomatis. Hal ini juga berlaku untuk ListItem; elemen dalam item daftar akan digabungkan dan layanan aksesibilitas akan melihatnya sebagai satu elemen.

Anda dapat memiliki sekumpulan composable yang membentuk grup logis, tetapi grup tersebut tidak dapat diklik atau merupakan bagian dari item daftar. Anda tetap ingin layanan aksesibilitas melihatnya sebagai satu elemen. Misalnya, bayangkan composable yang menampilkan avatar pengguna, nama mereka, dan beberapa informasi tambahan:

Sekumpulan elemen UI termasuk nama pengguna. Nama dipilih.

Anda dapat meminta Compose untuk menggabungkan elemen ini dengan menggunakan parameter mergeDescendants dalam pengubah semantics. Dengan cara ini, layanan aksesibilitas hanya akan memilih elemen gabungan, dan semua properti semantik turunan akan digabungkan.

@Composable
private fun PostMetadata(metadata: Metadata) {
    // Merge elements below for accessibility purposes
    Row(modifier = Modifier.semantics(mergeDescendants = true) {}) {
        Image(
            imageVector = Icons.Filled.AccountCircle,
            contentDescription = null // decorative
        )
        Column {
            Text(metadata.author.name)
            Text("${metadata.date} • ${metadata.readTimeMinutes} min read")
        }
    }
}

Layanan aksesibilitas kini akan berfokus pada seluruh container sekaligus, dengan menggabungkan kontennya:

Sekumpulan elemen UI termasuk nama pengguna. Semua elemen dipilih bersama.

Menambahkan tindakan kustom

Lihat item daftar berikut:

Item daftar umum, yang berisi judul artikel, penulis, dan ikon bookmark.

Saat Anda menggunakan pembaca layar seperti Talkback untuk mendengar apa yang ditampilkan di layar, pembaca akan memilih seluruh item terlebih dahulu, kemudian ikon bookmark.

Item daftar, dengan semua elemen dipilih bersama-sama.

Item daftar, hanya dengan ikon bookmark yang dipilih

Dalam daftar panjang, hal ini dapat menjadi sangat berulang. Pendekatan yang lebih baik adalah menentukan tindakan kustom yang memungkinkan pengguna mem-bookmark item. Perlu diingat bahwa Anda juga harus menghapus perilaku ikon bookmark secara eksplisit, untuk memastikan bahwa ikon tidak akan dipilih oleh layanan aksesibilitas. Hal ini dilakukan dengan pengubah clearAndSetSemantics:

@Composable
private fun PostCardSimple(
    /* ... */
    isFavorite: Boolean,
    onToggleFavorite: () -> Boolean
) {
    val actionLabel = stringResource(
        if (isFavorite) R.string.unfavorite else R.string.favorite
    )
    Row(
        modifier = Modifier
            .clickable(onClick = { /* ... */ })
            .semantics {
                // Set any explicit semantic properties
                customActions = listOf(
                    CustomAccessibilityAction(actionLabel, onToggleFavorite)
                )
            }
    ) {
        /* ... */
        BookmarkButton(
            isBookmarked = isFavorite,
            onClick = onToggleFavorite,
            // Clear any semantics properties set on this node
            modifier = Modifier.clearAndSetSemantics { }
        )
    }
}

Menjelaskan status elemen

Composable dapat menentukan stateDescription untuk semantik yang digunakan oleh framework Android untuk membaca status lokasi composable. Misalnya, composable yang dapat diganti dapat berada dalam status “Dicentang” atau “Tidak dicentang”. Dalam beberapa kasus, Anda mungkin ingin mengganti label deskripsi status default yang digunakan oleh Compose. Anda dapat melakukannya dengan menentukan label deskripsi status secara eksplisit sebelum menentukan composable sebagai dapat diganti:

@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
    val stateSubscribed = stringResource(R.string.subscribed)
    val stateNotSubscribed = stringResource(R.string.not_subscribed)
    Row(
        modifier = Modifier
            .semantics {
                // Set any explicit semantic properties
                stateDescription = if (selected) stateSubscribed else stateNotSubscribed
            }
            .toggleable(
                value = selected,
                onValueChange = { onToggle() }
            )
    ) {
        /* ... */
    }
}

Menentukan judul

Aplikasi terkadang menampilkan banyak konten di satu layar, dalam container yang dapat di-scroll. Misalnya, layar dapat menampilkan konten lengkap artikel yang sedang dibaca pengguna:

Screenshot postingan blog, dengan teks artikel dalam container yang dapat di-scroll.

Pengguna dengan kebutuhan aksesibilitas akan mengalami kesulitan saat menavigasi layar tersebut. Untuk membantu navigasi, Anda dapat menunjukkan elemen mana yang merupakan judul. Pada contoh di atas, setiap judul subbagian dapat didefinisikan sebagai judul untuk aksesibilitas. Beberapa layanan aksesibilitas, seperti Talkback, memungkinkan pengguna menavigasi langsung dari judul ke judul.

Di Compose, Anda menunjukkan bahwa composable adalah judul dengan menentukan properti semantiknya:

@Composable
private fun Subsection(text: String) {
    Text(
        text = text,
        style = MaterialTheme.typography.headlineSmall,
        modifier = Modifier.semantics { heading() }
    )
}

Pengujian otomatis properti aksesibilitas

Saat menyesuaikan properti semantik aplikasi, misalnya saat mengikuti kasus penggunaan yang tercantum di atas, Anda dapat memverifikasi ketepatan dan mencegah regresi dengan menggunakan pengujian UI otomatis.

Misalnya, untuk menguji apakah label klik elemen sudah ditetapkan dengan benar, gunakan kode berikut:

@Test
fun test() {
    composeTestRule
        .onNode(nodeMatcher)
        .assert(
            SemanticsMatcher("onClickLabel is set correctly") {
                it.config.getOrNull(SemanticsActions.OnClick)?.label == "My Click Label"
            }
        )
}

Membuat composable level rendah kustom

Kasus penggunaan yang lebih lanjut melibatkan penggantian komponen Material tertentu di aplikasi Anda dengan versi kustom. Dalam skenario ini, Anda harus selalu mempertimbangkan aksesibilitas. Misalnya Anda mengganti Checkbox Material dengan penerapan Anda sendiri. Akan sangat mudah untuk menambahkan pengubah triStateToggleable, yang menangani properti aksesibilitas untuk komponen ini.

Prinsipnya adalah, Anda harus melihat penerapan komponen dalam library Material dan meniru perilaku aksesibilitas yang dapat Anda temukan. Selain itu, gunakan banyak pengubah Foundation, berbeda dengan pengubah tingkat UI, karena hal ini mencakup pertimbangan aksesibilitas. Pastikan untuk menguji penerapan komponen kustom Anda dengan beberapa layanan aksesibilitas untuk memverifikasi perilakunya.

Mengubah urutan traversal dengan isTraversalGroup dan traversalIndex

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

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

Halaman ini menjelaskan cara menggunakan isTraversalGroup dan traversalIndex di aplikasi Anda untuk mengontrol urutan traversal pembaca layar.

Mengelompokkan elemen dengan isTraversalGroup

isTraversalGroup adalah properti boolean yang menentukan apakah node semantik adalah grup traversal atau tidak. Jenis node ini adalah node yang fungsinya berfungsi sebagai batas atau batas dalam mengatur turunannya.

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

Dalam contoh ini, cuplikan dimodifikasi untuk menggunakan isTraversalGroup. Cuplikan di bawah ini memunculkan empat elemen teks. Dua elemen kiri termasuk dalam satu elemen CardBox, sedangkan dua elemen 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 menghasilkan output yang mirip dengan berikut ini:

Tata letak dengan dua kolom teks, dengan kolom kiri bertuliskan 'Kalimat ini
  berada di kolom kiri' dan kolom kanan bertuliskan 'Kalimat ini di sebelah kanan'.
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 dari atas ke bawah. Karena default ini, TalkBack membacakan fragmen kalimat dalam urutan yang salah:

"Kalimat ini ada di" → "Kalimat ini adalah" → "kolom kiri." → "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 akan diterapkan saat mengurutkan elemennya. Dalam hal ini, CardBox kiri akan dibaca terlebih dahulu, diikuti oleh CardBox yang tepat.

Sekarang, TalkBack akan membacakan fragmen kalimat dalam urutan yang benar:

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

Menyesuaikan urutan traversal lebih lanjut dengan traversalIndex

traversalIndex adalah properti float yang memungkinkan Anda menyesuaikan urutan traversal TalkBack. Jika sekadar mengelompokkan elemen tidak cukup bagi TalkBack untuk berfungsi dengan benar, Anda dapat menggunakan 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.
  • 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 traversalIndex saja pada Kolom tidak akan berpengaruh, kecuali jika Kolom memiliki isTraversalGroup yang ditetapkan di dalamnya.

Contoh berikut menunjukkan cara menggunakan traversalIndex dan isTraversalGroup secara bersamaan.

Contoh: Jelajahi tampilan jam

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

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

Dalam cuplikan yang disederhanakan berikut, ada CircularLayout yang menampilkan 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 kiri ke kanan dan atas ke bawah, TalkBack membaca angka secara tidak berurutan. Untuk memperbaikinya, gunakan nilai penghitung yang bertambah, 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 pengurutan traversal dengan benar, pertama-tama buat CircularLayout sebagai grup traversal dan tetapkan isTraversalGroup = true. Kemudian, saat setiap teks jam digambar ke tata letak, setel traversalIndex yang sesuai ke nilai penghitung.

Karena nilai penghitung terus meningkat, setiap traversalIndex nilai jam menjadi lebih besar saat angka ditambahkan ke layar— nilai jam 0 memiliki traversalIndex 0, nilai jam 1 memiliki traversalIndex 1, dan seterusnya. Dengan demikian, urutan pembacaan TalkBack telah ditetapkan. Sekarang, angka di dalam CircularLayout dibaca dalam urutan yang diharapkan.

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

Perhatikan 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 telah dikunjungi. Hal ini terjadi karena semua elemen lain 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, Anda menggunakan traversalIndex dan isTraversalGroup untuk mengontrol urutan traversal tombol tindakan mengambang (FAB) Desain Material. Contoh ini didasarkan pada tata letak berikut:

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

Secara default, tata letak di atas memiliki urutan TalkBack berikut:

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

Anda mungkin ingin pembaca layar berfokus terlebih dahulu pada FAB. Untuk menetapkan 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 yang ditetapkan ke true dan menetapkan traversalIndex pada kotak yang sama (-1f lebih rendah dari nilai default 0f) berarti kotak mengambang akan muncul sebelum semua elemen lain di layar.

Selanjutnya, Anda dapat menempatkan kotak mengambang dan elemen lainnya ke dalam scaffold yang menerapkan tata letak Desain Material sederhana:

@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 → Contoh teks 0 sampai 6 → Panel Aplikasi Bawah

Pelajari lebih lanjut

Untuk mempelajari lebih lanjut cara mendukung aksesibilitas dalam kode Compose, buka codelab Aksesibilitas di Jetpack Compose.