Aksesibilitas di Jetpack Compose

1. Pengantar

Dalam codelab ini, Anda akan mempelajari cara menggunakan Jetpack Compose untuk meningkatkan aksesibilitas aplikasi. Kami akan memandu Anda menjalani beberapa kasus penggunaan umum dan mengembangkan aplikasi contoh secara bertahap. Kita akan membahas ukuran target sentuh, deskripsi konten, label klik, dan lainnya.

Orang dengan gangguan penglihatan, buta warna, gangguan pendengaran, gangguan ketangkasan, gangguan kognitif, dan banyak gangguan lainnya menggunakan perangkat Android untuk melakukan berbagai aktivitasnya dalam kehidupan sehari-hari. Saat mengembangkan aplikasi dengan mempertimbangkan aksesibilitas, Anda akan membuat pengalaman pengguna menjadi lebih baik, terutama bagi pengguna yang memiliki kebutuhan aksesibilitas tersebut dan lainnya.

Selama codelab ini, kita akan menggunakan TalkBack untuk menguji perubahan kode secara manual. TalkBack adalah layanan aksesibilitas yang paling sering digunakan oleh penyandang gangguan penglihatan. Pastikan juga untuk menguji setiap perubahan pada kode Anda dengan layanan aksesibilitas lainnya, misalnya Tombol Akses.

Persegi panjang fokus TalkBack bergerak melalui layar utama Jetnews. Teks yang diumumkan oleh TalkBack ditampilkan di bagian bawah layar.

Cara kerja TalkBack di aplikasi Jetnews.

Yang akan Anda pelajari

Dalam codelab ini, Anda akan mempelajari:

  • Cara memfasilitasi pengguna yang memiliki gangguan ketangkasan dengan meningkatkan ukuran target sentuh.
  • Pengertian properti semantik dan cara Anda mengubahnya.
  • Cara memberikan informasi ke composable agar lebih mudah diakses.

Yang akan Anda butuhkan

Yang akan Anda build

Dalam codelab ini, kita akan meningkatkan aksesibilitas aplikasi pembaca berita. Kita akan memulai dengan aplikasi yang tidak memiliki fitur aksesibilitas penting dan menerapkan apa yang telah dipelajari agar aplikasi dapat lebih bermanfaat bagi orang dengan kebutuhan aksesibilitas.

2. Mempersiapkan

Pada langkah ini, Anda akan mendownload kode untuk ini yang terdiri dari aplikasi pembaca berita sederhana.

Yang akan Anda butuhkan

Mendapatkan kode

Kode untuk codelab ini dapat ditemukan di repositori GitHub android-compose-codelabs. Untuk melakukan clone kode ini, jalankan:

$ git clone https://github.com/android/codelab-android-compose

Atau, Anda dapat mendownload dua file ZIP:

Melihat aplikasi contoh

Kode yang baru saja Anda download berisi kode untuk semua codelab Compose yang tersedia. Untuk menyelesaikan codelab ini, buka project AccessibilityCodelab di dalam Android Studio.

Sebaiknya Anda memulai dengan kode di cabang main dan mengikuti codelab langkah demi langkah sesuai kemampuan Anda.

Menyiapkan TalkBack

Selama Codelab ini, kita akan menggunakan TalkBack untuk memeriksa perubahan. Saat Anda menggunakan perangkat fisik untuk melakukan pengujian, ikuti petunjuk-petunjuk ini untuk mengaktifkan TalkBack. Emulator tidak dilengkapi dengan TalkBack yang diinstal secara default. Pilih emulator yang menyertakan Play Store, dan download Android Accessibility Suite.

3. Ukuran target sentuh

Elemen apa pun di layar yang dapat diklik, disentuh, atau berinteraksi dengan seseorang harus cukup besar untuk interaksi yang dapat diandalkan. Anda harus memastikan elemen ini memiliki lebar dan tinggi minimal 48 dp.

