Pengubah memungkinkan Anda mendekorasi atau meningkatkan composable. Pengubah memungkinkan Anda melakukan hal-hal berikut:
- Mengubah ukuran, tata letak, perilaku, dan tampilan composable
- Menambahkan informasi, seperti label aksesibilitas
- Memproses input pengguna
- Menambahkan interaksi tingkat tinggi, seperti membuat elemen yang dapat diklik, dapat di-scroll, dapat ditarik, atau dapat di-zoom
Pengubah adalah objek Kotlin standar. Buat pengubah dengan memanggil salah satu fungsi class
Modifier:
@Composable private fun Greeting(name: String) { Column(modifier = Modifier.padding(24.dp)) { Text(text = "Hello,") Text(text = name) } }
Anda dapat menggabungkan fungsi-fungsi berikut untuk mengomposisikannya:
@Composable private fun Greeting(name: String) { Column( modifier = Modifier .padding(24.dp) .fillMaxWidth() ) { Text(text = "Hello,") Text(text = name) } }
Dalam kode di atas, perhatikan berbagai fungsi pengubah yang digunakan bersama-sama.
paddingmenempatkan ruang di sekitar elemen.fillMaxWidthmembuat composable mengisi lebar maksimum yang diberikan kepadanya dari induknya.
Praktik terbaiknya adalah semua composable Anda menerima parameter modifier, dan meneruskan pengubah tersebut ke turunan pertamanya yang menampilkan UI.
Dengan melakukannya,
kode Anda akan lebih mudah digunakan kembali dan membuat perilakunya lebih dapat diprediksi serta intuitif. Untuk
informasi selengkapnya, lihat panduan Compose API, Elemen menerima dan mematuhi
parameter Pengubah.
Pentingnya urutan pengubah
Urutan fungsi pengubah bersifat signifikan. Karena setiap fungsi melakukan
perubahan pada Modifier yang ditampilkan oleh fungsi sebelumnya, urutan tersebut
akan memengaruhi hasil akhir. Mari kita lihat contohnya:
@Composable fun ArtistCard(/*...*/) { val padding = 16.dp Column( Modifier .clickable(onClick = onClick) .padding(padding) .fillMaxWidth() ) { // rest of the implementation } }
Pada kode di atas, seluruh area dapat diklik, termasuk padding
di sekitarnya, karena pengubah padding telah diterapkan setelah pengubah clickable. Jika urutan pengubah dibalik, ruang yang ditambahkan oleh padding tidak akan merespons input pengguna:
@Composable fun ArtistCard(/*...*/) { val padding = 16.dp Column( Modifier .padding(padding) .clickable(onClick = onClick) .fillMaxWidth() ) { // rest of the implementation } }
Pengubah bawaan
Jetpack Compose menyediakan daftar pengubah bawaan untuk membantu Anda mendekorasi atau meningkatkan composable. Berikut adalah beberapa pengubah umum yang akan Anda gunakan untuk menyesuaikan tata letak.
padding dan size
Secara default, tata letak yang disediakan di Compose menggabungkan turunannya. Namun,
Anda dapat menyetel ukuran dengan menggunakan pengubah size:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image(/*...*/) Column { /*...*/ } } }
Perhatikan bahwa ukuran yang Anda tentukan mungkin tidak diterapkan jika tidak memenuhi batasan yang berasal dari induk tata letak. Jika Anda mengharuskan ukuran composable
diperbaiki, terlepas dari batasan yang masuk,
gunakan pengubah requiredSize:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image( /*...*/ modifier = Modifier.requiredSize(150.dp) ) Column { /*...*/ } } }
Dalam contoh ini, meski induk height disetel ke 100.dp, tinggi
Image akan menjadi 150.dp, karena pengubah requiredSize
lebih diutamakan.
Jika Anda ingin tata letak turunan mengisi semua tinggi yang tersedia yang diizinkan oleh
induk, tambahkan pengubah fillMaxHeight (Compose juga menyediakan
fillMaxSize dan fillMaxWidth):
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image( /*...*/ modifier = Modifier.fillMaxHeight() ) Column { /*...*/ } } }
Untuk menambahkan padding di sekitar elemen, setel pengubah padding.
Jika Anda ingin menambahkan padding di atas dasar pengukuran teks sehingga Anda mencapai
jarak tertentu dari bagian atas tata letak ke dasar pengukuran, gunakan
pengubah paddingFromBaseline:
@Composable fun ArtistCard(artist: Artist) { Row(/*...*/) { Column { Text( text = artist.name, modifier = Modifier.paddingFromBaseline(top = 50.dp) ) Text(artist.lastSeenOnline) } } }
Offset
Untuk menempatkan tata letak relatif ke posisi aslinya, tambahkan pengubah
offset dan setel offset dalam sumbu x dan y.
Offset bisa positif dan tidak positif. Perbedaan antara
padding dan offset adalah menambahkan offset ke composable tidak akan
mengubah pengukurannya:
@Composable fun ArtistCard(artist: Artist) { Row(/*...*/) { Column { Text(artist.name) Text( text = artist.lastSeenOnline, modifier = Modifier.offset(x = 4.dp) ) } } }
Pengubah offset diterapkan secara horizontal sesuai dengan arah tata letak.
Dalam konteks kiri-ke-kanan, offset positif menggeser elemen ke
kanan, sedangkan dalam konteks kanan-ke-kiri, elemen akan bergeser ke kiri.
Jika Anda perlu menyetel offset tanpa mempertimbangkan arah tata letak, lihat pengubah absoluteOffset, ketika nilai offset positif selalu menggeser elemen ke kanan.
Pengubah offset menyediakan dua overload - offset yang menggunakan
offset sebagai parameter dan offset yang menggunakan lambda.
Untuk informasi yang lebih mendalam tentang kapan harus menggunakan setiap pengubah tersebut dan cara mengoptimalkan
performa, baca bagian
Performa Compose - Menunda pembacaan selama mungkin.
Keamanan cakupan di Compose
Di Compose terdapat pengubah yang hanya dapat digunakan saat diterapkan ke turunan composable tertentu. Compose menerapkannya melalui cakupan kustom.
Misalnya, jika Anda ingin membuat turunan sebesar Box induk tanpa
memengaruhi ukuran Box, gunakan
pengubah
matchParentSize. matchParentSize hanya tersedia di
BoxScope.
Oleh karenanya, ini hanya dapat digunakan pada turunan dalam induk Box.
Keamanan cakupan mencegah Anda menambahkan pengubah yang tidak akan berfungsi di composable dan cakupan lain serta menghemat waktu untuk uji coba.
Pengubah cakupan memberi tahu induk tentang beberapa informasi turunan yang harus diketahui induk. Ini juga biasa disebut sebagai pengubah data induk. Internalnya berbeda dari pengubah tujuan umum, tetapi dari perspektif penggunaan, perbedaan ini tidak penting.
matchParentSize dalam Box
Seperti yang disebutkan di atas, jika Anda ingin tata letak turunan berukuran sama dengan Box induk
tanpa memengaruhi ukuran Box, gunakan pengubah matchParentSize.
Perhatikan bahwa matchParentSize hanya tersedia dalam cakupan Box, artinya
hanya berlaku untuk turunan langsung dari Box composable.
Dalam contoh di bawah ini, Spacer turunan mengambil ukurannya dari Box induknya,
yang kemudian mengambil ukurannya dari turunan terbesar,
dalam hal ini ArtistCard.
@Composable fun MatchParentSizeComposable() { Box { Spacer( Modifier .matchParentSize() .background(Color.LightGray) ) ArtistCard() } }
Jika fillMaxSize digunakan sebagai pengganti matchParentSize, Spacer akan mengambil
semua ruang tersedia yang diizinkan untuk induk, yang pada akhirnya menyebabkan induk
memperluas dan mengisi semua ruang yang tersedia.
weight di Row dan Column
Seperti yang telah Anda lihat di bagian sebelumnya tentang Padding dan
ukuran, secara default, ukuran composable ditentukan oleh konten
yang digabungkannya. Anda dapat menyetel ukuran composable agar fleksibel dalam induknya
menggunakan Pengubah weight yang hanya tersedia di RowScope, dan
ColumnScope.
Mari kita ambil Row yang berisi dua composable Box.
Kotak pertama diberikan dua kali weight kotak kedua, sehingga diberi lebar
dua kali lipat. Karena Row selebar 210.dp, Box pertama selebar 140.dp, dan
kedua selebar 70.dp:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.fillMaxWidth() ) { Image( /*...*/ modifier = Modifier.weight(2f) ) Column( modifier = Modifier.weight(1f) ) { /*...*/ } } }
Mengekstrak dan menggunakan kembali pengubah
Beberapa pengubah dapat dirantai untuk mendekorasi atau
meningkatkan composable. Rantai ini dibuat melalui antarmuka Modifier
yang mewakili satu daftar Modifier.Elements yang diurutkan dan tidak dapat diubah.
Setiap Modifier.Element mewakili satu perilaku, seperti perilaku tata letak, gambar
dan grafis, semua perilaku terkait gestur, fokus, dan semantik, serta
peristiwa input perangkat. Urutannya penting: elemen pengubah yang
ditambahkan terlebih dahulu akan diterapkan terlebih dahulu.
Terkadang, akan bermanfaat jika menggunakan kembali instance rantai pengubah yang sama dalam beberapa composable, dengan mengekstraknya ke dalam variabel dan mengangkatnya ke dalam cakupan yang lebih tinggi. Hal ini dapat meningkatkan keterbacaan kode atau membantu meningkatkan performa aplikasi karena beberapa alasan:
- Alokasi ulang pengubah tidak akan diulang saat rekomposisi terjadi untuk composable yang menggunakannya
- Rantai pengubah berpotensi sangat panjang dan rumit, sehingga menggunakan kembali instance rantai yang sama dapat mengurangi beban kerja yang perlu dilakukan runtime Compose saat membandingkannya
- Ekstraksi ini mendukung kebersihan kode, konsistensi, dan pemeliharaan kode di seluruh codebase
Praktik terbaik untuk menggunakan kembali pengubah
Buat rantai Modifier Anda sendiri dan ekstrak untuk digunakan kembali pada beberapa
komponen composable. Anda dapat menyimpan pengubah karena pengubah
merupakan objek yang mirip data:
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp)
Mengekstrak dan menggunakan kembali pengubah saat mengamati status yang sering berubah
Saat mengamati status yang sering berubah di dalam composable, seperti status
animasi atau scrollState, mungkin ada sejumlah besar rekomposisi
yang dilakukan. Dalam hal ini, pengubah akan dialokasikan pada setiap rekomposisi
dan kemungkinan besar untuk setiap frame:
@Composable fun LoadingWheelAnimation() { val animatedState = animateFloatAsState(/*...*/) LoadingWheel( // Creation and allocation of this modifier will happen on every frame of the animation! modifier = Modifier .padding(12.dp) .background(Color.Gray), animatedState = animatedState ) }
Sebagai gantinya, Anda dapat membuat, mengekstrak, dan menggunakan kembali instance pengubah yang sama dan meneruskannya ke composable seperti ini:
// Now, the allocation of the modifier happens here: val reusableModifier = Modifier .padding(12.dp) .background(Color.Gray) @Composable fun LoadingWheelAnimation() { val animatedState = animateFloatAsState(/*...*/) LoadingWheel( // No allocation, as we're just reusing the same instance modifier = reusableModifier, animatedState = animatedState ) }
Mengekstrak dan menggunakan kembali pengubah tidak tercakup
Pengubah dapat tidak dicakupkan atau dicakupkan ke composable tertentu. Untuk pengubah tidak tercakup, Anda dapat dengan mudah mengekstraknya di luar composable sebagai variabel sederhana:
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp) @Composable fun AuthorField() { HeaderText( // ... modifier = reusableModifier ) SubtitleText( // ... modifier = reusableModifier ) }
Hal ini dapat bermanfaat terutama jika digabungkan dengan tata letak Lambat. Dalam kebanyakan kasus, Anda ingin semua jumlah item yang berpotensi signifikan memiliki pengubah yang sama persis:
val reusableItemModifier = Modifier .padding(bottom = 12.dp) .size(216.dp) .clip(CircleShape) @Composable private fun AuthorList(authors: List<Author>) { LazyColumn { items(authors) { AsyncImage( // ... modifier = reusableItemModifier, ) } } }
Mengekstrak dan menggunakan kembali pengubah tercakup
Saat menangani pengubah yang dicakupkan ke composable tertentu, Anda dapat mengekstraknya ke tingkat tertinggi dan menggunakan kembali jika sesuai:
Column(/*...*/) { val reusableItemModifier = Modifier .padding(bottom = 12.dp) // Align Modifier.Element requires a ColumnScope .align(Alignment.CenterHorizontally) .weight(1f) Text1( modifier = reusableItemModifier, // ... ) Text2( modifier = reusableItemModifier // ... ) // ... }
Anda hanya boleh meneruskan pengubah tercakup yang diekstrak ke turunan langsung dengan cakupan yang sama. Lihat bagian Keamanan cakupan di Compose untuk mengetahui referensi selengkapnya tentang alasan pentingnya hal ini:
Column(modifier = Modifier.fillMaxWidth()) { // Weight modifier is scoped to the Column composable val reusableItemModifier = Modifier.weight(1f) // Weight will be properly assigned here since this Text is a direct child of Column Text1( modifier = reusableItemModifier // ... ) Box { Text2( // Weight won't do anything here since the Text composable is not a direct child of Column modifier = reusableItemModifier // ... ) } }
Perantaian lebih lanjut dari pengubah yang diekstrak
Anda dapat membuat rantai atau menambahkan rantai pengubah yang diekstrak lebih lanjut dengan memanggil
fungsi .then():
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp) // Append to your reusableModifier reusableModifier.clickable { /*...*/ } // Append your reusableModifier otherModifier.then(reusableModifier)
Perhatikan bahwa urutan pengubah sangat penting!
Pelajari lebih lanjut
Kami menyediakan daftar lengkap pengubah, beserta parameter dan cakupannya.
Untuk mengetahui praktik lain tentang cara menggunakan pengubah, Anda juga dapat membuka codelab Tata letak dasar di Compose atau lihat Repositori Now di Android.
Untuk informasi selengkapnya tentang pengubah kustom dan cara membuatnya, lihat dokumentasi tentang Tata letak kustom - Menggunakan pengubah tata letak.
Direkomendasikan untuk Anda
- Catatan: teks link ditampilkan saat JavaScript nonaktif
- Dasar-dasar tata letak Compose
- Tindakan editor {:#editor-actions}
- Tata letak kustom {:#custom-layouts }