Komposisi menjelaskan UI aplikasi Anda dan diproduksi dengan menjalankan composable. Komposisi adalah struktur pohon yang terdiri dari composable yang mendeskripsikan UI Anda.
Di samping Komposisi, terdapat pohon paralel yang disebut Pohon semantik. Pohon ini menjelaskan UI dengan cara lain yang dapat dipahami untuk layanan Aksesibilitas dan untuk framework Pengujian. Layanan aksesibilitas menggunakan pohon untuk mendeskripsikan aplikasi kepada pengguna yang memiliki kebutuhan tertentu. Framework Pengujian menggunakannya untuk berinteraksi dengan aplikasi Anda dan membuat pernyataan tentang aplikasi tersebut. Hierarki Semantik tidak berisi informasi cara menggambar composable Anda, tetapi berisi informasi makna semantik dari composable.
Gambar 1. Hierarki UI umum dan pohon 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:
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 hanya akan
mengumumkan "Kalender" atau, yang sedikit lebih baik, "Kalender bulan 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 atribut
library foundation Compose sudah menetapkan
properti yang relevan untuk Anda. Atau, Anda dapat menetapkan atau mengganti
properti dengan pengubah
semantics
dan clearAndSetSemantics
. Misalnya, Anda dapat menambahkan tindakan aksesibilitas kustom ke node, memberikan
deskripsi status alternatif untuk
elemen yang dapat diganti, atau menunjukkan bahwa composable teks tertentu harus
dianggap sebagai judul.
Untuk memvisualisasikan pohon Semantik, kita dapat menggunakan Alat Layout Inspector atau menggunakan metode printToLog()
di dalam pengujian. Cara 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]
Mari kita perhatikan contoh untuk melihat cara properti semantik digunakan guna menyampaikan makna composable. Mari kita pikirkan 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, saat ini dalam status 'Aktif'. Anda dapat mengkliknya untuk berinteraksi dengan tombol”.
Inilah kegunaan properti semantik yang sebenarnya. Node semantik dari elemen Tombol Akses ini berisi properti berikut, seperti yang divisualisasikan dengan Layout Inspector:
Gambar 4. Layout Inspector menampilkan properti Semantik composable Tombol Akses.
Role
menunjukkan jenis elemen yang sedang dilihat. StateDescription
menjelaskan bagaimana status "Aktif" seharusnya direferensikan. Secara
default, ini hanyalah versi lokal dari kata "Aktif", tetapi
dapat dibuat lebih spesifik (misalnya, "Diaktifkan") dengan menyesuaikan konteks. ToggleableState
adalah status Tombol Akses saat ini. Properti OnClick
mereferensikan metode yang digunakan untuk berinteraksi dengan elemen ini. Untuk 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 membaca informasi yang ditampilkan di layar dengan jelas dan memungkinkan pengguna berinteraksi dengannya secara lancar. Untuk Tombol Akses, mungkin tertulis: “Aktif; Tombol Akses; ketuk dua kali untuk beralih”. Pengguna dapat mengetuk layar dua kali untuk menonaktifkan Tombol Akses.
-
Framework pengujian menggunakan properti untuk menemukan node, berinteraksi dengannya,
dan membuat pernyataan. Contoh pengujian untuk Tombol Akses kita adalah:
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 tidak memiliki properti semantik yang ditetapkan, composable tidak akan disertakan sebagai bagian dari pohon 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, kita bisa 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 dari composable tersebut adalah Tombol. Kita ingin menjelaskan Tombol sebagai elemen tunggal, meskipun mungkin berisi beberapa node turunan:
Button(onClick = { /*TODO*/ }) {
Icon(
imageVector = Icons.Filled.Favorite,
contentDescription = null
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text("Like")
}
Dalam pohon Semantik kita, properti turunan Tombol digabungkan, dan Tombol ditampilkan sebagai satu node daun dalam pohon:
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 Tombol akan
digabungkan. Baca dokumentasi aksesibilitas untuk mempelajari lebih lanjut kapan Anda
harus mengubah perilaku penggabungan di 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.
Memeriksa pohon
Saat membahas pohon Semantik, kita sebenarnya membahas 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 pohon yang terpisah dan menerapkan algoritma penggabungan
sendiri dengan mempertimbangkan properti mergeDescendants
. Framework
pengujian menggunakan struktur gabungan secara default.
Anda dapat memeriksa kedua pohon tersebut menggunakan metode printToLog()
. Secara default, dan seperti dalam
contoh sebelumnya, pohon gabungan akan dicatat dalam log. Untuk mencetak pohon
yang terpisah, tetapkan parameter useUnmergedTree
dari pencocok onRoot()
ke
true
:
composeTestRule.onRoot(useUnmergedTree = true).printToLog("MY TAG")
Dengan Layout Inspector, Anda dapat menampilkan pohon Semantik gabungan dan terpisah dengan memilih pohon yang dipilih dalam filter tampilan:
Gambar 5. Opsi tampilan Layout Inspector, yang memungkinkan tampilan pohon Semantik gabungan dan terpisah.
Untuk setiap node di pohon Anda, Layout Inspector akan menampilkan Semantik Gabungan dan Semantik yang ditetapkan di node tersebut dalam panel properti:
Secara default, pencocok dalam Framework Pengujian menggunakan pohon Semantik gabungan. Oleh karena itu, Anda dapat berinteraksi dengan Tombol dengan mencocokkan teks yang ditampilkan di dalamnya:
composeTestRule.onNodeWithText("Like").performClick()
Anda dapat mengganti perilaku ini dengan menetapkan parameter useUnmergedTree
dari pencocok ke true
, seperti yang kita lakukan sebelumnya dengan 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. Anda dapat memastikan strategi penggabungan dari properti semantik dengan memeriksa
implementasi mergePolicy
-nya di SemanticsProperties.kt
.
Properti dapat memilih untuk selalu mengambil nilai induk atau turunan, menggabungkan
nilai ke dalam daftar atau string, tidak mengizinkan penggabungan sama sekali dan menampilkan
pengecualian, atau strategi penggabungan kustom lainnya.
Perhatikan bahwa turunan yang telah menetapkan
mergeDescendants = true
tidak akan disertakan dalam penggabungan. Mari kita lihat contoh berikut:
Gambar 6. Item daftar dengan gambar, beberapa teks, dan ikon bookmark.
Di sini, kita memiliki 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 memberi bookmark artikel ini. Dalam hal ini, kita memiliki elemen bertingkat yang dapat diklik sehingga tombol akan muncul secara terpisah di pohon gabungan. Konten lainnya yang ada di baris digabungkan:
Gambar 7. Pohon gabungan berisi beberapa teks dalam daftar di dalam node Baris. Pohon terpisah berisi node yang terpisah untuk setiap komposisi Teks.
Menyesuaikan pohon Semantik
Seperti yang disebutkan sebelumnya, Anda dapat mengganti atau menghapus properti semantik tertentu, atau mengubah perilaku penggabungan pohon. Penyesuaian ini sangat berguna saat Anda membuat komponen kustom sendiri. Tanpa menetapkan properti dan perilaku penggabungan yang tepat, aplikasi mungkin tidak dapat diakses, dan perilaku pengujian mungkin berbeda dari yang Anda harapkan. Untuk membaca lebih lanjut beberapa kasus penggunaan umum ketika Anda harus menyesuaikan pohon Semantik, baca dokumentasi aksesibilitas. Jika Anda ingin mempelajari pengujian lebih lanjut, lihat Panduan Pengujian.