Jika ukuran kontrol ini diubah secara dinamis, atau berdasarkan ukuran kontennya, sebaiknya gunakan pengubah sizeIn untuk menetapkan batas bawah dimensinya.

Beberapa komponen Material menetapkan ukuran ini untuk Anda. Misalnya, composable Button memiliki MinHeight yang disetel ke 36dp, dan menggunakan padding vertikal 8dp. Setelan ini dapat menambah tinggi yang diinginkan hingga 48 dp.

Saat membuka aplikasi contoh dan menjalankan TalkBack, kita akan melihat bahwa ikon silang di kartu pos memiliki target sentuh yang sangat kecil. Kita ingin target sentuh ini memiliki resolusi minimal 48 dp.

Berikut adalah screenshot yang menampilkan aplikasi asli di sebelah kiri, dan dibandingkan dengan solusi yang telah ditingkatkan di sebelah kanan.

Perbandingan item daftar yang menampilkan garis batas kecil ikon silang di sebelah kiri, garis besar di sebelah kanan.

Mari lihat implementasinya dan periksa ukuran composable ini. Buka PostCards.kt dan cari composable PostCardHistory. Seperti yang dapat Anda lihat, implementasi menetapkan ukuran ikon menu tambahan ke 24 dp:

@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
   // ...

   Row(
       // ...
   ) {
       // ...
       CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
           Icon(
               imageVector = Icons.Default.Close,
               contentDescription = stringResource(R.string.cd_show_fewer),
               modifier = Modifier
                   .clickable { openDialog = true }
                   .size(24.dp)
           )
       }
   }
   // ...
}

Untuk meningkatkan ukuran target sentuh Icon ini, kita dapat menambahkan padding:

@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
   // ...
   Row(
       // ...
   ) {
       // ...
       CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
           Icon(
               imageVector = Icons.Default.Close,
               contentDescription = stringResource(R.string.cd_show_fewer),
               modifier = Modifier
                   .clickable { openDialog = true }
                   .padding(12.dp)
                   .size(24.dp)
           )
       }
   }
   // ...
}

Dalam kasus penggunaan kita, ada cara yang lebih mudah untuk memastikan target sentuh setidaknya 48 dp. Kita dapat menggunakan komponen Material IconButton yang akan menangani hal ini untuk kita:

@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
   // ...
   Row(
       // ...
   ) {
       // ...
       CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
           IconButton(onClick = { openDialog = true }) {
               Icon(
                   imageVector = Icons.Default.Close,
                   contentDescription = stringResource(R.string.cd_show_fewer)
               )
           }
       }
   }
   // ...
}

Kini membuka layar dengan TalkBack menampilkan area target sentuh 48 dp dengan benar. Selain itu, IconButton juga menambahkan indikasi ripple yang menunjukkan kepada pengguna bahwa elemen ini dapat diklik.

4. Label klik

Elemen yang dapat diklik di aplikasi Anda secara default tidak memberikan informasi tentang tindakan yang akan dilakukan dengan mengklik elemen tersebut. Oleh karena itu, layanan aksesibilitas seperti TalkBack akan menggunakan deskripsi default yang sangat umum.

Untuk memberikan pengalaman terbaik bagi pengguna dengan kebutuhan aksesibilitas, kita dapat memberikan deskripsi khusus yang menjelaskan apa yang akan terjadi saat pengguna mengklik elemen ini.

Di aplikasi Jetnews, pengguna dapat mengklik berbagai kartu postingan untuk membaca postingan selengkapnya. Secara default, konten elemen yang dapat diklik akan dibacakan, diikuti dengan teks "Ketuk dua kali untuk mengaktifkan". Sebaliknya, kita ingin lebih spesifik dan menggunakan "Ketuk dua kali untuk membaca artikel". Seperti inilah tampilan versi aslinya, dibandingkan dengan solusi ideal kita:

Dua rekaman layar dengan TalkBack diaktifkan dengan mengetuk postingan dalam daftar vertikal dan postingan di carousel horizontal.

