Semantik di Compose

Komposisi menjelaskan UI aplikasi Anda dan dihasilkan dengan menjalankan composable. Komposisi adalah struktur hierarki yang terdiri dari composable yang mendeskripsikan UI Anda.

Di samping Komposisi, ada pohon paralel yang disebut hierarki semantik. Hierarki ini menjelaskan UI dengan cara alternatif yang dapat dipahami untuk layanan Aksesibilitas dan untuk framework Pengujian. Layanan aksesibilitas menggunakan hierarki untuk mendeskripsikan aplikasi kepada pengguna dengan kebutuhan spesifik. Framework pengujian menggunakan hierarki untuk berinteraksi dengan aplikasi dan membuat pernyataan tentang aplikasi tersebut. Hierarki Semantik tidak berisi informasi cara menggambar composable, tetapi berisi informasi makna semantik composable Anda.

Hierarki UI standar dan hierarki semantiknya
Gambar 1. Hierarki UI standar dan hierarki semantiknya.

Jika aplikasi Anda terdiri atas composable dan pengubah dari library foundation dan material Compose, pohon Semantik akan otomatis diisi dan dibuat untuk Anda. Namun, saat menambahkan composable kustom tingkat rendah, Anda harus menyediakan semantiknya secara manual. Mungkin juga ada situasi saat pohon Anda salah atau tidak sepenuhnya mewakili makna dari elemen di layar, dan Anda dapat menyesuaikan pohonnya.

Misalnya, perhatikan composable kalender kustom ini:

Composable kalender kustom dengan elemen hari yang dapat dipilih
Gambar 2. Composable kalender kustom dengan elemen hari yang dapat dipilih.

Dalam contoh ini, seluruh kalender diimplementasikan sebagai satu composable tingkat rendah menggunakan composable Layout dan menggambar langsung ke Canvas. Jika Anda tidak melakukan tindakan lain, layanan aksesibilitas tidak akan menerima informasi yang cukup tentang konten composable dan pilihan pengguna dalam kalender. Misalnya, jika pengguna mengklik pada hari yang berisi 17, framework aksesibilitas hanya akan menerima informasi deskripsi untuk seluruh kontrol kalender. Dalam hal ini, layanan aksesibilitas TalkBack akan mengumumkan "Kalender" atau, yang sedikit lebih baik, "Kalender April" dan pengguna akan bertanya-tanya hari apa yang dipilih. Agar composable ini lebih mudah diakses, Anda harus menambahkan informasi semantik secara manual.

Properti semantik

Semua node di pohon UI dengan beberapa makna semantik memiliki node paralel di pohon Semantik. Node di pohon Semantik berisi properti yang menyajikan makna composable yang sesuai. Misalnya, composable Text berisi properti semantik text karena itu adalah makna dari composable tersebut. Icon berisi properti contentDescription (jika ditetapkan oleh developer) yang menyampaikan makna Icon dalam teks. Composable dan pengubah yang di-build berdasarkan library fondasi Compose sudah menetapkan properti yang relevan untuk Anda. Secara opsional, tetapkan atau ganti properti Anda dengan pengubah semantics dan clearAndSetSemantics. Misalnya, tambahkan tindakan aksesibilitas kustom ke node, berikan deskripsi status alternatif untuk elemen yang dapat diganti statusnya, atau tunjukkan bahwa composable teks tertentu harus dianggap sebagai judul.

Untuk memvisualisasikan hierarki Semantik, gunakan Alat Layout Inspector atau gunakan metode printToLog() dalam pengujian. Tindakan ini akan mencetak pohon Semantik saat ini di dalam Logcat.

class MyComposeTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun MyTest() {
        // Start the app
        composeTestRule.setContent {
            MyTheme {
                Text("Hello world!")
            }
        }
        // Log the full semantics tree
        composeTestRule.onRoot().printToLog("MY TAG")
    }
}

Output pengujian ini adalah:

    Printing with useUnmergedTree = 'false'
    Node #1 at (l=0.0, t=63.0, r=221.0, b=120.0)px
     |-Node #2 at (l=0.0, t=63.0, r=221.0, b=120.0)px
       Text = '[Hello world!]'
       Actions = [GetTextLayoutResult]

Pertimbangkan cara properti semantik menyampaikan arti composable. Pertimbangkan Switch. Ini yang akan dilihat pengguna:

Gambar 3. Tombol akses dalam status "Aktif" dan "Nonaktif".

