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.
padding
menempatkan ruang di sekitar elemen.fillMaxWidth
membuat 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 menetapkan 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
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 }