Mengubah label klik dari composable. Sebelum (di sebelah kiri) vs setelah (di sebelah kanan).

Pengubah clickable menyertakan parameter yang memungkinkan Anda menetapkan label klik ini secara langsung.

Mari perhatikan kembali implementasi PostCardHistory:

@Composable
fun PostCardHistory(
   // ...
) {
   Row(
       Modifier.clickable { navigateToArticle(post.id) }
   ) {
       // ...
   }
}

Seperti yang dapat Anda lihat, implementasi ini menggunakan pengubah clickable. Untuk menetapkan label klik, kita dapat menetapkan parameter onClickLabel:

@Composable
fun PostCardHistory(
   // ...
) {
   Row(
       Modifier.clickable(
               // R.string.action_read_article = "read article"
               onClickLabel = stringResource(R.string.action_read_article)
           ) {
               navigateToArticle(post.id)
           }
   ) {
       // ...
   }
}

TalkBack kini mengumumkan "Ketuk dua kali untuk membaca artikel" dengan benar.

Kartu pos lainnya di layar utama memiliki label klik umum yang sama. Mari lihat implementasi dari composable PostCardPopular dan perbarui label kliknya:

@Composable
fun PostCardPopular(
   // ...
) {
   Card(
       shape = MaterialTheme.shapes.medium,
       modifier = modifier.size(280.dp, 240.dp),
       onClick = { navigateToArticle(post.id) }
   ) {
       // ...
   }
}

Composable ini menggunakan composable Card secara internal, yang tidak memungkinkan Anda untuk menetapkan label klik secara langsung. Sebagai gantinya, Anda dapat menggunakan pengubah semantics untuk menetapkan label klik:

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun PostCardPopular(
   post: Post,
   navigateToArticle: (String) -> Unit,
   modifier: Modifier = Modifier
) {
   val readArticleLabel = stringResource(id = R.string.action_read_article)
   Card(
       shape = MaterialTheme.shapes.medium,
       modifier = modifier
          .size(280.dp, 240.dp)
          .semantics { onClick(label = readArticleLabel, action = null) },
       onClick = { navigateToArticle(post.id) }
   ) {
       // ...
   }
}

5. Tindakan kustom

Banyak aplikasi menampilkan semacam daftar dengan setiap item dalam daftar berisi satu atau beberapa tindakan. Saat menggunakan pembaca layar, menjelajahi daftar seperti itu bisa jadi sangat membosankan karena tindakan yang sama akan difokuskan berulang kali.

Sebagai gantinya, kita dapat menambahkan tindakan aksesibilitas kustom ke composable. Dengan cara ini, tindakan yang berkaitan dengan item daftar yang sama dapat dikelompokkan bersama.

Di aplikasi Jetnews, kita menampilkan daftar artikel yang dapat dibaca oleh pengguna. Setiap item daftar menyertakan tindakan untuk menunjukkan bahwa pengguna ingin melihat lebih sedikit topik ini. Di bagian ini, kita akan memindahkan tindakan ini ke tindakan aksesibilitas kustom sehingga menjelajahi daftar menjadi lebih mudah.

Di sebelah kiri, Anda dapat melihat situasi default dengan setiap ikon silang dapat difokuskan. Di sebelah kanan, Anda dapat melihat solusi yang menyertakan tindakan dalam tindakan kustom di TalkBack:

Dua rekaman layar dengan TalkBack diaktifkan. Layar di sebelah kiri menunjukkan cara memilih ikon silang pada item postingan. Mengetuk dua kali akan membuka dialog. Layar di sebelah kanan menampilkan gestur ketuk tiga kali untuk membuka menu Tindakan kustom. Mengetuk tindakan 'Show fewer of this' akan membuka dialog yang sama.

Menambahkan tindakan kustom ke item postingan. Sebelum (di sebelah kiri) vs setelah (di sebelah kanan).