Untuk mendeskripsikan makna elemen ini, Anda dapat mengucapkan: "Ini adalah Tombol Akses, yang merupakan elemen yang dapat diganti statusnya pada status 'Aktif'. Anda dapat mengkliknya untuk berinteraksi dengannya."

Inilah kegunaan properti semantik yang sebenarnya. Node semantik dari elemen Switch ini berisi properti berikut, seperti yang divisualisasikan dengan Layout Inspector:

Layout Inspector menampilkan properti Semantik dari composable Switch
Gambar 4. Layout Inspector menampilkan properti Semantik dari composable Switch.

Role menunjukkan jenis elemen. StateDescription menjelaskan cara status "Aktif" direferensikan. Secara default, ini adalah versi lokal dari kata "Aktif", tetapi dapat dibuat lebih spesifik (misalnya, "Diaktifkan") berdasarkan konteksnya. ToggleableState adalah status Tombol saat ini. Properti OnClick merujuk ke metode yang digunakan untuk berinteraksi dengan elemen ini. Untuk mengetahui daftar lengkap properti semantik, lihat objek SemanticsProperties. Untuk daftar lengkap Tindakan Aksesibilitas yang mungkin, lihat objek SemanticsActions.

Melacak properti semantik dari setiap composable di aplikasi Anda akan membuka berbagai kemungkinan yang canggih. Beberapa contohnya:

  • Talkback menggunakan properti untuk membacakan secara lisan apa yang ditampilkan di layar dan memungkinkan pengguna berinteraksi dengannya secara lancar. Untuk composable Switch, Talkback mungkin akan bertuliskan: "Aktif; Switch; ketuk dua kali untuk beralih". Pengguna dapat mengetuk dua kali layar untuk menonaktifkan Tombol.
  • Framework pengujian menggunakan properti untuk menemukan node, berinteraksi dengannya, dan membuat pernyataan. Contoh pengujian untuk Tombol Akses dapat berupa:
    val mySwitch = SemanticsMatcher.expectValue(
        SemanticsProperties.Role, Role.Switch
    )
    composeTestRule.onNode(mySwitch)
        .performClick()
        .assertIsOff()

Pohon Semantik yang digabungkan dan yang tidak digabungkan

Seperti yang disebutkan sebelumnya, setiap composable di pohon UI mungkin memiliki nol atau beberapa properti semantik yang ditetapkan. Jika composable belum memiliki properti semantik yang ditetapkan, composable tersebut tidak akan disertakan sebagai bagian dari hierarki Semantik. Dengan demikian, pohon Semantik hanya berisi node yang benar-benar berisi makna semantik. Akan tetapi, terkadang menyampaikan makna yang benar dari informasi yang ditampilkan di layar juga berguna untuk menggabungkan sub-pohon tertentu dari node dan memperlakukannya sebagai node. Dengan begitu, Anda dapat mempertimbangkan sekumpulan node secara keseluruhan, bukan menangani setiap node turunan satu per satu. Prinsipnya adalah, setiap node dalam pohon ini mewakili elemen yang dapat difokuskan selama menggunakan layanan Aksesibilitas.

Contoh composable tersebut adalah Button. Anda dapat memikirkan tombol sebagai elemen tunggal, meskipun dapat berisi beberapa node turunan:

Button(onClick = { /*TODO*/ }) {
    Icon(
        imageVector = Icons.Filled.Favorite,
        contentDescription = null
    )
    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
    Text("Like")
}

Dalam hierarki Semantik, properti turunan tombol digabungkan, dan tombol ditampilkan sebagai satu node daun dalam hierarki:

Menggabungkan representasi semantik satu daun
Gambar 5. Menggabungkan representasi semantik satu daun.

Composable dan pengubah dapat menunjukkan bahwa keduanya ingin menggabungkan properti semantik turunannya dengan memanggil Modifier.semantics (mergeDescendants = true) {}. Menyetel properti ini ke true menunjukkan bahwa properti semantik harus digabungkan. Dalam contoh Button, composable Button menggunakan pengubah clickable secara internal yang menyertakan pengubah semantics ini. Oleh karena itu, node turunan dari tombol digabungkan. Baca dokumentasi aksesibilitas untuk mempelajari lebih lanjut kapan Anda harus mengubah perilaku penggabungan dalam composable.

Beberapa pengubah dan composable di library Foundation dan Material Compose memiliki kumpulan properti ini. Misalnya, pengubah clickable dan toggleable akan otomatis menggabungkan turunannya. Selain itu, composable ListItem akan menggabungkan turunannya.

Periksa pohonnya

