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 Switch Access.

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.

Menjelaskan elemen visual

Saat Anda menentukan Image atau composable 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 ikon "bagikan" disorot

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

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

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

Beberapa elemen visual bersifat murni 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
fun PostImage(post: Post, modifier: Modifier = Modifier) {
  val image = post.imageThumb ?: imageResource(R.drawable.placeholder_1_1)

  Image(
    bitmap = 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.

Menetapkan deskripsi konten kustom

Mungkin ada situasi saat Anda ingin menetapkan deskripsi konten secara eksplisit. Dalam situasi tersebut, Anda dapat menggunakan pengubah semantics untuk menetapkan deskripsi konten secara langsung ke sebuah composable. Misalnya, Anda mungkin ingin menggambar ikon khusus, yang berarti Anda tidak dapat langsung menggunakan composable Icon:

@Composable
fun CustomShareIcon(modifier: Modifier = Modifier) {
  val cd = stringResource(R.string.custom_share_icon_content_description)
  Canvas(modifier.semantics { contentDescription = cd }) {
    /* Draw custom icon here */
  }
}

Menggabungkan elemen

Layanan aksesibilitas seperti Talkback dan Tombol Akses memungkinkan pengguna memindahkan fokus ke seluruh elemen di layar. Elemen-elemen harus difokuskan pada perincian yang tepat. Jika setiap satu composable level rendah di layar Anda berfokus secara independen, pengguna harus berinteraksi banyak untuk berpindah 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 penampung sekaligus, 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 khusus 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
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 penampung yang dapat di-scroll. Misalnya, layar dapat menampilkan konten lengkap artikel yang sedang dibaca pengguna:

Screenshot postingan blog, dengan teks artikel dalam penampung 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.h5,
    modifier = Modifier.semantics { heading() }
  )
}

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 pertimbangan aksesibilitas. Misalnya Anda mengganti Material Checkbox 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.