Mari buka PostCards.kt dan lihat implementasi composable PostCardHistory. Perhatikan properti yang dapat diklik dari Row dan IconButton, menggunakan Modifier.clickable dan onClick:

@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
   // ...
   Row(
       Modifier.clickable(
           onClickLabel = stringResource(R.string.action_read_article)
       ) {
           navigateToArticle(post.id)
       }
   ) {
       // ...
       CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
           IconButton(onClick = { openDialog = true }) {
               Icon(
                   imageVector = Icons.Default.Close,
                   contentDescription = stringResource(R.string.cd_show_fewer)
               )
           }
       }
   }
   // ...
}

Secara default, Row dan composable IconButton dapat diklik, sehingga akan difokuskan oleh TalkBack. Ini terjadi untuk setiap item dalam daftar, yang artinya Anda akan banyak menggeser selama menjelajahi daftar. Kita ingin tindakan yang terkait dengan IconButton disertakan sebagai tindakan kustom pada item daftar. Kita dapat memberi tahu Layanan Aksesibilitas agar tidak berinteraksi dengan Icon ini menggunakan pengubah clearAndSetSemantics:

@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
   // ...
   Row(
       Modifier.clickable(
           onClickLabel = stringResource(R.string.action_read_article)
       ) {
           navigateToArticle(post.id)
       }
   ) {
       // ...
       CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
            IconButton(
                modifier = Modifier.clearAndSetSemantics { },
                onClick = { openDialog = true }
            ) {
                Icon(
                    imageVector = Icons.Default.Close,
                    contentDescription = stringResource(R.string.cd_show_fewer)
                )
            }
       }
   }
   // ...
}

Namun, dengan menghapus semantik IconButton, sekarang tidak ada lagi cara untuk menjalankan tindakan tersebut. Kita dapat menambahkan tindakan ke item daftar dengan menambahkan tindakan kustom di pengubah semantics:

@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
   // ...
   val showFewerLabel = stringResource(R.string.cd_show_fewer)
   Row(
        Modifier
            .clickable(
                onClickLabel = stringResource(R.string.action_read_article)
            ) {
                navigateToArticle(post.id)
            }
            .semantics {
                customActions = listOf(
                    CustomAccessibilityAction(
                        label = showFewerLabel,
                        // action returns boolean to indicate success
                        action = { openDialog = true; true }
                    )
                )
            }
   ) {
       // ...
       CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
            IconButton(
                modifier = Modifier.clearAndSetSemantics { },
                onClick = { openDialog = true }
            ) {
                Icon(
                    imageVector = Icons.Default.Close,
                    contentDescription = showFewerLabel
                )
            }
       }
   }
   // ...
}

Sekarang kita dapat menggunakan pop-up tindakan kustom di TalkBack untuk menerapkan tindakan. Cara ini menjadi semakin relevan seiring dengan meningkatnya jumlah tindakan di dalam item daftar.

6. Deskripsi elemen visual

Tidak semua pengguna aplikasi Anda dapat melihat atau menafsirkan elemen visual yang ditampilkan di aplikasi, seperti ikon dan ilustrasi. Layanan aksesibilitas juga tidak dapat digunakan untuk memahami elemen visual hanya berdasarkan pikselnya. Oleh karena itu, Anda sebagai developer harus meneruskan lebih banyak informasi tentang elemen visual dalam aplikasi ke layanan aksesibilitas.

Composable visual seperti Image dan Icon menyertakan parameter contentDescription. Di sini, Anda meneruskan deskripsi yang dilokalkan pada elemen visual tersebut, atau null jika elemen tersebut hanya bersifat dekoratif.

Di aplikasi, layar artikel tidak memiliki beberapa deskripsi konten. Mari jalankan aplikasi dan pilih artikel teratas untuk membuka layar artikel.

Dua rekaman layar dengan TalkBack diaktifkan dengan mengetuk tombol kembali di layar artikel. Layar kiri menyebutkan 'Button—double tap to activate'. Layar kanan menyebutkan 'Navigate up—double tap to activate'.

