1. Sebelum memulai
Dalam codelab ini, Anda akan mempelajari cara menambahkan animasi sederhana ke aplikasi Android. Animasi dapat membuat aplikasi Anda lebih interaktif, menarik, dan mudah diinterpretasikan oleh pengguna. Menganimasikan setiap perubahan di layar yang penuh informasi dapat membantu pengguna melihat perubahan yang terjadi.
Ada banyak jenis animasi yang dapat digunakan di antarmuka pengguna aplikasi. Item dapat perlahan semakin jelas saat muncul dan perlahan memudar saat menghilang, dapat bergerak ke dalam atau keluar layar, atau dapat berubah dengan cara yang menarik. Hal ini membantu membuat UI aplikasi menjadi ekspresif dan mudah digunakan.
Animasi juga dapat membuat aplikasi Anda terlihat lebih menarik dengan tampilan dan nuansa elegan, serta membantu pengguna pada saat yang sama.
Prasyarat
- Pengetahuan tentang Kotlin, termasuk fungsi, lambda, dan composable stateless.
- Pengetahuan dasar tentang cara membuat tata letak di Jetpack Compose.
- Pengetahuan dasar tentang cara membuat daftar di Jetpack Compose.
- Pengetahuan dasar tentang Desain Material.
Yang akan Anda pelajari
- Cara membuat animasi pegas sederhana dengan Jetpack Compose.
Yang akan Anda build
- Anda akan mem-build aplikasi Woof dari codelab Tema Material dengan Jetpack Compose, dan menambahkan animasi sederhana untuk mengonfirmasi tindakan pengguna.
Yang Anda butuhkan
- Versi stabil terbaru Android Studio.
- Koneksi internet untuk mendownload kode awal.
2. Ringkasan Aplikasi
Dalam codelab Penerapan Tema Material dengan Jetpack Compose, Anda membuat aplikasi Woof menggunakan Desain Material yang menampilkan daftar anjing dan informasinya.
Dalam codelab ini, Anda akan menambahkan animasi ke aplikasi Woof. Anda akan menambahkan informasi hobi yang akan ditampilkan saat meluaskan item daftar. Anda juga akan menambahkan animasi pegas untuk menganimasikan item daftar yang diperluas.
Mendapatkan kode awal
Untuk memulai, download kode awal:
Atau, Anda dapat membuat clone repositori GitHub untuk kode tersebut:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-woof.git $ cd basic-android-kotlin-compose-training-woof $ git checkout material
Anda dapat menjelajahi kode di repositori GitHub Woof app
.
3. Menambahkan ikon luaskan
Di bagian ini, Anda akan menambahkan ikon Luaskan dan Ciutkan ke aplikasi.
Ikon
Ikon adalah simbol yang dapat membantu pengguna memahami antarmuka pengguna dengan menyampaikan fungsi yang diinginkan secara visual. Aplikasi ini sering kali mengambil inspirasi dari objek di dunia nyata yang dialami secara langsung oleh pengguna. Desain ikon sering kali mengurangi tingkat detail hingga jumlah minimum yang diperlukan agar mudah dikenali oleh pengguna. Misalnya, pensil dalam dunia nyata digunakan untuk menulis, sehingga ikon bandingannya biasanya menunjukkan buat atau edit.
Foto oleh Angelina Litvin di Unsplash |
Desain Material menyediakan sejumlah ikon yang diatur dalam kategori umum untuk sebagian besar kebutuhan Anda.
Menambahkan dependensi Gradle
Tambahkan dependensi library material-icons-extended
ke project Anda. Anda akan menggunakan ikon Icons.Filled.ExpandLess
dan Icons.Filled.ExpandMore
dari library ini.
- Di panel Project, buka Gradle Scripts > build.gradle.kts (Module :app).
- Scroll ke akhir file
build.gradle.kts (Module :app)
. Di blokdependencies{}
, tambahkan baris berikut:
implementation("androidx.compose.material:material-icons-extended")
Menambahkan composable ikon
Tambahkan fungsi untuk menampilkan ikon Luaskan dari library ikon Material dan gunakan sebagai tombol.
- Di
MainActivity.kt
, setelah fungsiDogItem()
, buat fungsi composable baru yang disebutDogItemButton()
. - Teruskan
Boolean
untuk status diperluas, ekspresi lambda untuk pengendali onClick tombol, danModifier
opsional sebagai berikut:
@Composable
private fun DogItemButton(
expanded: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
}
- Di dalam fungsi
DogItemButton()
, tambahkan composableIconButton()
yang menerima parameter bernamaonClick
, lambda menggunakan sintaksis lambda di akhir, yang dipanggil saat ikon ini ditekan danmodifier
opsional. TetapkanIconButton's onClick
danmodifier value parameters
sama dengan yang diteruskan keDogItemButton
.
@Composable
private fun DogItemButton(
expanded: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier
){
IconButton(
onClick = onClick,
modifier = modifier
) {
}
}
- Di dalam blok lambda
IconButton()
, tambahkan composableIcon
dan tetapkanimageVector value-parameter
keIcons.Filled.ExpandMore
. Inilah yang akan ditampilkan di akhir item daftar . Android Studio menampilkan peringatan untuk parameter composableIcon()
yang akan Anda perbaiki di langkah berikutnya.
import androidx.compose.material.icons.filled.ExpandMore
import androidx.compose.material.icons.Icons
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
IconButton(
onClick = onClick,
modifier = modifier
) {
Icon(
imageVector = Icons.Filled.ExpandMore
)
}
- Tambahkan parameter nilai
tint
, dan setel warna ikon keMaterialTheme.colorScheme.secondary
. Tambahkan parameter bernamacontentDescription
, dan setel ke resource stringR.string.expand_button_content_description
.
IconButton(
onClick = onClick,
modifier = modifier
){
Icon(
imageVector = Icons.Filled.ExpandMore,
contentDescription = stringResource(R.string.expand_button_content_description),
tint = MaterialTheme.colorScheme.secondary
)
}
Menampilkan ikon
Tampilkan composable DogItemButton()
dengan menambahkannya ke tata letak.
- Di awal
DogItem()
, tambahkanvar
untuk menyimpan status yang diluaskan dari item daftar. Setel nilai awal kefalse
.
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
var expanded by remember { mutableStateOf(false) }
- Tampilkan tombol ikon di dalam item daftar. Pada composable
DogItem()
, di akhir blokRow
, setelah panggilan keDogInformation()
, tambahkanDogItemButton()
. Teruskan statusexpanded
dan lambda kosong untuk callback. Anda akan menentukan tindakanonClick
di langkah berikutnya.
Row(
modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_small))
) {
DogIcon(dog.imageResourceId)
DogInformation(dog.name, dog.age)
DogItemButton(
expanded = expanded,
onClick = { /*TODO*/ }
)
}
- Lihat
WoofPreview()
di panel Design.
Perhatikan bahwa tombol luaskan tidak sejajar dengan bagian akhir item daftar. Anda akan memperbaikinya di langkah berikutnya.
Menyejajarkan tombol luaskan
Untuk menyejajarkan tombol luaskan dengan bagian akhir item daftar, Anda perlu menambahkan pengatur jarak dalam tata letak dengan atribut Modifier.weight()
.
Di aplikasi Woof, setiap baris item daftar berisi gambar anjing, informasi anjing, dan tombol luaskan. Anda akan menambahkan composable Spacer
sebelum tombol luaskan dengan bobot 1f
untuk menyejajarkan ikon tombol dengan benar. Karena pengatur jarak adalah satu-satunya elemen turunan berbobot di baris, pengatur jarak akan mengisi ruang yang tersisa di baris setelah mengukur lebar elemen turunan tanpa bobot lainnya.
Menambahkan pengatur jarak ke baris item daftar
- Di
DogItem()
, antaraDogInformation()
danDogItemButton()
, tambahkanSpacer
. TeruskanModifier
denganweight(1f)
.Modifier.weight()
menyebabkan pengatur jarak mengisi ruang yang tersisa di baris.
import androidx.compose.foundation.layout.Spacer
Row(
modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_small))
) {
DogIcon(dog.imageResourceId)
DogInformation(dog.name, dog.age)
Spacer(modifier = Modifier.weight(1f))
DogItemButton(
expanded = expanded,
onClick = { /*TODO*/ }
)
}
- Lihat
WoofPreview()
di panel Design. Perhatikan bahwa tombol luaskan kini sejajar dengan bagian akhir item daftar.
4. Menambahkan Composable untuk menampilkan hobi
Dalam tugas ini, Anda akan menambahkan composable Text
untuk menampilkan informasi hobi anjing.
- Buat fungsi composable baru yang disebut
DogHobby()
yang menggunakan ID resource string hobi anjing danModifier
opsional.
@Composable
fun DogHobby(
@StringRes dogHobby: Int,
modifier: Modifier = Modifier
) {
}
- Di dalam fungsi
DogHobby()
, buatColumn
dan teruskan pengubah yang diteruskan keDogHobby()
.
@Composable
fun DogHobby(
@StringRes dogHobby: Int,
modifier: Modifier = Modifier
){
Column(
modifier = modifier
) {
}
}
- Di dalam blok
Column
, tambahkan dua composableText
. Satu untuk menampilkan teks About di atas informasi hobi, dan satu lagi untuk menampilkan informasi hobi.
Setel text
dari composable yang pertama ke about
dari file strings.xml dan tetapkan style
sebagai labelSmall
. Tetapkan text
dari composable yang kedua ke dogHobby
yang diteruskan dan setel style
ke bodyLarge
.
Column(
modifier = modifier
) {
Text(
text = stringResource(R.string.about),
style = MaterialTheme.typography.labelSmall
)
Text(
text = stringResource(dogHobby),
style = MaterialTheme.typography.bodyLarge
)
}
- Di
DogItem()
, composableDogHobby()
akan berada di bawahRow
yang berisiDogIcon()
,DogInformation()
,Spacer()
, danDogItemButton()
. Untuk melakukannya, gabungkanRow
denganColumn
sehingga hobi dapat ditambahkan di bawahRow
.
Column() {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_small))
) {
DogIcon(dog.imageResourceId)
DogInformation(dog.name, dog.age)
Spacer(modifier = Modifier.weight(1f))
DogItemButton(
expanded = expanded,
onClick = { /*TODO*/ }
)
}
}
- Tambahkan
DogHobby()
setelahRow
sebagai turunan kedua dariColumn
. Teruskandog.hobbies
yang berisi hobi unik anjing yang diteruskan danmodifier
dengan padding untuk composableDogHobby()
.
Column() {
Row() {
...
}
DogHobby(
dog.hobbies,
modifier = Modifier.padding(
start = dimensionResource(R.dimen.padding_medium),
top = dimensionResource(R.dimen.padding_small),
end = dimensionResource(R.dimen.padding_medium),
bottom = dimensionResource(R.dimen.padding_medium)
)
)
}
Fungsi DogItem()
lengkap akan terlihat seperti ini:
@Composable
fun DogItem(
dog: Dog,
modifier: Modifier = Modifier
) {
var expanded by remember { mutableStateOf(false) }
Card(
modifier = modifier
) {
Column() {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_small))
) {
DogIcon(dog.imageResourceId)
DogInformation(dog.name, dog.age)
Spacer(Modifier.weight(1f))
DogItemButton(
expanded = expanded,
onClick = { /*TODO*/ },
)
}
DogHobby(
dog.hobbies,
modifier = Modifier.padding(
start = dimensionResource(R.dimen.padding_medium),
top = dimensionResource(R.dimen.padding_small),
end = dimensionResource(R.dimen.padding_medium),
bottom = dimensionResource(R.dimen.padding_medium)
)
)
}
}
}
- Lihat
WoofPreview()
di panel Design. Perhatikan hobi anjing yang ditampilkan.
5. Menampilkan atau menyembunyikan hobi saat tombol diklik
Aplikasi Anda memiliki tombol Luaskan untuk setiap item daftar, namun belum ada tindakan apa pun. Di bagian ini, Anda akan menambahkan opsi untuk menyembunyikan atau menampilkan informasi hobi saat pengguna mengklik tombol luaskan.
- Pada fungsi composable
DogItem()
, dalam panggilan fungsiDogItemButton()
, tentukan ekspresi lambdaonClick()
, ubah nilai status booleanexpanded
menjaditrue
saat tombol diklik, dan ubah kembali kefalse
jika tombol diklik lagi.
DogItemButton(
expanded = expanded,
onClick = { expanded = !expanded }
)
- Di fungsi
DogItem()
, gabungkan panggilan fungsiDogHobby()
dengan pemeriksaanif
pada booleanexpanded
.
@Composable
fun DogItem(
dog: Dog,
modifier: Modifier = Modifier
) {
var expanded by remember { mutableStateOf(false) }
Card(
...
) {
Column(
...
) {
Row(
...
) {
...
}
if (expanded) {
DogHobby(
dog.hobbies, modifier = Modifier.padding(
start = dimensionResource(R.dimen.padding_medium),
top = dimensionResource(R.dimen.padding_small),
end = dimensionResource(R.dimen.padding_medium),
bottom = dimensionResource(R.dimen.padding_medium)
)
)
}
}
}
}
Sekarang, informasi hobi anjing hanya ditampilkan jika nilai expanded
adalah true
.
- Pratinjau dapat menunjukkan tampilan UI, dan Anda juga dapat berinteraksi dengannya. Untuk berinteraksi dengan pratinjau UI, arahkan kursor ke atas teks WoofPreview di Panel Design, lalu klik tombol Interactive Mode di sudut kanan atas panel Design. Tombol ini akan memulai pratinjau dalam mode interaktif.
- Berinteraksilah dengan pratinjau dengan mengklik tombol luaskan. Perhatikan bahwa informasi hobi anjing disembunyikan dan ditampilkan saat Anda mengklik tombol luaskan.
Perhatikan bahwa ikon tombol luaskan tetap sama saat item daftar diluaskan. Untuk pengalaman pengguna yang lebih baik, ubahlah ikon sehingga ExpandMore
menampilkan panah bawah , dan ExpandLess
menampilkan panah atas .
- Dalam fungsi
DogItemButton()
, tambahkan pernyataanif
yang memperbarui nilaiimageVector
berdasarkan statusexpanded
sebagai berikut:
import androidx.compose.material.icons.filled.ExpandLess
@Composable
private fun DogItemButton(
...
) {
IconButton(onClick = onClick) {
Icon(
imageVector = if (expanded) Icons.Filled.ExpandLess else Icons.Filled.ExpandMore,
...
)
}
}
Perhatikan cara Anda menulis if-else
di cuplikan kode sebelumnya.
if (expanded) Icons.Filled.ExpandLess else Icons.Filled.ExpandMore
Hal ini sama dengan menggunakan tanda kurung kurawal { } dalam kode berikut:
if (expanded) {
`Icons.Filled.ExpandLess`
} else {
`Icons.Filled.ExpandMore`
}
Tanda kurung kurawal bersifat opsional jika ada satu baris kode untuk pernyataan if
-else
.
- Jalankan aplikasi di perangkat atau emulator, atau gunakan mode interaktif lagi dalam pratinjau. Perhatikan bahwa ikon berganti-ganti antara ikon
ExpandMore
danExpandLess
.
Bagus, Anda telah memperbarui ikon!
Saat memperluas item daftar, apakah Anda menyadari bahwa tingginya tiba-tiba berubah? Perubahan tinggi yang tiba-tiba tentu membuat aplikasi tidak menarik. Untuk mengatasinya, tambahkan animasi ke aplikasi seperti yang akan dijelaskan di bagian berikutnya.
6. Menambahkan animasi
Animasi dapat menambahkan petunjuk visual yang memberi tahu pengguna tentang apa yang terjadi di aplikasi Anda. Fitur ini berguna terutama saat status UI berubah, misalnya saat konten baru dimuat atau tindakan baru tersedia. Animasi juga dapat membuat aplikasi Anda tampil lebih menarik.
Pada bagian ini, Anda akan menambahkan animasi pegas untuk menganimasikan perubahan tinggi item daftar.
Animasi Pegas
Animasi pegas adalah animasi berbasis fisika yang digerakkan oleh gaya pegas. Dengan animasi pegas, nilai dan kecepatan gerakan dihitung berdasarkan gaya pegas yang diterapkan.
Misalnya, jika Anda menarik ikon aplikasi mengitari layar lalu melepaskannya dengan mengangkat jari, ikon akan kembali ke lokasi semula dengan gaya yang tak terlihat.
Animasi berikut menunjukkan efek pegas. Setelah jari dilepaskan dari ikon, ikon akan kembali ke posisi semula, yang meniru pegas.
Efek pegas
Gaya pegas digerakkan oleh dua properti berikut:
- Rasio redaman: Kelenturan pegas.
- Tingkat kekakuan: Kekakuan pegas, yaitu seberapa cepat pegas bergerak menuju ujungnya.
Berikut adalah beberapa contoh animasi dengan berbagai rasio redaman dan tingkat kekakuan.
Pantulan Tinggi | Tanpa Pantulan |
Kekakuan Tinggi | Kekakuan Sangat Rendah |
Lihat panggilan fungsi DogHobby()
dalam fungsi composable DogItem()
. Informasi hobi anjing disertakan dalam komposisi, berdasarkan nilai boolean expanded
. Tinggi item daftar berubah, bergantung pada apakah informasi hobi terlihat atau tersembunyi. Saat ini, transisi tersebut belum berjalan lancar. Di bagian ini, Anda akan menggunakan pengubah animateContentSize
untuk menambahkan transisi yang lebih lancar antara status yang diperluas dan tidak diperluas.
// No need to copy over
@Composable
fun DogItem(...) {
...
if (expanded) {
DogHobby(
dog.hobbies,
modifier = Modifier.padding(
start = dimensionResource(R.dimen.padding_medium),
top = dimensionResource(R.dimen.padding_small),
end = dimensionResource(R.dimen.padding_medium),
bottom = dimensionResource(R.dimen.padding_medium)
)
)
}
}
- Di
MainActivity.kt
, diDogItem()
, tambahkan parametermodifier
ke tata letakColumn
.
@Composable
fun DogItem(
dog: Dog,
modifier: Modifier = Modifier
) {
...
Card(
...
) {
Column(
modifier = Modifier
){
...
}
}
}
- Buat rantai pengubah dengan pengubah
animateContentSize
untuk menganimasikan perubahan ukuran (tinggi item daftar).
import androidx.compose.animation.animateContentSize
Column(
modifier = Modifier
.animateContentSize()
)
Dalam implementasi saat ini, Anda menganimasikan tinggi item daftar di aplikasi Anda. Namun, animasinya sangat halus sehingga sulit untuk dilihat saat Anda menjalankan aplikasi. Untuk mengatasi hal ini, gunakan parameter animationSpec
opsional yang memungkinkan Anda menyesuaikan animasi.
- Untuk Woof, animasi akan mudah masuk dan keluar tanpa pantulan. Untuk mencapainya, tambahkan parameter
animationSpec
ke panggilan fungsianimateContentSize()
. Setel ke animasi pegas denganDampingRatioNoBouncy
sehingga tidak ada pantulan dan parameterStiffnessMedium
untuk membuat pegas sedikit kaku.
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
Column(
modifier = Modifier
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioNoBouncy,
stiffness = Spring.StiffnessMedium
)
)
)
- Lihat
WoofPreview()
di panel Design, dan gunakan mode interaktif atau jalankan aplikasi di emulator atau perangkat untuk melihat cara kerja animasi pegas.
Anda berhasil! Nikmati aplikasi yang menarik dengan animasi.
7. (Opsional) Bereksperimen dengan animasi lain
animate*AsState
Fungsi animate*AsState()
adalah salah satu Animation API yang paling sederhana di Compose untuk menganimasikan satu nilai. Anda hanya memberikan nilai akhir (atau nilai target), dan API akan memulai animasi dari nilai saat ini ke nilai akhir yang ditentukan.
Compose menyediakan fungsi animate*AsState()
untuk Float
, Color
, Dp
, Size
, Offset
, dan Int
, sebagai contoh. Anda dapat dengan mudah menambahkan dukungan untuk jenis data lainnya menggunakan animateValueAsState()
yang menggunakan jenis umum.
Coba gunakan fungsi animateColorAsState()
untuk mengubah warna saat item daftar diluaskan.
- Dalam
DogItem()
, deklarasikan warna dan delegasikan inisialisasinya ke fungsianimateColorAsState()
.
import androidx.compose.animation.animateColorAsState
@Composable
fun DogItem(
dog: Dog,
modifier: Modifier = Modifier
) {
var expanded by remember { mutableStateOf(false) }
val color by animateColorAsState()
...
}
- Setel parameter bernama
targetValue
, bergantung pada nilai booleanexpanded
. Jika item daftar diluaskan, tetapkan item daftar ke warnatertiaryContainer
. Selain itu, tetapkan ke warnaprimaryContainer
.
import androidx.compose.animation.animateColorAsState
@Composable
fun DogItem(
dog: Dog,
modifier: Modifier = Modifier
) {
var expanded by remember { mutableStateOf(false) }
val color by animateColorAsState(
targetValue = if (expanded) MaterialTheme.colorScheme.tertiaryContainer
else MaterialTheme.colorScheme.primaryContainer,
)
...
}
- Setel
color
sebagai pengubah latar belakang keColumn
.
@Composable
fun DogItem(
dog: Dog,
modifier: Modifier = Modifier
) {
...
Card(
...
) {
Column(
modifier = Modifier
.animateContentSize(
...
)
)
.background(color = color)
) {...}
}
- Lihat bagaimana warna berubah saat item daftar diluaskan. Item daftar yang tidak diluaskan memiliki warna
primaryContainer
dan item daftar yang diluaskan berwarnatertiaryContainer
.
8. Mendapatkan kode solusi
Guna mendownload kode untuk codelab yang sudah selesai, Anda dapat menggunakan perintah git ini:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-woof.git
Atau, Anda dapat mendownload repositori sebagai file ZIP, lalu mengekstraknya, dan membukanya di Android Studio.
Jika Anda ingin melihat kode solusi, lihat di GitHub.
9. Kesimpulan
Selamat! Anda telah menambahkan tombol untuk menyembunyikan dan menampilkan informasi tentang anjing tersebut. Anda telah meningkatkan pengalaman pengguna menggunakan animasi pegas. Anda juga telah mempelajari cara menggunakan mode interaktif di panel Design.
Anda juga dapat mencoba berbagai jenis Animasi Jetpack Compose. Jangan lupa untuk membagikan karya Anda di media sosial dengan #AndroidBasics.