Pelapisan arsitektur Jetpack Compose

Halaman ini menyediakan ringkasan tingkat tinggi tentang lapisan arsitektur yang membentuk Jetpack Compose, dan prinsip inti yang menjadi dasar desain ini.

Jetpack Compose bukan project monolitik tunggal; project ini dibuat dari sejumlah modul yang disusun menjadi satu untuk membentuk stack lengkap. Memahami berbagai modul yang membentuk Jetpack Compose memungkinkan Anda untuk:

  • Menggunakan tingkat abstraksi yang sesuai untuk membuat aplikasi atau library
  • Memahami kapan Anda dapat melakukan 'drop down' ke level yang lebih rendah untuk kontrol atau penyesuaian lebih lanjut
  • Meminimalkan dependensi Anda

Lapisan

Lapisan utama Jetpack Compose adalah:

Gambar 1. Lapisan utama Jetpack Compose.

Setiap lapisan dibuat di atas level yang lebih rendah, dengan menggabungkan fungsi untuk membuat komponen dengan tingkat yang lebih tinggi. Setiap lapisan dibuat di API publik dari lapisan yang lebih rendah untuk memverifikasi batas modul dan memungkinkan Anda mengganti lapisan apa pun yang diperlukan. Mari memeriksa lapisan ini dari bawah ke atas.

Runtime
Modul ini memberikan dasar-dasar runtime Compose, seperti remember, mutableStateOf, anotasi @Composable, dan SideEffect. Sebaiknya Anda membuat lapisan ini secara langsung jika Anda hanya memerlukan kemampuan pengelolaan hierarki Compose, bukan UI-nya.
UI
Lapisan UI terdiri dari beberapa modul ( ui-text, ui-graphics, ui-tooling, dll.). Modul ini mengimplementasikan dasar-dasar toolkit UI, seperti LayoutNode, Modifier, pengendali input, tata letak kustom, dan gambar. Sebaiknya memanfaatkan lapisan ini jika Anda hanya memerlukan konsep dasar toolkit UI.
Fondasi
Modul ini memberikan elemen penyusun terpadu desain sistem untuk Compose UI, seperti Row dan Column, LazyColumn, mengenali gestur tertentu, dll. Anda dapat mempertimbangkan untuk membuat lapisan dasar guna membuat sistem desain Anda sendiri.
Material
Modul ini menyediakan implementasi sistem Desain Material untuk Compose UI, yang menyediakan sistem bertema, komponen bergaya, indikasi ripple, dan ikon. Buat lapisan ini saat menggunakan Desain Material di aplikasi Anda.

Prinsip-prinsip desain

Prinsip panduan Jetpack Compose adalah menyediakan beberapa fungsionalitas kecil dan fokus yang dapat disusun (atau dikumpulkan) bersama, bukan beberapa komponen monolitik. Pendekatan ini memiliki sejumlah keuntungan.

Kontrol

Komponen dengan level yang lebih tinggi cenderung melakukan lebih banyak hal untuk Anda, tetapi membatasi kontrol langsung yang Anda miliki. Jika memerlukan kontrol lebih besar, Anda dapat melakukan "drop down" untuk menggunakan komponen dengan level lebih rendah.

Misalnya, jika ingin menganimasikan warna komponen, Anda dapat menggunakan animateColorAsState API:

val color = animateColorAsState(if (condition) Color.Green else Color.Red)

Namun, jika komponen harus selalu dimulai dengan warna abu-abu, Anda tidak dapat melakukannya dengan API ini. Sebagai gantinya, Anda dapat melakukan drop-down untuk menggunakan Animatable API dengan level lebih rendah:

val color = remember { Animatable(Color.Gray) }
LaunchedEffect(condition) {
    color.animateTo(if (condition) Color.Green else Color.Red)
}

animateColorAsState API dengan level yang lebih tinggi itu sendiri dibuat pada Animatable API dengan level yang lebih rendah. Penggunaan API dengan level yang lebih rendah lebih kompleks tetapi menawarkan kontrol lebih besar. Pilih tingkat abstraksi yang paling sesuai dengan kebutuhan Anda.

Penyesuaian

Menyusun komponen dengan level yang lebih tinggi dari elemen dasar yang lebih kecil membuat penyesuaian komponen yang Anda perlukan jauh lebih mudah. Misalnya, sebaiknya implementasikan Button yang disediakan oleh lapisan Material:

@Composable
fun Button(
    // …
    content: @Composable RowScope.() -> Unit
) {
    Surface(/* … */) {
        CompositionLocalProvider(/* … */) { // set LocalContentAlpha
            ProvideTextStyle(MaterialTheme.typography.button) {
                Row(
                    // …
                    content = content
                )
            }
        }
    }
}

Button disusun dari 4 komponen:

  1. Material Surface yang menyediakan latar belakang, bentuk, penanganan klik, dll.

  2. CompositionLocalProvider yang mengubah alfa konten saat tombol diaktifkan atau dinonaktifkan

  3. ProvideTextStyle menyetel gaya teks default yang akan digunakan

  4. Row menyediakan kebijakan tata letak default untuk konten tombol

Kami telah menghilangkan beberapa parameter dan komentar untuk membuat struktur lebih jelas, tetapi seluruh komponen hanya berisi sekitar 40 baris kode karena hanya menyusun 4 komponen ini untuk mengimplementasikan tombol. Komponen seperti Button bersifat dogmatis untuk mengetahui parameter yang diekspos, sehingga menyeimbangkan kemungkinan penyesuaian umum terhadap ledakan parameter yang dapat membuat komponen lebih sulit digunakan. Komponen material, misalnya, menawarkan penyesuaian yang ditentukan dalam sistem Desain Material, sehingga memudahkan untuk mengikuti prinsip desain material.

Namun, jika ingin membuat penyesuaian di luar parameter komponen, Anda dapat melakukan "drop down" level dan fork komponen. Misalnya, Desain Material menetapkan bahwa tombol harus memiliki latar belakang berwarna solid. Jika Anda memerlukan latar belakang gradien, opsi ini tidak didukung oleh parameter Button. Dalam hal ini, Anda dapat menggunakan implementasi Button Material sebagai referensi dan membuat komponen Anda sendiri:

@Composable
fun GradientButton(
    // …
    background: List<Color>,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Row(
        // …
        modifier = modifier
            .clickable(onClick = {})
            .background(
                Brush.horizontalGradient(background)
            )
    ) {
        CompositionLocalProvider(/* … */) { // set material LocalContentAlpha
            ProvideTextStyle(MaterialTheme.typography.button) {
                content()
            }
        }
    }
}

Implementasi di atas terus menggunakan komponen dari lapisan Material, seperti konsep Material alfa konten saat ini dan gaya teks saat ini. Namun, metode ini menggantikan Surface material dengan Row dan menata gayanya untuk mencapai tampilan yang diinginkan.

Jika tidak ingin menggunakan konsep Material sama sekali, misalnya jika mem-build sistem desain pesanan khusus Anda sendiri, Anda dapat melakukan drop-down hanya untuk menggunakan komponen lapisan dasar:

@Composable
fun BespokeButton(
    // …
    backgroundColor: Color,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Row(
        // …
        modifier = modifier
            .clickable(onClick = {})
            .background(backgroundColor)
    ) {
        // No Material components used
        content()
    }
}

Jetpack Compose mencadangkan nama yang paling sederhana untuk komponen dengan level tertinggi. Misalnya, androidx.compose.material.Text dibuat berdasarkan androidx.compose.foundation.text.BasicText. Hal ini memungkinkan Anda melakukan implementasi sendiri dengan nama yang paling mudah ditemukan jika ingin mengganti level yang lebih tinggi.

Memilih abstraksi yang tepat

Filosofi Compose dalam membuat komponen berlapis dan dapat digunakan kembali berarti bahwa Anda tidak boleh selalu menjangkau elemen penyusun level bawah. Banyak komponen dengan level yang lebih tinggi tidak hanya menawarkan lebih banyak fungsi, tetapi sering kali menerapkan praktik terbaik seperti mendukung aksesibilitas.

Misalnya, jika ingin menambahkan dukungan gestur ke komponen kustom, Anda dapat membuat dari awal menggunakan Modifier.pointerInput, tetapi ada komponen lain dengan level yang lebih tinggi di atas ini, yang mungkin menawarkan titik awal yang lebih baik, misalnya Modifier.draggable, Modifier.scrollable atau Modifier.swipeable.

Biasanya lebih suka membuat komponen level tertinggi yang menawarkan fungsi yang Anda butuhkan untuk mendapatkan manfaat dari praktik terbaik yang disertakannya.

Selengkapnya

Lihat contoh Jetsnack untuk mengetahui contoh pembuatan sistem desain kustom.