Menambahkan deskripsi konten visual. Sebelum (di sebelah kiri) vs setelah (di sebelah kanan).

Jika tidak tersedia informasi apa pun, ikon navigasi kiri atas hanya akan menyebutkan "Button, double tap to activate". Ini tidak akan memberi tahu pengguna apa pun tentang tindakan yang akan diambil saat tombol tersebut diaktifkan. Mari buka ArticleScreen.kt:

@Composable
fun ArticleScreen(
   // ...
) {
   // ...
   Scaffold(
       topBar = {
           InsetAwareTopAppBar(
               title = {
                   // ...
               },
               navigationIcon = {
                   IconButton(onClick = onBack) {
                       Icon(
                           imageVector = Icons.Filled.ArrowBack,
                           contentDescription = null
                       )
                   }
               }
           )
       }
   ) {
       // ...
   }
}

Tambahkan deskripsi konten penting ke Ikon:

@Composable
fun ArticleScreen(
   // ...
) {
   // ...
   Scaffold(
       topBar = {
           InsetAwareTopAppBar(
               title = {
                   // ...
               },
               navigationIcon = {
                   IconButton(onClick = onBack) {
                       Icon(
                           imageVector = Icons.Filled.ArrowBack,
                           contentDescription = stringResource(
                               R.string.cd_navigate_up
                           )
                       )
                   }
               }
           )
       }
   ) {
       // ...
   }
}

Elemen visual lain dalam artikel ini adalah gambar header. Dalam kasus kita, gambar ini hanya bersifat dekoratif sehingga tidak menunjukkan apa pun yang perlu disampaikan kepada pengguna. Oleh karena itu, deskripsi konten disetel ke null dan elemen dilewati selama kita menggunakan layanan aksesibilitas.

Elemen visual terakhir di layar adalah foto profil. Dalam hal ini, kita menggunakan avatar generik, sehingga tidak perlu menambahkan deskripsi konten di sini. Jika akan menggunakan foto profil sebenarnya dari penulis ini, kita dapat meminta mereka untuk memberikan deskripsi konten yang sesuai untuk foto tersebut.

7. Judul

Saat layar berisi banyak teks, seperti layar artikel, cukup sulit bagi pengguna dengan masalah penglihatan untuk menemukan dengan cepat bagian yang dicari tersebut. Untuk membantu hal tersebut, kita dapat menunjukkan bagian teks mana yang merupakan judul. Pengguna kemudian dapat dengan cepat berpindah-pindah antara judul yang berbeda ini dengan menggeser ke atas atau ke bawah.

Secara default, tidak ada composable yang ditandai sebagai judul sehingga tidak akan ada navigasi yang mungkin. Kita ingin layar artikel menyediakan judul melalui navigasi heading:

Dua rekaman layar dengan TalkBack diaktifkan dan pengguna dapat menggunakan geser ke bawah untuk memilih judul. Layar kiri bertuliskan 'No next heading'. Layar kanan berganti-ganti judul dan membaca setiap judul dengan keras.

Menambahkan judul. Sebelum (di sebelah kiri) vs setelah (di sebelah kanan).

Judul dalam artikel kita ditentukan dalam PostContent.kt. Mari buka file tersebut dan scroll ke composable Paragraph:

@Composable
private fun Paragraph(paragraph: Paragraph) {
   // ...
   Box(modifier = Modifier.padding(bottom = trailingPadding)) {
       when (paragraph.type) {
           // ...
           ParagraphType.Header -> {
               Text(
                   modifier = Modifier.padding(4.dp),
                   text = annotatedString,
                   style = textStyle.merge(paragraphStyle)
               )
           }
           // ...
       }
   }
}

Di sini, Header ditentukan sebagai composable Text sederhana. Kita dapat menetapkan properti semantik heading untuk menunjukkan bahwa composable ini merupakan judul.

