Animasi Sederhana dengan Jetpack Compose

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 berangsur jelas dan memudar saat muncul atau 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:

Animasi yang memberikan reward kepada pengguna untuk suatu tugas dapat membuat momen penting dalam perjalanan pengguna menjadi lebih bermakna.

Elemen animasi yang merespons input keypad menyediakan masukan untuk ditampilkan jika tindakan berhasil.

Animasi item daftar adalah placeholder yang menunjukkan bahwa konten sedang dimuat.

Animasi tindakan geser untuk membuka akan mengajak dan mendorong pengguna melakukan gestur yang diperlukan.

Animasi ikon dapat melengkapi atau menambahkan arti ikon dengan cara yang menarik.

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 terbaru Android Studio.
  • Koneksi internet untuk mendownload kode awal.

2. Ringkasan Aplikasi

Dalam codelab Tema Material dengan Jetpack Compose, Anda membuat aplikasi Woof menggunakan Desain Material, yang menampilkan daftar anjing dan informasinya.

7252aa244a54ad90.png

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:

1e9cf1dbc490924a.gif

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

Langkah pertama dalam membuat animasi pegas adalah menambahkan ikon luaskan f88173321938c003.png. Ikon luaskan menyediakan tombol bagi pengguna untuk meluaskan item daftar.

9fbd3fb0daf35fd3.png

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

Ikon pensil hitam dan putih

Desain Material menyediakan sejumlah ikon yang diatur dalam kategori umum untuk sebagian besar kebutuhan Anda.

bfdb896506790c69.png

Menambahkan dependensi Gradle

Tambahkan dependensi library material-icons-extended ke project Anda. Anda akan menggunakan ikon Icons.Filled.ExpandLess 30c384f00846e69b.png dan Icons.Filled.ExpandMore f88173321938c003.png dari library ini.

  1. Di panel project, buka Gradle Scripts > build.gradle (Module: Woof.app).

f7fe58e936bbad3e.png

  1. Scroll ke akhir file build.gradle (Module: Woof.app). Di blok dependencies{}, tambahkan baris berikut:
implementation "androidx.compose.material:material-icons-extended:$compose_version"

Menambahkan composable ikon

Tambahkan fungsi untuk menampilkan ikon luaskan dari library ikon Material dan gunakan sebagai tombol.

  1. Di MainActivity.kt, setelah fungsi DogItem(), buat fungsi composable baru yang disebut DogItemButton().
  2. Teruskan Boolean untuk status diperluas, ekspresi lambda untuk peristiwa klik tombol, dan Modifier opsional sebagai berikut:
@Composable
private fun DogItemButton(
    expanded: Boolean,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {

}
  1. Di dalam fungsi DogItemButton(), tambahkan composable IconButton() yang menerima parameter bernama onClick, lambda menggunakan sintaksis lambda di akhir, yang dipanggil saat ikon ini ditekan, dan setel ke argumen onClick yang dimasukkan.
@Composable
private fun DogItemButton(
   // ...
) {
   IconButton(onClick = onClick) {

   }
}
  1. Di dalam blok lambda IconButton(), tambahkan composable Icon dengan parameter bernama imageVector dan setel ke Icons.Filled.ExpandMore. Ini adalah tombol ikon f88173321938c003.png yang akan ditampilkan di akhir item daftar. Android Studio menampilkan peringatan untuk parameter composable Icon() yang akan Anda perbaiki di langkah-langkah berikutnya.
  2. Tambahkan parameter bernama tint, dan setel warna ikon ke MaterialTheme.colors.secondary. Tambahkan parameter bernama contentDescription, dan setel ke resource string R.string.expand_button_content_description.
import androidx.compose.material.Icon
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ExpandMore

IconButton(onClick = onClick) {
   Icon(
       imageVector = Icons.Filled.ExpandMore,
       tint = MaterialTheme.colors.secondary,
       contentDescription = stringResource(R.string.expand_button_content_description)
   )
}

Menampilkan ikon

Tampilkan composable DogItemButton() dengan menambahkannya ke tata letak.

  1. Di awal fungsi composable DogItem(), tambahkan var untuk menyimpan status diluaskan dari item daftar. Setel nilai awal ke false.
var expanded by remember { mutableStateOf(false) }
  1. Di akhir blok Row fungsi composable DogItem(), panggil fungsi DogItemButton(), lalu teruskan status diperluas dan lambda kosong untuk callback. Kode ini menampilkan tombol ikon di item daftar.
  2. Untuk menampilkan tombol ikon dalam item daftar, pada fungsi composable DogItem() di akhir blok Row, setelah panggilan ke DogInformation(), lakukan panggilan ke DogItemButton(). Teruskan status expanded dan lambda kosong untuk callback. Anda akan mendefinisikan fungsi lambda ini di langkah berikutnya.
Row(
   modifier = Modifier
       .fillMaxWidth()
       .padding(8.dp)
) {
   DogIcon(dog.imageResourceId)
   DogInformation(dog.name, dog.age)
   DogItemButton(
      expanded = expanded,
      onClick = { }
   )
}
  1. Build & Refresh pratinjau di panel Design.

a49643f08701a8d.png

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 panjang elemen turunan tanpa bobot lainnya.

6c2b523849f0f626.png

Menambahkan pengatur jarak ke baris item daftar

  1. Di akhir blok Row dalam fungsi composable DogItem(), tambahkan Spacer. Teruskan Modifier dengan weight(1f). Modifier.weight() menyebabkan pengatur jarak mengisi ruang yang tersisa di baris.
Row(
   modifier = Modifier
       .fillMaxWidth()
       .padding(8.dp)
) {
   DogIcon(dog.imageResourceId)
   DogInformation(dog.name, dog.age)
   Spacer(Modifier.weight(1f))
   DogItemButton(
      expanded = expanded,
      onClick = { }
   )
}
  1. Build & Refresh pratinjau di panel Design. Perhatikan bahwa tombol luaskan kini sejajar dengan bagian akhir item daftar.

f6a140413de9ad54.png

4. Menambahkan Composable untuk menampilkan hobi

Dalam tugas ini, Anda akan menambahkan composable Text untuk menampilkan informasi hobi anjing.

66ea5cc5c7253d55.png

  1. Buat fungsi composable baru yang disebut DogHobby() yang menggunakan ID resource string hobi anjing dan Modifier opsional.
  2. Di dalam fungsi DogHobby(), buat kolom dengan atribut padding berikut untuk menambahkan ruang antara kolom dan composable turunan.
import androidx.annotation.StringRes

@Composable
fun DogHobby(@StringRes dogHobby: Int, modifier: Modifier = Modifier) {
   Column(
       modifier = modifier.padding(
           start = 16.dp,
            top = 8.dp,
            bottom = 16.dp,
            end = 16.dp
       )
   ) { }
}
  1. Di dalam blok kolom, tambahkan dua composable Text. Satu untuk menampilkan teks About di atas informasi hobi, dan satu lagi untuk menampilkan informasi hobi.

3051387c4b9c7455.png

  1. Untuk teks About, setel gayanya ke h3 (Heading 3) dan warnanya ke onBackground. Untuk informasi hobi, setel gayanya ke body1.
Column(
   modifier = modifier.padding(
       //..
   )
) {
   Text(
       text = stringResource(R.string.about),
       style = MaterialTheme.typography.h3,
   )
   Text(
       text = stringResource(dogHobby),
       style = MaterialTheme.typography.body1,
   )
}
  1. Seperti inilah tampilan fungsi composable DogHobby() yang telah selesai.
@Composable
fun DogHobby(@StringRes dogHobby: Int, modifier: Modifier = Modifier) {
   Column(
       modifier = modifier.padding(
           start = 16.dp,
           top = 8.dp,
           bottom = 16.dp,
           end = 16.dp
       )
   ) {
       Text(
           text = stringResource(R.string.about),
           style = MaterialTheme.typography.h3
       )
       Text(
           text = stringResource(dogHobby),
           style = MaterialTheme.typography.body1
       )
   }
}
  1. Untuk menampilkan composable DogHobby(), di DogItem(), gabungkan Row dengan Column. Lakukan panggilan ke fungsi DogHobby(), dengan meneruskan dog.hobbies sebagai parameter, setelah Row sebagai turunan kedua.
Column() {
   Row(
       //..
   ) {
       //..
   }
   DogHobby(dog.hobbies)
}

Fungsi DogItem() lengkap akan terlihat seperti ini:

@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
   var expanded by remember { mutableStateOf(false) }
   Card(
        elevation = 4.dp,
       modifier = modifier.padding(8.dp)
   ) {
       Column() {
           Row(
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(8.dp)
           ) {
               DogIcon(dog.imageResourceId)
               DogInformation(dog.name, dog.age)
               Spacer(Modifier.weight(1f))
               DogItemButton(
                   expanded = expanded,
                   onClick = { expanded = !expanded },
               )
           }
           DogHobby(dog.hobbies)
       }
   }
}
  1. Build & Refresh pratinjau di panel Design. Perhatikan hobi anjing yang ditampilkan.

9e2e68a4bc4a8ae1.png

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.

  1. Pada fungsi composable DogItem(), dalam panggilan fungsi DogItemButton(), tentukan ekspresi lambda onClick(), ubah nilai status boolean expanded menjadi true saat tombol diklik, dan ubah kembali ke false jika tombol diklik lagi.
DogItemButton(
   expanded = expanded,
   onClick = { expanded = !expanded }
)
  1. Di fungsi DogItemButton(), gabungkan panggilan fungsi DogHobby() dengan pemeriksaan if pada boolean expanded.
// No need to copy over
@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
   var expanded by remember { mutableStateOf(false) }
   Card(
       //..
   ) {
       Column() {
           Row(
               //..
           ) {
               //..
           }
           if (expanded) {
               DogHobby(dog.hobbies)
           }
       }
   }
}

Pada kode di atas, informasi hobi anjing hanya ditampilkan jika nilai expanded adalah true.

  1. Pratinjau dapat menunjukkan tampilan UI, dan Anda juga dapat berinteraksi dengannya. Untuk berinteraksi dengan pratinjau UI, klik tombol Interactive Mode 42379dbe94a7a497.png di pojok kanan atas panel Desain. Tombol ini akan memulai pratinjau dalam mode interaktif.

2a4ad1f3d2d0bff7.png

  1. Berinteraksilah dengan pratinjau dengan mengklik tombol luaskan. Perhatikan bahwa informasi hobi anjing disembunyikan dan ditampilkan saat Anda mengklik tombol luaskan.

6ee6774b5b14c7e1.gif

Perhatikan bahwa ikon tombol luaskan tetap sama saat item daftar diluaskan. Untuk pengalaman pengguna yang lebih baik, ubahlah ikon sehingga ExpandMore menampilkan panah bawah c761ef298c2aea5a.png, dan ExpandLess menampilkan panah atas b380f933be0b6ff4.png.

  1. Pada fungsi DogItemButton(), perbarui nilai imageVector berdasarkan status expanded 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,
           //..
       )
   }
}
  1. Jalankan aplikasi di perangkat atau emulator, atau gunakan mode interaktif lagi dalam pratinjau. Perhatikan bahwa ikon berganti-ganti antara ikon ExpandMore c761ef298c2aea5a.png dan ExpandLess b380f933be0b6ff4.png.

bf8bb280a774a6d4.gif

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.

7b52f63dc639c28d.gif

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.

efek pegasPantulan Tinggi

efek pegasTanpa Pantulan

Kekakuan Tinggi

Kekakuan Sangat Rendah

Sekarang, Anda akan menambahkan animasi pegas ke aplikasi.

  1. Di MainActivity.kt, di DogItem(), tambahkan parameter modifier ke tata letak Column.
@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
   //..
   Card(
       //..
   ) {
       Column(
          modifier = Modifier
       ){
           //..
       }
   }
}

Amati 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. Anda akan menggunakan pengubah animateContentSize untuk menambahkan transisi antara tinggi baru dan lama.

// No need to copy over
@Composable
fun DogItem(...) {

        //..
           if (expanded) {
               DogHobby(dog.hobbies)
           }
}
  1. Buat rantai pengubah dengan pengubah animateContentSize untuk menganimasikan perubahan ukuran (tinggi item daftar).
import androidx.compose.animation.animateContentSize

Column(
           modifier = Modifier
               .animateContentSize()
       ) {
            //..
       }

Dengan 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, Anda akan menggunakan parameter animationSpec opsional yang memungkinkan Anda menyesuaikan animasi.

  1. Tambahkan parameter animationSpec ke panggilan fungsi animateContentSize(). Setel ke animasi pegas dengan parameter DampingRatioMediumBouncy dan StiffnessLow.
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring

Column(
   modifier = Modifier
       .animateContentSize(
           animationSpec = spring(
               dampingRatio = Spring.DampingRatioMediumBouncy,
               stiffness = Spring.StiffnessLow
           )
       )
)
  1. Build & Refresh pratinjau di panel Design, dan gunakan mode interaktif atau jalankan aplikasi Anda di emulator atau perangkat untuk melihat cara kerja animasi pegas.

8cf711b8821b4696.gif

Jalankan ulang aplikasi di emulator atau perangkat dan nikmati aplikasi Anda yang menarik dengan animasi.

1e9cf1dbc490924a.gif

7. (Opsional) Bereksperimen dengan animasi lain

animate*AsState

Fungsi animate*AsState() adalah salah satu API animasi 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.

Gunakan fungsi animateColorAsState() untuk menganimasikan warna saat item daftar diluaskan.

Petunjuk:

  1. Deklarasikan warna dan delegasikan inisialisasinya ke fungsi animateColorAsState().
  2. Setel parameter bernama targetValue, bergantung pada nilai boolean expanded.
@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
   //..
   val color by animateColorAsState(
       targetValue = if (expanded) Green25 else MaterialTheme.colors.surface,
   )
   Card(
       //..
   ) {...}
}
  1. Setel color yang Anda deklarasikan di atas sebagai pengubah latar belakang ke Column.
@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
   //..
   Card(
       //..
   ) {
       Column(
           modifier = Modifier
               .animateContentSize(
                   //..
                   )
               )
               .background(color = color)
       ) {...}
}

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.

Mempelajari lebih lanjut