Default API

API Material, Compose UI, dan Foundation menerapkan dan menawarkan banyak praktik yang mudah diakses secara default. Elemen ini berisi semantik bawaan yang mengikuti peran dan fungsi spesifiknya. Artinya, sebagian besar dukungan aksesibilitas disediakan dengan sedikit atau tanpa pekerjaan tambahan.

Menggunakan API yang sesuai untuk tujuan yang sesuai berarti komponen biasanya dilengkapi dengan perilaku aksesibilitas standar yang mencakup kasus penggunaan standar. Namun, selalu periksa kembali apakah setelan default ini sesuai dengan kebutuhan aksesibilitas Anda. Jika tidak, Compose menyediakan cara untuk memenuhi persyaratan yang lebih spesifik.

Memahami semantik dan pola aksesibilitas default di Compose API akan membantu Anda menggunakannya dengan mempertimbangkan aksesibilitas. Hal ini juga membantu Anda mendukung aksesibilitas di lebih banyak komponen kustom.

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, kotak centang akan menyertakan padding agar memiliki lebar dan tinggi minimal 48 dp.

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

Kotak centang dengan padding default dengan lebar dan tinggi 48 dp.
Gambar 1. Kotak centang dengan padding default.

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

Kotak centang yang tidak memiliki padding.
Gambar 2. Kotak centang tanpa padding.

Saat menerapkan kontrol pemilihan seperti Switch, RadioButton, atau Checkbox, Anda biasanya menaikkan perilaku yang dapat diklik ke container induk dengan 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)
        }
    }
}

Kotak centang di samping teks 'Opsi' yang dipilih dan tidak dipilih.
Gambar 3. Kotak centang dengan perilaku yang dapat diklik.

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.

Contoh berikut berisi 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)
        )
    }
}

Kotak kecil yang dapat diklik dan diperluas menjadi target sentuh yang lebih besar dengan mengetuk di samping kotak.
Gambar 4. Kotak kecil yang dapat diklik dan diperluas menjadi target sentuh yang lebih besar.

Untuk mencegah kemungkinan tumpang tindih di antara area sentuh composable yang berbeda, selalu gunakan ukuran minimum yang cukup besar untuk composable. Dalam contoh, 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)
        )
    }
}

Kotak yang sangat kecil dari contoh sebelumnya ditingkatkan ukurannya untuk membuat target sentuh yang lebih besar.
Gambar 5. Target sentuh kotak yang lebih besar.

Jika elemen harus mempertahankan ukuran terlihat yang lebih kecil, gunakan pengubah minimumInteractiveComponentSize dari Material 3. Komponen ini menyediakan setidaknya 48 dp ruang tata letak di setiap dimensi dan memusatkan elemen di ruang tersebut, sehingga elemen mempertahankan ukuran yang digambar dan elemen di sekitarnya berpindah untuk memberi ruang. Pengubah tidak memperluas target sentuh itu sendiri: pengubah menyisakan ruang dalam tata letak untuk perluasan yang dijelaskan sebelumnya. Komponen Material seperti Checkbox menggunakan pengubah ini secara internal. Tempatkan sebelum pengubah ukuran, karena pengubah ukuran sebelum pengubah ini membatasi ruang yang dapat dicadangkan.

@Composable
private fun MinimumInteractiveBox() {
    var clicked by remember { mutableStateOf(false) }
    Row(verticalAlignment = Alignment.CenterVertically) {
        Box(
            Modifier
                .clickable { clicked = !clicked }
                .minimumInteractiveComponentSize()
                .size(24.dp)
                .background(if (clicked) Color.DarkGray else Color.Black)
        )
        Text("Option")
    }
}

Elemen grafis

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

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

Strip berisi empat ikon yang dapat diklik, dengan ikon 'bagikan' ditandai.
Gambar 6. Baris ikon yang dapat diklik dengan ikon 'Bagikan' dipilih.

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

Parameter contentDescription mendeskripsikan elemen grafis. Gunakan string yang dilokalkan, karena string ini dapat dilihat oleh pengguna.

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

Beberapa elemen grafis 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)
    )
}

contentDescription terutama dimaksudkan untuk digunakan pada elemen grafis, seperti gambar. Komponen material, seperti Button atau Text, dan perilaku yang dapat ditindaklanjuti, seperti clickable atau toggleable, dilengkapi dengan semantik yang telah ditetapkan sebelumnya yang menjelaskan perilaku intrinsiknya, dan dapat diubah melalui Compose API lainnya.

Elemen interaktif

API Compose Material dan Foundation membuat elemen UI yang dapat digunakan pengguna untuk berinteraksi melalui API pengubah clickable dan toggleable. Karena komponen yang dapat berinteraksi mungkin terdiri dari beberapa elemen, clickable dan toggleable menggabungkan semantik turunannya secara default, sehingga komponen diperlakukan sebagai satu entity logis.

Misalnya, Button Material mungkin terdiri dari ikon turunan dan beberapa teks. Alih-alih memperlakukan turunan sebagai individu, Button Material menggabungkan semantik turunannya secara default, sehingga layanan aksesibilitas dapat mengelompokkannya sebagaimana mestinya:

Tombol dengan semantik turunan yang tidak digabungkan versus yang digabungkan.
Gambar 7. Tombol dengan semantik turunan yang tidak digabungkan versus digabungkan.

Demikian pula, menggunakan pengubah clickable juga menyebabkan composable menggabungkan semantik turunannya menjadi satu entity, yang dikirim ke layanan aksesibilitas dengan representasi tindakan yang sesuai:

Row(
    // Uses `mergeDescendants = true` under the hood
    modifier = Modifier.clickable { openArticle() }
) {
    Icon(
        painter = painterResource(R.drawable.ic_logo),
        contentDescription = "Open",
    )
    Text("Accessibility in Compose")
}

Anda juga dapat menetapkan onClickLabel tertentu pada induk yang dapat diklik untuk memberikan informasi tambahan ke layanan aksesibilitas dan menawarkan representasi tindakan yang lebih baik:

Row(
    modifier = Modifier
        .clickable(onClickLabel = "Open this article") {
            openArticle()
        }
) {
    Icon(
        painter = painterResource(R.drawable.ic_logo),
        contentDescription = "Open"
    )
    Text("Accessibility in Compose")
}

Dengan menggunakan TalkBack sebagai contoh, pengubah clickable ini dan label kliknya akan memungkinkan TalkBack memberikan petunjuk tindakan "Ketuk dua kali untuk membuka artikel ini", bukan masukan default yang lebih umum, yaitu "Ketuk dua kali untuk mengaktifkan".

Masukan ini berubah bergantung pada jenis tindakan. Klik lama akan memberikan petunjuk TalkBack "Ketuk dua kali dan tahan untuk", diikuti dengan label:

Row(
    modifier = Modifier
        .combinedClickable(
            onLongClickLabel = "Bookmark this article",
            onLongClick = { addToBookmarks() },
            onClickLabel = "Open this article",
            onClick = { openArticle() },
        )
) {}

Dalam beberapa kasus, Anda mungkin tidak memiliki akses langsung ke pengubah clickable (misalnya, saat ditetapkan di suatu tempat di lapisan bertingkat yang lebih rendah),tetapi masih ingin mengubah label pengumuman dari default. Untuk melakukannya, pisahkan setelan clickable dari mengubah pengumuman menggunakan pengubah semantics dan menetapkan label klik di sana, untuk mengubah representasi tindakan:

@Composable
private fun ArticleList(openArticle: () -> Unit) {
    NestedArticleListItem(
        // Clickable is set separately, in a nested layer:
        onClickAction = openArticle,
        // Semantics are set here:
        modifier = Modifier.semantics {
            onClick(
                label = "Open this article",
                action = {
                    // Not needed here: openArticle()
                    true
                }
            )
        }
    )
}

Anda tidak perlu meneruskan tindakan klik dua kali. Compose API yang ada, seperti clickable atau Button, akan menangani hal ini untuk Anda. Logika penggabungan memverifikasi bahwa label dan tindakan pengubah terluar diambil untuk informasi yang ada. Pada contoh sebelumnya, NestedArticleListItem otomatis meneruskan tindakan klik openArticle() ke semantik clickable-nya. Anda dapat membiarkan tindakan klik null dalam tindakan pengubah semantik kedua. Namun, label klik diambil dari pengubah semantik kedua onClick(label = "Open this document") karena tidak ada di pengubah pertama.

Anda mungkin mengalami skenario saat Anda mengharapkan semantik turunan digabungkan ke dalam semantik induk, tetapi hal itu tidak terjadi. Lihat Menggabungkan dan menghapus untuk mengetahui informasi yang lebih mendalam.

Komponen kustom

Saat membuat komponen kustom, tinjau implementasi komponen serupa di library Material atau library Compose lainnya. Kemudian, tiru atau ubah perilaku aksesibilitasnya sebagaimana mestinya. Misalnya, jika Anda mengganti Checkbox Material dengan penerapan Anda sendiri, melihat penerapan Checkbox yang ada akan mengingatkan Anda untuk menambahkan pengubah triStateToggleable, yang menangani properti aksesibilitas untuk komponen. Selain itu, gunakan banyak pengubah Foundation, karena hal ini mencakup pertimbangan aksesibilitas bawaan dan praktik Compose yang ada yang dibahas di bagian ini.

Anda juga dapat menemukan contoh komponen tombol kustom di bagian Menghapus dan menetapkan semantik, serta informasi yang lebih mendetail tentang cara mendukung aksesibilitas di komponen kustom dalam panduan API.