@Composable
private fun Paragraph(paragraph: Paragraph) {
   // ...
   Box(modifier = Modifier.padding(bottom = trailingPadding)) {
       when (paragraph.type) {
           // ...
           ParagraphType.Header -> {
               Text(
                   modifier = Modifier.padding(4.dp)
                     .semantics { heading() },
                   text = annotatedString,
                   style = textStyle.merge(paragraphStyle)
               )
           }
           // ...
       }
   }
}

8. Penggabungan kustom

Seperti yang telah kita lihat di langkah sebelumnya, layanan aksesibilitas seperti TalkBack menjelajahi elemen layar berdasarkan elemen. Secara default, setiap composable level rendah di Jetpack Compose yang menetapkan setidaknya satu properti semantik akan menerima fokus. Jadi misalnya, composable Text menetapkan properti semantik text sehingga menerima fokus.

Namun, memiliki terlalu banyak elemen yang dapat difokuskan di layar dapat menyebabkan kebingungan saat pengguna menjelajahinya satu per satu. Sebagai gantinya, composable dapat digabungkan menggunakan pengubah semantics dengan properti mergeDescendants-nya.

Mari periksa layar artikel. Sebagian besar elemen mendapatkan tingkat fokus yang tepat. Namun, metadata artikel saat ini dibacakan secara lisan sebagai beberapa item terpisah. Bagian ini dapat ditingkatkan dengan menggabungkannya menjadi satu entity yang dapat difokuskan:

Dua rekaman layar dengan TalkBack diaktifkan. Layar kiri menampilkan persegi panjang TalkBack hijau yang terpisah untuk kolom Penulis dan Metadata. Layar kanan menampilkan satu persegi panjang di sekitar kedua kolom dan membaca konten yang digabung.

Menggabungkan composable. Sebelum (di sebelah kiri) vs setelah (di sebelah kanan).

Mari buka PostContent.kt dan periksa composable PostMetadata:

@Composable
private fun PostMetadata(metadata: Metadata) {
   // ...
   Row {
       Image(
           // ...
       )
       Spacer(Modifier.width(8.dp))
       Column {
           Text(
               // ...
           )

           CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
               Text(
                   // ..
               )
           }
       }
   }
}

Kita dapat memberi tahu baris level atas untuk menggabungkan turunannya, yang akan menghasilkan perilaku yang kita inginkan:

@Composable
private fun PostMetadata(metadata: Metadata) {
   // ...
   Row(Modifier.semantics(mergeDescendants = true) {}) {
       Image(
           // ...
       )
       Spacer(Modifier.width(8.dp))
       Column {
           Text(
               // ...
           )

           CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
               Text(
                   // ..
               )
           }
       }
   }
}

9. Tombol akses dan kotak centang

Elemen yang dapat dialihkan seperti Switch dan Checkbox membacakan dengan keras status yang dicentangnya saat dipilih oleh TalkBack. Tanpa konteks, akan sulit untuk memahami arti elemen yang dapat diubah tersebut. Kita dapat menyertakan konteks untuk elemen yang dapat diubah dengan mencabut status yang dapat diubah tersebut, sehingga pengguna dapat mengalihkan Switch atau Checkbox dengan menekan composable itu sendiri atau label yang mendeskripsikannya.

Kita dapat melihat contohnya di layar Interests. Anda dapat menjelajahinya dengan membuka panel navigasi dari Layar utama. Di layar Interests, tersedia banyak daftar topik yang dapat diikuti pengguna. Secara default, kotak centang di layar ini difokuskan secara terpisah dari labelnya sehingga sulit untuk memahami konteksnya. Kita lebih memilih seluruh Row dapat diubah:

Dua rekaman layar dengan TalkBack diaktifkan sehingga akan menampilkan layar minat dengan daftar topik yang dapat dipilih. Di layar kiri, TalkBack memilih setiap Kotak centang secara terpisah. Di layar kanan, TalkBack memilih seluruh baris.

Menggunakan kotak centang. Sebelum (di sebelah kiri) vs setelah (di sebelah kanan).

