Default API

API Material, Compose UI, dan Foundation menerapkan dan menawarkan banyak praktik aksesibilitas 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 yang telah ditentukan sebelumnya yang mencakup kasus penggunaan standar. Namun, selalu periksa kembali apakah setelan default ini sesuai dengan kebutuhan aksesibilitas Anda. Jika tidak, Compose menyediakan cara untuk mencakup persyaratan yang lebih spesifik.

Memahami semantik dan pola aksesibilitas default di API Compose 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—menetapkan 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 penampung 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 sedang dipilih dan dibatalkan pilihannya.
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. Hal 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 yang sangat kecil dan dapat diklik yang 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 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 diperbesar ukurannya untuk membuat target sentuh yang lebih besar.
Gambar 5. Target sentuh kotak yang lebih besar.

Elemen grafis

Saat Anda menentukan composable Image atau Icon, tidak ada cara otomatis bagi framework Android untuk memahami apa yang sedang 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 empat ikon yang dapat diklik, dengan ikon 'bagikan' disorot.
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 menjelaskan elemen grafis. Gunakan string yang dilokalkan, karena string ini terlihat 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 ditentukan sebelumnya yang menjelaskan perilaku intrinsiknya, dan dapat diubah melalui Compose API lainnya.

Elemen interaktif

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

Misalnya, Button Material dapat terdiri dari ikon turunan dan beberapa teks. Daripada memperlakukan anak-anak sebagai individu, Material Button menggabungkan semantik turunannya secara default, sehingga layanan aksesibilitas dapat mengelompokkannya dengan tepat:

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

Demikian pula, penggunaan 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 elemen yang dapat diklik induk untuk memberikan informasi tambahan ke layanan aksesibilitas dan menawarkan representasi tindakan yang lebih sempurna:

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 "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 tetap ingin mengubah label pengumuman dari default. Untuk melakukannya, pisahkan setelan clickable dari pengubahan pengumuman dengan 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. API Compose yang ada, seperti clickable atau Button, menanganinya untuk Anda. Logika penggabungan memverifikasi bahwa label dan tindakan pengubah terluar dilakukan untuk informasi yang ada. Pada contoh sebelumnya, NestedArticleListItem otomatis meneruskan tindakan klik openArticle() ke semantik clickable-nya. Anda dapat membiarkan tindakan klik null di tindakan pengubah semantik kedua. Namun, label klik diambil dari pengubah semantik kedua onClick(label = "Open this document") karena tidak ada di yang 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 penerapan komponen serupa di library Material atau library Compose lainnya. Kemudian, tiru atau ubah perilaku aksesibilitasnya sesuai kebutuhan. 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 Semantik yang jelas dan ditetapkan, serta informasi yang lebih mendetail tentang cara mendukung aksesibilitas di komponen kustom dalam pedoman API.