Hierarki semantik sebenarnya merupakan dua pohon yang berbeda. Ada pohon Semantik gabungan, yang menggabungkan node turunan saat mergeDescendants ditetapkan ke true. Ada juga hierarki Semantik terpisah, yang tidak menerapkan penggabungan, tetapi menjaga setiap node tetap utuh. Layanan aksesibilitas menggunakan hierarki yang terpisah dan menerapkan algoritma penggabungannya sendiri, dengan mempertimbangkan properti mergeDescendants. Framework pengujian menggunakan hierarki gabungan secara default.

Anda dapat memeriksa kedua pohon tersebut menggunakan metode printToLog(). Secara default, dan seperti pada contoh sebelumnya, pohon gabungan dicatat ke dalam log. Untuk mencetak hierarki yang terpisah, tetapkan parameter useUnmergedTree dari pencocok onRoot() ke true:

composeTestRule.onRoot(useUnmergedTree = true).printToLog("MY TAG")

Layout Inspector memungkinkan Anda menampilkan hierarki Semantik yang digabungkan dan terpisah, dengan memilih yang diinginkan dalam filter tampilan:

Opsi tampilan Layout Inspector, yang memungkinkan tampilan hierarki Semantik yang digabungkan dan yang terpisah
Gambar 6. Opsi tampilan Layout Inspector, yang memungkinkan tampilan hierarki Semantik yang digabungkan dan terpisah.

Untuk setiap node di pohon Anda, Layout Inspector akan menampilkan Semantik Gabungan dan Semantik yang ditetapkan di node tersebut dalam panel properti:

Properti semantik digabungkan dan disetel
Gambar 7. Properti semantik digabungkan dan ditetapkan.

Secara default, pencocok dalam Framework Pengujian menggunakan pohon Semantik gabungan. Itulah sebabnya Anda dapat berinteraksi dengan Button dengan mencocokkan teks yang ditampilkan di dalamnya:

composeTestRule.onNodeWithText("Like").performClick()

Ganti perilaku ini dengan menetapkan parameter useUnmergedTree matcher ke true, seperti pencocok onRoot.

Perilaku penggabungan

Saat composable menunjukkan bahwa turunannya harus digabungkan, bagaimana penggabungan ini sebenarnya dilakukan?

Setiap properti semantik memiliki strategi penggabungan yang ditetapkan. Misalnya, properti ContentDescription menambahkan semua nilai ContentDescription turunan ke daftar. Periksa strategi penggabungan properti semantik dengan memeriksa implementasi mergePolicy di SemanticsProperties.kt. Properti dapat mengambil nilai induk atau turunan, menggabungkan nilai ke dalam daftar atau string, tidak mengizinkan penggabungan sama sekali dan menampilkan pengecualian, atau strategi penggabungan khusus lainnya.

Catatan penting adalah bahwa turunan yang telah menetapkan mergeDescendants = true sendiri tidak disertakan dalam penggabungan. Lihat contoh berikut:

Item daftar dengan gambar, beberapa teks, dan ikon bookmark
Gambar 8. Item daftar dengan gambar, beberapa teks, dan ikon bookmark.

Berikut adalah item daftar yang dapat diklik. Saat pengguna menekan baris, aplikasi akan membuka halaman detail artikel, tempat pengguna dapat membaca artikel. Di dalam item daftar, terdapat tombol untuk mem-bookmark artikel, yang membentuk elemen bertingkat yang dapat diklik, sehingga tombol tersebut muncul secara terpisah di hierarki gabungan. Konten lainnya yang ada di baris digabungkan:

Pohon gabungan berisi beberapa teks dalam daftar di dalam node Baris. Pohon terpisah berisi node yang terpisah untuk setiap komposisi Teks.
Gambar 9. Pohon gabungan berisi beberapa teks dalam daftar di dalam node Baris. Hierarki yang terpisah berisi node terpisah untuk setiap composable Text.

Menyesuaikan hierarki Semantik

Seperti yang disebutkan sebelumnya, Anda dapat mengganti atau menghapus properti semantik tertentu atau mengubah perilaku penggabungan hierarki. Hal ini sangat relevan saat Anda membuat komponen kustom sendiri. Tanpa menetapkan properti dan perilaku penggabungan yang benar, aplikasi Anda mungkin tidak dapat diakses, dan pengujian mungkin akan berperilaku berbeda dari yang Anda harapkan. Untuk membaca selengkapnya tentang beberapa kasus penggunaan umum saat Anda harus menyesuaikan hierarki Semantik, baca dokumentasi aksesibilitas. Jika Anda ingin mempelajari pengujian lebih lanjut, lihat panduan pengujian.

Referensi tambahan