Mari buka InterestsScreen.kt dan lihat implementasi composable TopicItem:

@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
   // ...
   Row(
       modifier = Modifier
           .padding(horizontal = 16.dp, vertical = 8.dp)
   ) {
       // ...
       Checkbox(
           checked = selected,
           onCheckedChange = { onToggle() },
           modifier = Modifier.align(Alignment.CenterVertically)
       )
   }
}

Seperti yang dapat Anda lihat di sini, Checkbox memiliki callback onCheckedChange yang menangani pengalihan elemen. Kita dapat mencabut callback ini ke level seluruh Row:

@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
   // ...
   Row(
       modifier = Modifier
           .toggleable(
               value = selected,
               onValueChange = { _ -> onToggle() },
               role = Role.Checkbox
           )
           .padding(horizontal = 16.dp, vertical = 8.dp)
   ) {
       // ...
       Checkbox(
           checked = selected,
           onCheckedChange = null,
           modifier = Modifier.align(Alignment.CenterVertically)
       )
   }
}

10. Deskripsi status

Di langkah sebelumnya, kita telah membatalkan perilaku beralih dari Checkbox ke Row induk. Kita dapat meningkatkan aksesibilitas elemen ini lebih jauh dengan menambahkan deskripsi kustom untuk status composable.

Secara default, status Checkbox dibaca sebagai "Ticked" atau "Not ticked". Kita dapat mengganti deskripsi ini dengan deskripsi kustom versi kita:

Dua rekaman layar dengan TalkBack diaktifkan dengan mengetuk topik di layar minat. Layar kiri mengumumkan 'Not ticked', sedangkan layar kanan mengumumkan 'Not subscribed'.

Menambahkan deskripsi status. Sebelum (di sebelah kiri) vs setelah (di sebelah kanan).

Kita dapat melanjutkan dengan composable TopicItem yang telah diadaptasi di langkah terakhir:

@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
   // ...
   Row(
       modifier = Modifier
           .toggleable(
               value = selected,
               onValueChange = { _ -> onToggle() },
               role = Role.Checkbox
           )
           .padding(horizontal = 16.dp, vertical = 8.dp)
   ) {
       // ...
       Checkbox(
           checked = selected,
           onCheckedChange = null,
           modifier = Modifier.align(Alignment.CenterVertically)
       )
   }
}

Kita dapat menambahkan deskripsi status kustom menggunakan properti stateDescription di dalam pengubah semantics:

@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
   // ...
   val stateNotSubscribed = stringResource(R.string.state_not_subscribed)
   val stateSubscribed = stringResource(R.string.state_subscribed)
   Row(
       modifier = Modifier
           .semantics {
               stateDescription = if (selected) {
                   stateSubscribed
               } else {
                   stateNotSubscribed
               }
           }
           .toggleable(
               value = selected,
               onValueChange = { _ -> onToggle() },
               role = Role.Checkbox
           )
           .padding(horizontal = 16.dp, vertical = 8.dp)
   ) {
       // ...
       Checkbox(
           checked = selected,
           onCheckedChange = null,
           modifier = Modifier.align(Alignment.CenterVertically)
       )
   }
}

11. Selamat!

Selamat, Anda telah berhasil menyelesaikan codelab ini dan mempelajari aksesibilitas di Compose lebih lanjut. Anda telah mempelajari tentang target sentuh, deskripsi elemen visual, dan deskripsi status. Anda juga telah menambahkan label klik, judul, tindakan kustom. Anda tahu cara menambahkan penggabungan kustom, serta cara menangani tombol dan kotak centang. Menerapkan pembelajaran ini ke aplikasi Anda akan sangat membantu dalam meningkatkan aksesibilitasnya.

Lihat codelab lainnya di jalur Compose, dan contoh kode lainnya, termasuk Jetnews.

Dokumentasi

Untuk mendapatkan informasi selengkapnya dan panduan tentang topik ini, lihat dokumentasi berikut: