Pelajaran 1: Fungsi composable
Jetpack Compose dibuat berdasarkan fungsi composable. Fungsi ini memungkinkan Anda menentukan
UI aplikasi Anda secara terprogram dengan menjelaskan tampilan dan penyediaan dependensi data,
bukan berfokus pada proses konstruksi UI (memulai elemen,
melampirkannya ke induk, dll.). Untuk membuat fungsi composable, cukup tambahkan
anotasi @Composable
ke nama fungsi.
Menambahkan elemen teks
Untuk memulai, unduh versi terbaru Android Studio, lalu buat aplikasi dengan memilih New Project, dan di bagian Kategori Ponsel dan Tablet, pilih Aktivitas Kosong. Beri nama aplikasi ComposeTutorial, lalu klik Finish. Template default sudah berisi beberapa elemen Compose, tetapi dalam tutorial ini Anda akan mem-build elemen langkah demi langkah.
Pertama, tampilkan teks “Halo dunia!” dengan menambahkan elemen teks di dalam
metode onCreate
. Anda dapat melakukannya dengan menentukan blok
konten, dan memanggil
fungsi composable Text
. Blok
setContent
menentukan tata letak aktivitas tempat
fungsi composable dipanggil. Fungsi composable hanya dapat dipanggil dari fungsi
composable lain.
Jetpack Compose menggunakan plugin compiler Kotlin untuk mengubah fungsi composable ini
menjadi elemen UI aplikasi. Misalnya, fungsi composable Text
yang
ditetapkan oleh library Compose UI akan menampilkan label teks di layar.
import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.material3.Text class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Text("Hello world!") } } }
Menentukan fungsi composable
Untuk membuat fungsi yang dapat dikomposisi, tambahkan anotasi @Composable
.
Untuk mencoba ini, tentukan fungsi MessageCard
yang
mendapatkan nama dan menggunakan nama tersebut untuk mengonfigurasi elemen teks.
// ... import androidx.compose.runtime.Composable class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MessageCard("Android") } } } @Composable fun MessageCard(name: String) { Text(text = "Hello $name!") }
Melihat pratinjau fungsi di Android Studio
Anotasi @Preview
memungkinkan Anda melihat pratinjau fungsi composable dalam Android
Studio tanpa harus mem-build dan menginstal aplikasi ke perangkat Android atau emulator. Anotasi
ini harus digunakan pada fungsi composable yang tidak menggunakan parameter. Karena
alasan ini, Anda tidak dapat melihat pratinjau fungsi MessageCard
secara langsung. Sebagai gantinya, buat fungsi kedua bernama
PreviewMessageCard
, yang memanggil
MessageCard
dengan parameter yang sesuai. Tambahkan
anotasi @Preview
sebelum
@Composable
.
// ... import androidx.compose.ui.tooling.preview.Preview @Composable fun MessageCard(name: String) { Text(text = "Hello $name!") } @Preview @Composable fun PreviewMessageCard() { MessageCard("Android") }
Membuat ulang project Anda. Aplikasi itu sendiri tidak berubah, karena fungsi
PreviewMessageCard
baru tidak dipanggil di mana pun,
tetapi Android Studio menambahkan jendela pratinjau yang dapat Anda luaskan dengan mengklik tampilan
(desain/kode) terpisah. Jendela ini menampilkan pratinjau elemen UI yang dibuat oleh fungsi
composable yang ditandai dengan anotasi @Preview
. Untuk memperbarui
pratinjau kapan pun, klik tombol muat ulang di bagian atas jendela pratinjau.
import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.material3.Text class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Text("Hello world!") } } }
// ... import androidx.compose.runtime.Composable class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MessageCard("Android") } } } @Composable fun MessageCard(name: String) { Text(text = "Hello $name!") }
// ... import androidx.compose.ui.tooling.preview.Preview @Composable fun MessageCard(name: String) { Text(text = "Hello $name!") } @Preview @Composable fun PreviewMessageCard() { MessageCard("Android") }
Tutorial 2: Tata letak
Elemen UI bersifat hierarkis, dengan elemen satu berada di dalam elemen lainnya. Di Compose, Anda mem-build hierarki UI dengan memanggil fungsi composable dari fungsi composable lainnya.
Menambahkan beberapa teks
Sejauh ini, Anda telah mem-build fungsi composable dan pratinjau pertama. Untuk menemukan kemampuan Jetpack Compose lainnya, Anda akan mem-build layar pesan sederhana yang berisi daftar pesan yang dapat diperluas dengan beberapa animasi.
Mulailah dengan melengkapi composable pesan, dengan menampilkan nama penulis dan
konten pesannya. Anda harus mengubah parameter composable terlebih dahulu untuk menerima objek
Message
sebagai ganti
String
, dan menambahkan composable
Text
lain di dalam composable
MessageCard
. Pastikan Anda juga memperbarui
pratinjau.
// ... class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MessageCard(Message("Android", "Jetpack Compose")) } } } data class Message(val author: String, val body: String) @Composable fun MessageCard(msg: Message) { Text(text = msg.author) Text(text = msg.body) } @Preview @Composable fun PreviewMessageCard() { MessageCard( msg = Message("Lexi", "Hey, take a look at Jetpack Compose, it's great!") ) }
Kode ini membuat dua elemen teks di dalam tampilan konten. Namun, karena informasi tentang cara mengaturnya belum disediakan, elemen teks tersebut digambar di atas satu sama lain, sehingga membuat teks tidak dapat dibaca.
Menggunakan Kolom
// ... import androidx.compose.foundation.layout.Column @Composable fun MessageCard(msg: Message) { Column { Text(text = msg.author) Text(text = msg.body) } }
Menambahkan elemen gambar
Lengkapi kartu pesan Anda dengan menambahkan foto profil pengirim. Gunakan
Resource Manager
untuk mengimpor gambar dari galeri foto Anda atau gunakan yang ini. Tambahkan
composable Row
agar memiliki desain yang terstruktur dengan baik dan
composable
Image
di dalamnya.
// ... import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Row import androidx.compose.ui.res.painterResource @Composable fun MessageCard(msg: Message) { Row { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = "Contact profile picture", ) Column { Text(text = msg.author) Text(text = msg.body) } } }
Mengonfigurasi tata letak Anda
Tata letak pesan Anda memiliki struktur yang tepat, tetapi elemennya tidak diberi spasi dengan baik dan gambarnya terlalu besar. Untuk mendekorasi atau mengonfigurasi composable, Compose menggunakan pengubah. Modifier memungkinkan Anda mengubah ukuran, tata letak, tampilan, atau interaksi tingkat tinggi composable, seperti membuat elemen menjadi dapat diklik. Anda dapat merangkainya untuk membuat composable yang lebih lengkap. Anda akan menggunakan beberapa di antaranya untuk membuat tata letak lebih baik.
// ... import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp @Composable fun MessageCard(msg: Message) { // Add padding around our message Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = "Contact profile picture", modifier = Modifier // Set image size to 40 dp .size(40.dp) // Clip image to be shaped as a circle .clip(CircleShape) ) // Add a horizontal space between the image and the column Spacer(modifier = Modifier.width(8.dp)) Column { Text(text = msg.author) // Add a vertical space between the author and message texts Spacer(modifier = Modifier.height(4.dp)) Text(text = msg.body) } } }
// ... class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MessageCard(Message("Android", "Jetpack Compose")) } } } data class Message(val author: String, val body: String) @Composable fun MessageCard(msg: Message) { Text(text = msg.author) Text(text = msg.body) } @Preview @Composable fun PreviewMessageCard() { MessageCard( msg = Message("Lexi", "Hey, take a look at Jetpack Compose, it's great!") ) }
// ... import androidx.compose.foundation.layout.Column @Composable fun MessageCard(msg: Message) { Column { Text(text = msg.author) Text(text = msg.body) } }
// ... import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Row import androidx.compose.ui.res.painterResource @Composable fun MessageCard(msg: Message) { Row { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = "Contact profile picture", ) Column { Text(text = msg.author) Text(text = msg.body) } } }
// ... import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp @Composable fun MessageCard(msg: Message) { // Add padding around our message Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = "Contact profile picture", modifier = Modifier // Set image size to 40 dp .size(40.dp) // Clip image to be shaped as a circle .clip(CircleShape) ) // Add a horizontal space between the image and the column Spacer(modifier = Modifier.width(8.dp)) Column { Text(text = msg.author) // Add a vertical space between the author and message texts Spacer(modifier = Modifier.height(4.dp)) Text(text = msg.body) } } }
Pelajaran 3: Desain Material
Compose dibuat untuk mendukung prinsip Desain Material. Banyak elemen UI-nya mengimplementasikan Desain Material secara unik. Dalam pelajaran ini, Anda akan menyesuaikan aplikasi dengan widget Desain Material.
Menggunakan Desain Material
Desain pesan Anda sekarang memiliki tata letak, tetapi belum terlihat bagus.
Jetpack Compose menyediakan implementasi Desain Material 3 dan elemen UI-nya
sejak awal. Anda akan meningkatkan tampilan composable MessageCard
menggunakan gaya Desain Material.
Untuk memulai, gabungkan fungsi MessageCard
dengan
tema Material yang dibuat di project Anda, ComposeTutorialTheme
,
serta Surface
.
Lakukan keduanya di @Preview
dan dalam
fungsi setContent
. Dengan melakukannya, composable dapat
mewarisi gaya seperti yang ditentukan dalam tema aplikasi untuk memastikan konsistensi di seluruh aplikasi.
Desain Material di-build berdasarkan tiga pilar: Color
,
Typography
, dan Shape
.
Anda akan menambahkannya satu per satu.
Catatan: Template Empty Compose Activity menghasilkan tema default untuk project Anda,
yang memungkinkan Anda menyesuaikan
MaterialTheme
.
Jika Anda memberi project nama yang berbeda dengan
ComposeTutorial, Anda dapat menemukan tema kustom di
file Theme.kt
dalam
sub-paket ui.theme
.
// ... class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ComposeTutorialTheme { Surface(modifier = Modifier.fillMaxSize()) { MessageCard(Message("Android", "Jetpack Compose")) } } } } } @Preview @Composable fun PreviewMessageCard() { ComposeTutorialTheme { Surface { MessageCard( msg = Message("Lexi", "Take a look at Jetpack Compose, it's great!") ) } } }
Warna
Gunakan MaterialTheme.colorScheme
untuk memberi gaya dengan warna dari
tema gabungan. Anda dapat menggunakan nilai-nilai ini dari tema di mana pun warna diperlukan. Contoh ini menggunakan warna tema dinamis (yang ditentukan oleh preferensi perangkat).
Anda dapat menetapkan dynamicColor
ke false
dalam file MaterialTheme.kt
untuk mengubahnya.
Beri gaya pada judul dan tambahkan bingkai ke gambar.
// ... import androidx.compose.foundation.border import androidx.compose.material3.MaterialTheme @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary ) Spacer(modifier = Modifier.height(4.dp)) Text(text = msg.body) } } }
Tipografi
Gaya Tipografi Material tersedia dalam MaterialTheme
,
cukup tambahkan ke composable Text
.
// ... @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.titleSmall ) Spacer(modifier = Modifier.height(4.dp)) Text( text = msg.body, style = MaterialTheme.typography.bodyMedium ) } } }
Bentuk
Dengan Shape
, Anda dapat menambahkan sentuhan akhir. Pertama, gabungkan
teks isi pesan di sekitar
composable
Surface
. Dengan melakukannya, Anda dapat menyesuaikan
bentuk dan ketinggian isi pesan. Padding juga ditambahkan ke pesan untuk tata letak yang lebih baik.
// ... import androidx.compose.material3.Surface @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.titleSmall ) Spacer(modifier = Modifier.height(4.dp)) Surface(shape = MaterialTheme.shapes.medium, shadowElevation = 1.dp) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), style = MaterialTheme.typography.bodyMedium ) } } } }
Mengaktifkan tema gelap
Tema gelap (atau mode malam) dapat diaktifkan untuk menghindari tampilan terang, khususnya pada malam hari, atau hanya untuk menghemat baterai perangkat. Berkat dukungan Desain Material, Jetpack Compose dapat menangani tema gelap secara default. Setelah menggunakan warna Desain Material, teks dan latar belakang akan otomatis menyesuaikan dengan latar belakang gelap.
Anda dapat membuat beberapa pratinjau dalam file sebagai fungsi terpisah, atau menambahkan beberapa anotasi ke fungsi yang sama.
Menambahkan anotasi pratinjau baru dan mengaktifkan mode malam.
// ... import android.content.res.Configuration @Preview(name = "Light Mode") @Preview( uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true, name = "Dark Mode" ) @Composable fun PreviewMessageCard() { ComposeTutorialTheme { Surface { MessageCard( msg = Message("Lexi", "Hey, take a look at Jetpack Compose, it's great!") ) } } }
Pilihan warna untuk tema terang dan gelap ditentukan dalam file Theme.kt
yang dihasilkan IDE.
Sejauh ini, Anda telah membuat elemen UI pesan yang menampilkan gambar dan dua teks dengan gaya berbeda, serta terlihat bagus dalam tema terang dan gelap.
// ... import android.content.res.Configuration @Preview(name = "Light Mode") @Preview( uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true, name = "Dark Mode" ) @Composable fun PreviewMessageCard() { ComposeTutorialTheme { Surface { MessageCard( msg = Message("Lexi", "Hey, take a look at Jetpack Compose, it's great!") ) } } }
// ... class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ComposeTutorialTheme { Surface(modifier = Modifier.fillMaxSize()) { MessageCard(Message("Android", "Jetpack Compose")) } } } } } @Preview @Composable fun PreviewMessageCard() { ComposeTutorialTheme { Surface { MessageCard( msg = Message("Lexi", "Take a look at Jetpack Compose, it's great!") ) } } }
// ... import androidx.compose.foundation.border import androidx.compose.material3.MaterialTheme @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary ) Spacer(modifier = Modifier.height(4.dp)) Text(text = msg.body) } } }
// ... @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.titleSmall ) Spacer(modifier = Modifier.height(4.dp)) Text( text = msg.body, style = MaterialTheme.typography.bodyMedium ) } } }
// ... import androidx.compose.material3.Surface @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.titleSmall ) Spacer(modifier = Modifier.height(4.dp)) Surface(shape = MaterialTheme.shapes.medium, shadowElevation = 1.dp) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), style = MaterialTheme.typography.bodyMedium ) } } } }
// ... import android.content.res.Configuration @Preview(name = "Light Mode") @Preview( uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true, name = "Dark Mode" ) @Composable fun PreviewMessageCard() { ComposeTutorialTheme { Surface { MessageCard( msg = Message("Lexi", "Hey, take a look at Jetpack Compose, it's great!") ) } } }
Modul 4: Daftar dan animasi
Daftar dan animasi ada di mana saja dalam aplikasi. Dalam modul ini, Anda akan mempelajari bagaimana Compose memudahkan pembuatan daftar dan menyenangkan untuk menambahkan animasi.
Membuat daftar pesan
Chat dengan satu pesan terasa agak sepi, jadi kita akan mengubah percakapan menjadi memiliki lebih dari
satu pesan. Anda harus membuat fungsi Conversation
yang akan menampilkan beberapa pesan. Untuk kasus penggunaan ini, gunakan
LazyColumn
dan
LazyRow
Compose. Composable ini hanya merender elemen
yang terlihat di layar, sehingga composable tersebut didesain agar menjadi sangat efisien untuk daftar yang panjang.
Dalam cuplikan kode ini, Anda dapat melihat bahwa LazyColumn
memiliki
turunan items
. Perlu List
sebagai parameter dan lambda-nya
menerima parameter yang telah kita beri nama message
(kita dapat
menamainya sesuai keinginan) yang merupakan instance Message
.
Singkatnya, lambda ini dipanggil untuk setiap item dari
List
yang disediakan. Salin
sampel set data
ke dalam project Anda untuk membantu mem-bootstrap percakapan dengan cepat.
// ... import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @Composable fun Conversation(messages: List<Message>) { LazyColumn { items(messages) { message -> MessageCard(message) } } } @Preview @Composable fun PreviewConversation() { ComposeTutorialTheme { Conversation(SampleData.conversationSample) } }
Menganimasikan pesan saat meluaskan
Percakapan menjadi lebih menarik. Saatnya berkreasi dengan animasi! Anda akan menambahkan
kemampuan untuk meluaskan pesan agar menampilkan pesan yang lebih panjang, yang menganimasikan ukuran konten dan
warna latar belakang. Untuk menyimpan status UI lokal ini, Anda perlu memantau apakah pesan telah
diluaskan atau tidak. Untuk memantau perubahan status ini, Anda harus menggunakan fungsi
remember
dan
mutableStateOf
.
Fungsi composable dapat menyimpan status lokal dalam memori menggunakan
remember
, dan memantau perubahan pada nilai yang diteruskan ke
mutableStateOf
. Composable (dan turunannya) yang menggunakan
status ini akan digambar ulang secara otomatis saat nilai diperbarui. Ini disebut
rekomposisi.
Dengan menggunakan API status Compose seperti remember
dan
mutableStateOf
, perubahan apa pun pada status akan otomatis memperbarui UI.
Catatan: Anda harus menambahkan impor berikut untuk menggunakan metode
sintaksis properti yang didelegasikan (kata kunci by
). Alt+Enter atau Option+Enter akan menambahkannya
keamanan untuk Anda.
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
// ... import androidx.compose.foundation.clickable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ComposeTutorialTheme { Conversation(SampleData.conversationSample) } } } } @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) // We keep track if the message is expanded or not in this // variable var isExpanded by remember { mutableStateOf(false) } // We toggle the isExpanded variable when we click on this Column Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.titleSmall ) Spacer(modifier = Modifier.height(4.dp)) Surface( shape = MaterialTheme.shapes.medium, shadowElevation = 1.dp, ) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), // If the message is expanded, we display all its content // otherwise we only display the first line maxLines = if (isExpanded) Int.MAX_VALUE else 1, style = MaterialTheme.typography.bodyMedium ) } } } }
Sekarang Anda dapat mengubah latar belakang konten pesan berdasarkan
isExpanded
saat kita mengklik pesan. Anda akan menggunakan
pengubah clickable
untuk menangani peristiwa klik pada
composable. Daripada hanya mengganti warna latar belakang
Surface
, Anda akan menganimasikan warna latar belakang dengan
mengubah nilainya secara bertahap dari
MaterialTheme.colorScheme.surface
ke
MaterialTheme.colorScheme.primary
, dan sebaliknya. Untuk melakukannya,
Anda akan menggunakan fungsi animateColorAsState
. Terakhir, Anda
akan menggunakan pengubah animateContentSize
untuk menganimasikan
ukuran penampung pesan dengan lancar:
// ... import androidx.compose.animation.animateColorAsState import androidx.compose.animation.animateContentSize @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.secondary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) // We keep track if the message is expanded or not in this // variable var isExpanded by remember { mutableStateOf(false) } // surfaceColor will be updated gradually from one color to the other val surfaceColor by animateColorAsState( if (isExpanded) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface, ) // We toggle the isExpanded variable when we click on this Column Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.titleSmall ) Spacer(modifier = Modifier.height(4.dp)) Surface( shape = MaterialTheme.shapes.medium, shadowElevation = 1.dp, // surfaceColor color will be changing gradually from primary to surface color = surfaceColor, // animateContentSize will change the Surface size gradually modifier = Modifier.animateContentSize().padding(1.dp) ) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), // If the message is expanded, we display all its content // otherwise we only display the first line maxLines = if (isExpanded) Int.MAX_VALUE else 1, style = MaterialTheme.typography.bodyMedium ) } } } }
// ... import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @Composable fun Conversation(messages: List<Message>) { LazyColumn { items(messages) { message -> MessageCard(message) } } } @Preview @Composable fun PreviewConversation() { ComposeTutorialTheme { Conversation(SampleData.conversationSample) } }
// ... import androidx.compose.foundation.clickable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ComposeTutorialTheme { Conversation(SampleData.conversationSample) } } } } @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) // We keep track if the message is expanded or not in this // variable var isExpanded by remember { mutableStateOf(false) } // We toggle the isExpanded variable when we click on this Column Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.titleSmall ) Spacer(modifier = Modifier.height(4.dp)) Surface( shape = MaterialTheme.shapes.medium, shadowElevation = 1.dp, ) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), // If the message is expanded, we display all its content // otherwise we only display the first line maxLines = if (isExpanded) Int.MAX_VALUE else 1, style = MaterialTheme.typography.bodyMedium ) } } } }
// ... import androidx.compose.animation.animateColorAsState import androidx.compose.animation.animateContentSize @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.secondary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) // We keep track if the message is expanded or not in this // variable var isExpanded by remember { mutableStateOf(false) } // surfaceColor will be updated gradually from one color to the other val surfaceColor by animateColorAsState( if (isExpanded) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface, ) // We toggle the isExpanded variable when we click on this Column Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.titleSmall ) Spacer(modifier = Modifier.height(4.dp)) Surface( shape = MaterialTheme.shapes.medium, shadowElevation = 1.dp, // surfaceColor color will be changing gradually from primary to surface color = surfaceColor, // animateContentSize will change the Surface size gradually modifier = Modifier.animateContentSize().padding(1.dp) ) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), // If the message is expanded, we display all its content // otherwise we only display the first line maxLines = if (isExpanded) Int.MAX_VALUE else 1, style = MaterialTheme.typography.bodyMedium ) } } } }
Langkah berikutnya
Selamat, Anda telah menyelesaikan tutorial Compose! Anda telah membuat layar chat sederhana yang secara efisien menampilkan daftar pesan yang dapat diperluas & animasi yang berisi gambar dan teks, yang didesain menggunakan prinsip Desain Material dengan menyertakan tema gelap dan pratinjau—semuanya kurang dari 100 baris kode!
Berikut yang telah Anda pelajari sejauh ini:
- Menentukan fungsi composable
- Menambahkan berbagai elemen di composable Anda
- Membuat struktur komponen UI menggunakan composable tata letak
- Memperluas composable dengan menggunakan pengubah
- Membuat daftar yang efisien
- Melacak status dan mengubahnya
- Menambahkan interaksi pengguna pada composable
- Menganimasikan pesan saat memperluasnya
Jika Anda ingin menggali lebih dalam beberapa langkah ini, jelajahi referensi di bawah.