Android Lanjutan di Kotlin 03.2: Animasi dengan MotionLayout

1. Sebelum memulai

Codelab ini adalah bagian dari kursus Android Lanjutan di Kotlin. Anda akan mendapatkan manfaat maksimal dari kursus ini jika menyelesaikan codelab secara berurutan, tetapi ini tidak bersifat wajib. Semua codelab kursus tercantum di halaman landing codelab Android Lanjutan di Kotlin.

MotionLayout adalah library yang memungkinkan Anda menambahkan gerakan yang kaya ke dalam aplikasi Android Anda. Library ini didasarkan pada ConstraintLayout, dan memungkinkan Anda menganimasikan apa pun yang dapat Anda buat menggunakan ConstraintLayout.

Anda dapat menggunakan MotionLayout untuk menganimasikan lokasi, ukuran, visibilitas, alfa, warna, ketinggian, rotasi, dan atribut lainnya dari beberapa tampilan sekaligus. Dengan menggunakan XML deklaratif, Anda dapat membuat animasi terkoordinasi yang melibatkan beberapa tampilan yang sulit dicapai dalam kode.

Animasi adalah cara yang bagus untuk meningkatkan pengalaman aplikasi. Anda dapat menggunakan animasi untuk:

  • Tampilkan perubahan—menganimasikan antara status memungkinkan pengguna secara alami melacak perubahan di UI Anda.
  • Menarik perhatian—gunakan animasi untuk menarik perhatian ke elemen UI yang penting.
  • Buat desain yang menarik—gerakan yang efektif dalam desain membuat aplikasi terlihat sempurna.

Prasyarat

Codelab ini dirancang untuk developer dengan beberapa pengalaman pengembangan Android. Sebelum mencoba menyelesaikan codelab ini, Anda harus:

  • Mengetahui cara membuat aplikasi dengan aktivitas, tata letak dasar, dan menjalankannya di perangkat atau emulator menggunakan Android Studio. Pahami ConstraintLayout. Baca codelab Constraint Layout untuk mempelajari ConstraintLayout lebih lanjut.

Yang akan Anda lakukan

  • Menentukan animasi dengan ConstraintSets dan MotionLayout
  • Membuat animasi berdasarkan peristiwa tarik
  • Mengubah animasi dengan KeyPosition
  • Mengubah atribut dengan KeyAttribute
  • Menjalankan animasi dengan kode
  • Menganimasikan header yang dapat diciutkan dengan MotionLayout

Yang Anda butuhkan

  • Android Studio 4.0 (Editor MotionLayout hanya berfungsi dengan Android Studio versi ini.)

2. Memulai

Untuk mendownload aplikasi contoh, Anda dapat melakukan:

... atau clone repositori GitHub dari command line dengan menggunakan perintah berikut:

$ git clone https://github.com/googlecodelabs/motionlayout.git

3. Membuat animasi dengan MotionLayout

Pertama, Anda akan membuat animasi yang memindahkan tampilan dari awal atas layar ke akhir bawah sebagai respons terhadap klik pengguna.

Untuk membuat animasi dari kode awal, Anda memerlukan bagian utama berikut:

  • MotionLayout, yang merupakan subclass dari ConstraintLayout. Anda menentukan semua tampilan yang akan dianimasikan di dalam tag MotionLayout.
  • MotionScene, yang merupakan file XML yang menjelaskan animasi untuk MotionLayout.
  • Transition, yang merupakan bagian dari MotionScene yang menentukan durasi animasi, pemicu, dan cara memindahkan tampilan.
  • ConstraintSet yang menentukan batasan start dan end transisi.

Mari kita lihat satu per satu, dimulai dengan MotionLayout.

Langkah 1: Pelajari kode yang ada

MotionLayout adalah subclass dari ConstraintLayout, sehingga mendukung semua fitur yang sama sambil menambahkan animasi. Untuk menggunakan MotionLayout, Anda menambahkan tampilan MotionLayout di tempat Anda akan menggunakan ConstraintLayout.

  1. Di res/layout, buka activity_step1.xml. Di sini Anda memiliki ConstraintLayout dengan satu ImageView bintang, dengan warna yang diterapkan di dalamnya.

activity_step1.xml

<!-- initial code -->
<androidx.constraintlayout.widget.ConstraintLayout
       ...
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       >

   <ImageView
           android:id="@+id/red_star"
           ...
   />

</androidx.constraintlayout.motion.widget.MotionLayout>

ConstraintLayout ini tidak memiliki batasan apa pun, jadi jika Anda menjalankan aplikasi sekarang, Anda akan melihat tampilan bintang tanpa batasan, yang berarti bintang akan diposisikan di lokasi yang tidak diketahui. Android Studio akan memberikan peringatan tentang kurangnya batasan.

Langkah 2: Konversi ke Tata Letak Gerakan

Untuk menganimasikan menggunakan MotionLayout,, Anda harus mengonversi ConstraintLayout menjadi MotionLayout.

Agar tata letak Anda dapat menggunakan adegan gerakan, tata letak harus menunjuk ke adegan tersebut.

  1. Untuk melakukannya, buka permukaan desain. Di Android Studio 4.0, Anda membuka permukaan desain menggunakan ikon terpisah atau desain di kanan atas saat melihat file XML tata letak.

a2beea710c2decb7.png

  1. Setelah Anda membuka permukaan desain, klik kanan pratinjau, lalu pilih Convert to MotionLayout.

4fa936a98a8393b9.png

Tindakan ini menggantikan tag ConstraintLayout dengan tag MotionLayout dan menambahkan motion:layoutDescription ke tag MotionLayout yang mengarah ke @xml/activity_step1_scene.

activity_step1**.xml**

<!-- explore motion:layoutDescription="@xml/activity_step1_scene" -->
<androidx.constraintlayout.motion.widget.MotionLayout
       ...
       motion:layoutDescription="@xml/activity_step1_scene">

Scene gerak adalah satu file XML yang mendeskripsikan animasi dalam MotionLayout.

Segera setelah Anda mengonversi ke MotionLayout, platform desain akan menampilkan Motion Editor

66d0e80d5ab4daf8.png

Ada tiga elemen UI baru di Motion Editor:

  1. Ringkasan – Ini adalah pilihan modal yang memungkinkan Anda memilih berbagai bagian animasi. Dalam gambar ini, start ConstraintSet dipilih. Anda juga dapat memilih transisi antara start dan end dengan mengklik panah di antara keduanya.
  2. Bagian – Di bawah ringkasan terdapat jendela bagian yang berubah berdasarkan item ringkasan yang saat ini dipilih. Pada gambar ini, informasi start ConstraintSet ditampilkan di jendela pilihan.
  3. Atribut – Panel atribut menampilkan dan memungkinkan Anda mengedit atribut item yang saat ini dipilih dari ringkasan atau jendela pilihan. Pada gambar ini, atribut untuk start ConstraintSet ditampilkan.

Langkah 3: Tentukan batasan awal dan akhir

Semua animasi dapat ditentukan dalam hal awal dan akhir. Awal menjelaskan tampilan layar sebelum animasi, dan akhir menjelaskan tampilan layar setelah animasi selesai. MotionLayout bertanggung jawab untuk mencari tahu cara menganimasikan antara status awal dan akhir (dari waktu ke waktu).

MotionScene menggunakan tag ConstraintSet untuk menentukan status awal dan akhir. ConstraintSet adalah sekumpulan batasan yang dapat diterapkan pada tampilan. Ini mencakup batasan lebar, tinggi, dan ConstraintLayout. Selain itu, atribut seperti alpha juga disertakan. Tidak berisi tampilan itu sendiri, hanya batasan pada tampilan tersebut.

Batasan apa pun yang ditentukan dalam ConstraintSet akan menggantikan batasan yang ditentukan dalam file tata letak. Jika Anda menentukan batasan di tata letak dan MotionScene, hanya batasan di MotionScene yang diterapkan.

Pada langkah ini, Anda akan membatasi tampilan bintang agar dimulai di bagian atas layar, dan berakhir di bagian bawah layar.

Anda dapat menyelesaikan langkah ini menggunakan Editor Gerakan, atau dengan mengedit teks activity_step1_scene.xml secara langsung.

  1. Pilih start ConstraintSet di panel ringkasan

6e57661ed358b860.png

  1. Di panel pilihan, pilih red_star. Saat ini, Source of layout ditampilkan – artinya tidak dibatasi dalam ConstraintSet ini. Gunakan ikon pensil di kanan atas untuk Buat Batasan

f9564c574b86ea8.gif

  1. Pastikan red_star menampilkan Sumber start saat start ConstraintSet dipilih di panel ringkasan.
  2. Di panel Attributes, dengan red_star dipilih di start ConstraintSet, tambahkan Batasan di bagian atas dan mulai dengan mengklik tombol + biru.

2fce076cd7b04bd.png

  1. Buka xml/activity_step1_scene.xml untuk melihat kode yang dihasilkan Motion Editor untuk batasan ini.

activity_step1_scene.xml

<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
   <Constraint
           android:id="@+id/red_star"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           motion:layout_constraintStart_toStartOf="parent"
           motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>

ConstraintSet memiliki id @id/start, dan menentukan semua batasan yang akan diterapkan ke semua tampilan di MotionLayout. Karena MotionLayout ini hanya memiliki satu tampilan, maka hanya memerlukan satu Constraint.

Constraint di dalam ConstraintSet menentukan ID tampilan yang dibatasinya, @id/red_star yang ditentukan di activity_step1.xml. Penting untuk diperhatikan bahwa tag Constraint hanya menentukan batasan dan informasi tata letak. Tag Constraint tidak mengetahui bahwa tag tersebut diterapkan ke ImageView.

Batasan ini menentukan tinggi, lebar, dan dua batasan lainnya yang diperlukan untuk membatasi tampilan red_star ke awal atas induknya.

  1. Pilih endConstraintSet di panel ringkasan.

346e1248639b6f1e.png

  1. Ikuti langkah-langkah yang sama seperti sebelumnya untuk menambahkan Constraint untuk red_star di end ConstraintSet.
  2. Untuk menggunakan Motion Editor guna menyelesaikan langkah ini, tambahkan batasan ke bottom dan end dengan mengklik tombol + berwarna biru.

fd33c779ff83c80a.png

  1. Kode dalam XML akan terlihat seperti ini:

activitiy_step1_scene.xml

<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
   <Constraint
           android:id="@+id/red_star"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           motion:layout_constraintEnd_toEndOf="parent"
           motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>

Sama seperti @id/start, ConstraintSet ini memiliki satu Constraint di @id/red_star. Kali ini, batasi ke ujung bawah layar.

Anda tidak harus menamainya @id/start dan @id/end, tetapi lebih mudah jika Anda melakukannya.

Langkah 4: Tentukan transisi

Setiap MotionScene juga harus menyertakan setidaknya satu transisi. Transisi menentukan setiap bagian dari satu animasi, dari awal hingga akhir.

Transisi harus menentukan ConstraintSet awal dan akhir untuk transisi. Transisi juga dapat menentukan cara mengubah animasi dengan cara lain, seperti durasi menjalankan animasi atau cara menganimasikan dengan menarik tampilan.

  1. Motion Editor membuat transisi untuk kita secara default saat membuat file MotionScene. Buka activity_step1_scene.xml untuk melihat transisi yang dihasilkan.

activity_step1_scene.xml

<!-- A transition describes an animation via start and end state -->
<Transition
   motion:constraintSetEnd="@+id/end"
   motion:constraintSetStart="@id/start"
   motion:duration="1000">
  <KeyFrameSet>
  </KeyFrameSet>
</Transition>

Inilah semua yang dibutuhkan MotionLayout untuk membuat animasi. Melihat setiap atribut:

  • constraintSetStart akan diterapkan ke tampilan saat animasi dimulai.
  • constraintSetEnd akan diterapkan ke tampilan di akhir animasi.
  • duration menentukan lamanya waktu animasi yang diperlukan dalam milidetik.

MotionLayout kemudian akan menentukan jalur antara batasan awal dan akhir, lalu menganimasikannya selama durasi yang ditentukan.

Langkah 5: Lihat pratinjau animasi di Editor Gerakan

dff9ecdc1f4a0740.gif

Animasi: Video tentang cara memutar pratinjau transisi di Editor Gerakan

  1. Buka Editor Gerakan dan pilih transisi dengan mengklik panah di antara start dan end di panel ringkasan.

1dc541ae8c43b250.png

  1. Panel pilihan menampilkan kontrol pemutaran dan panel geser saat transisi dipilih. Klik putar atau tarik posisi saat ini untuk melihat pratinjau animasi.

a0fd2593384dfb36.png

Langkah 6: Tambahkan pengendali saat diklik

Anda memerlukan cara untuk memulai animasi. Salah satu cara untuk melakukannya adalah dengan membuat MotionLayout merespons peristiwa klik pada @id/red_star.

  1. Buka editor gerakan dan pilih transisi dengan mengklik panah di antara awal dan akhir di panel ringkasan.

b6f94b344ce65290.png

  1. Klik 699f7ae04024ccf6.png Create click or swipe handler di toolbar untuk panel ringkasan . Tindakan ini menambahkan pengendali yang akan memulai transisi.
  2. Pilih Click Handler dari pop-up

ccf92d06335105fe.png

  1. Ubah Lihat Untuk Klik menjadi red_star.

b0d3f0c970604f01.png

  1. Klik Tambahkan pengendali klik diwakili oleh titik kecil pada Editor Gerakan Transisi.

cec3913e67fb4105.png

  1. Setelah transisi dipilih di panel ringkasan, tambahkan atribut clickAction dari toggle ke pengendali OnClick yang baru saja Anda tambahkan di panel atribut.

9af6fc60673d093d.png

  1. Buka activity_step1_scene.xml untuk melihat kode yang dihasilkan Motion Editor

activity_step1_scene.xml

<!-- A transition describes an animation via start and end state -->
<Transition
    motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    motion:duration="1000">
    <!-- MotionLayout will handle clicks on @id/red_star to "toggle" the animation between the start and end -->
    <OnClick
        motion:targetId="@id/red_star"
        motion:clickAction="toggle" />
</Transition>

Transition memberi tahu MotionLayout untuk menjalankan animasi sebagai respons terhadap peristiwa klik menggunakan tag <OnClick>. Melihat setiap atribut:

  • targetId adalah tampilan yang harus dipantau untuk klik.
  • clickAction dari toggle akan beralih antara status awal dan akhir saat diklik. Anda dapat melihat opsi lain untuk clickAction di dokumentasi.
  1. Jalankan kode Anda, klik Langkah 1, lalu klik bintang merah dan lihat animasinya.

Langkah 5: Cara kerja animasi

Jalankan aplikasi. Anda akan melihat animasi berjalan saat mengklik bintang.

7ba88af963fdfe10.gif

File scene gerak yang telah selesai menentukan satu Transition yang mengarah ke ConstraintSet awal dan akhir.

Di awal animasi (@id/start), ikon bintang dibatasi ke bagian atas awal layar. Di akhir animasi (@id/end), ikon bintang dibatasi ke ujung bawah layar.

<?xml version="1.0" encoding="utf-8"?>

<!-- Describe the animation for activity_step1.xml -->
<MotionScene xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:android="http://schemas.android.com/apk/res/android">
   <!-- A transition describes an animation via start and end state -->
   <Transition
           motion:constraintSetStart="@+id/start"
           motion:constraintSetEnd="@+id/end"
           motion:duration="1000">
       <!-- MotionLayout will handle clicks on @id/star to "toggle" the animation between the start and end -->
       <OnClick
               motion:targetId="@id/red_star"
               motion:clickAction="toggle" />
   </Transition>

   <!-- Constraints to apply at the end of the animation -->
   <ConstraintSet android:id="@+id/start">
       <Constraint
               android:id="@+id/red_star"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               motion:layout_constraintStart_toStartOf="parent"
               motion:layout_constraintTop_toTopOf="parent" />
   </ConstraintSet>

   <!-- Constraints to apply at the end of the animation -->
   <ConstraintSet android:id="@+id/end">
       <Constraint
               android:id="@+id/red_star"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               motion:layout_constraintEnd_toEndOf="parent"
               motion:layout_constraintBottom_toBottomOf="parent" />
   </ConstraintSet>
</MotionScene>

4. Membuat animasi berdasarkan peristiwa penarikan

Untuk langkah ini, Anda akan membuat animasi yang merespons peristiwa penarikan pengguna (saat pengguna menggeser layar) untuk menjalankan animasi. MotionLayout mendukung pelacakan peristiwa sentuhan untuk memindahkan tampilan, serta gestur melempar berbasis fisika untuk membuat gerakan yang lancar.

Langkah 1: Periksa kode awal

  1. Untuk memulai, buka file tata letak activity_step2.xml, yang memiliki MotionLayout yang ada. Lihat kodenya.

activity_step2.xml

<!-- initial code -->

<androidx.constraintlayout.motion.widget.MotionLayout
       ...
       motion:layoutDescription="@xml/step2" >

   <ImageView
           android:id="@+id/left_star"
           ...
   />

   <ImageView
           android:id="@+id/right_star"
           ...
   />

   <ImageView
           android:id="@+id/red_star"
           ...
   />

   <TextView
           android:id="@+id/credits"
           ...
           motion:layout_constraintTop_toTopOf="parent"
           motion:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.motion.widget.MotionLayout>

Tata letak ini menentukan semua tampilan untuk animasi. Tiga ikon bintang tidak dibatasi dalam tata letak karena akan dianimasikan dalam adegan gerakan.

Kredit TextView memiliki batasan yang diterapkan, karena tetap berada di tempat yang sama selama animasi dan tidak mengubah atribut apa pun.

Langkah 2: Animasi adegan

Sama seperti animasi terakhir, animasi akan ditentukan oleh ConstraintSet, awal dan akhir serta Transition.

Tentukan ConstraintSet awal

  1. Buka adegan gerak xml/step2.xml untuk menentukan animasi.
  2. Tambahkan batasan untuk batasan awal start. Pada awalnya, ketiga bintang berada di tengah bagian bawah layar. Bintang kanan dan kiri memiliki nilai alpha 0.0, yang berarti bintang tersebut sepenuhnya transparan dan tersembunyi.

step2.xml

<!-- TODO apply starting constraints -->

<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
   <Constraint
           android:id="@+id/red_star"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           motion:layout_constraintStart_toStartOf="parent"
           motion:layout_constraintEnd_toEndOf="parent"
           motion:layout_constraintBottom_toBottomOf="parent" />

   <Constraint
           android:id="@+id/left_star"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:alpha="0.0"
           motion:layout_constraintStart_toStartOf="parent"
           motion:layout_constraintEnd_toEndOf="parent"
           motion:layout_constraintBottom_toBottomOf="parent" />

   <Constraint
           android:id="@+id/right_star"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:alpha="0.0"
           motion:layout_constraintStart_toStartOf="parent"
           motion:layout_constraintEnd_toEndOf="parent"
           motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>

Dalam ConstraintSet ini, Anda menentukan satu Constraint untuk setiap bintang. Setiap batasan akan diterapkan oleh MotionLayout di awal animasi.

Setiap tampilan bintang dipusatkan di bagian bawah layar menggunakan batasan awal, akhir, dan bawah. Kedua bintang @id/left_star dan @id/right_star memiliki nilai alfa tambahan yang membuatnya tidak terlihat dan akan diterapkan di awal animasi.

Set batasan start dan end menentukan awal dan akhir animasi. Batasan pada awal, seperti motion:layout_constraintStart_toStartOf akan membatasi awal tampilan ke awal tampilan lain. Hal ini mungkin membingungkan pada awalnya, karena nama start digunakan untuk dan keduanya digunakan dalam konteks batasan. Untuk membantu membedakannya, start dalam layout_constraintStart mengacu pada "awal" tampilan, yaitu kiri dalam bahasa kiri ke kanan dan kanan dalam bahasa kanan ke kiri. Set batasan start mengacu pada awal animasi.

Menentukan ConstraintSet akhir

  1. Tentukan batasan akhir untuk menggunakan rantai guna memosisikan ketiga bintang bersama-sama di bawah @id/credits. Selain itu, nilai akhir alpha bintang kiri dan kanan akan ditetapkan ke 1.0.

step2.xml

<!-- TODO apply ending constraints -->

<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">

   <Constraint
           android:id="@+id/left_star"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:alpha="1.0"
           motion:layout_constraintHorizontal_chainStyle="packed"
           motion:layout_constraintStart_toStartOf="parent"
           motion:layout_constraintEnd_toStartOf="@id/red_star"
           motion:layout_constraintTop_toBottomOf="@id/credits" />

   <Constraint
           android:id="@+id/red_star"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           motion:layout_constraintStart_toEndOf="@id/left_star"
           motion:layout_constraintEnd_toStartOf="@id/right_star"
           motion:layout_constraintTop_toBottomOf="@id/credits" />

   <Constraint
           android:id="@+id/right_star"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:alpha="1.0"
           motion:layout_constraintStart_toEndOf="@id/red_star"
           motion:layout_constraintEnd_toEndOf="parent"
           motion:layout_constraintTop_toBottomOf="@id/credits" />
</ConstraintSet>

Hasil akhirnya adalah tampilan akan menyebar dan naik dari tengah saat dianimasikan.

Selain itu, karena properti alpha ditetapkan pada @id/right_start dan @id/left_star di kedua ConstraintSets, kedua tampilan akan memudar saat animasi berlangsung.

Membuat animasi berdasarkan gesekan pengguna

MotionLayout dapat melacak peristiwa penarikan pengguna, atau gesekan, untuk membuat animasi "lemparan" berbasis fisika. Artinya, tampilan akan terus bergerak jika pengguna melemparnya dan akan melambat seperti objek fisik saat menggelinding di permukaan. Anda dapat menambahkan jenis animasi ini dengan tag OnSwipe di Transition.

  1. Ganti TODO untuk menambahkan tag OnSwipe dengan <OnSwipe motion:touchAnchorId="@id/red_star" />.

step2.xml

<!-- TODO add OnSwipe tag -->

<!-- A transition describes an animation via start and end state -->
<Transition
       motion:constraintSetStart="@+id/start"
       motion:constraintSetEnd="@+id/end">
   <!-- MotionLayout will track swipes relative to this view -->
   <OnSwipe motion:touchAnchorId="@id/red_star" />
</Transition>

OnSwipe berisi beberapa atribut, yang paling penting adalah touchAnchorId.

  • touchAnchorId adalah tampilan yang dilacak dan bergerak sebagai respons terhadap sentuhan. MotionLayout akan menjaga tampilan ini pada jarak yang sama dari jari yang menggeser.
  • touchAnchorSide menentukan sisi tampilan yang harus dilacak. Hal ini penting untuk tampilan yang diubah ukurannya, mengikuti jalur yang rumit, atau memiliki satu sisi yang bergerak lebih cepat daripada sisi lainnya.
  • dragDirection menentukan arah mana yang penting untuk animasi ini (atas, bawah, kiri, atau kanan).

Saat MotionLayout memproses peristiwa tarik, pemroses akan didaftarkan di tampilan MotionLayout, bukan tampilan yang ditentukan oleh touchAnchorId. Saat pengguna memulai gestur di mana saja di layar, MotionLayout akan menjaga jarak antara jari pengguna dan touchAnchorSide tampilan touchAnchorId tetap konstan. Jika mereka menyentuh 100 dp dari sisi penahan, misalnya, MotionLayout akan menjaga sisi tersebut 100 dp dari jari mereka selama seluruh animasi.

Cobalah

  1. Jalankan aplikasi lagi, dan buka layar Langkah 2. Anda akan melihat animasinya.
  2. Coba "melempar" atau melepaskan jari Anda di tengah animasi untuk mempelajari cara MotionLayout menampilkan animasi berbasis fisika yang lancar.

fefcdd690a0dcaec.gif

MotionLayout dapat menganimasikan desain yang sangat berbeda menggunakan fitur dari ConstraintLayout untuk membuat efek yang kaya.

Dalam animasi ini, ketiga tampilan diposisikan relatif terhadap induknya di bagian bawah layar untuk memulai. Pada akhirnya, ketiga tampilan diposisikan relatif terhadap @id/credits dalam rantai.

Meskipun tata letaknya sangat berbeda, MotionLayout akan membuat animasi yang lancar antara awal dan akhir.

5. Mengubah jalur

Pada langkah ini, Anda akan membuat animasi yang mengikuti jalur kompleks selama animasi dan menganimasikan kredit selama gerakan. MotionLayout dapat mengubah jalur yang akan dilalui tampilan antara awal dan akhir menggunakan KeyPosition.

Langkah 1: Pelajari kode yang ada

  1. Buka layout/activity_step3.xml dan xml/step3.xml untuk melihat tata letak dan adegan gerakan yang ada. ImageView dan TextView menampilkan teks bulan dan kredit.
  2. Buka file scene gerak (xml/step3.xml). Anda akan melihat bahwa Transition dari @id/start hingga @id/end ditentukan. Animasi memindahkan gambar bulan dari kiri bawah layar ke kanan bawah layar menggunakan dua ConstraintSets. Teks kredit memudar dari alpha="0.0" menjadi alpha="1.0" saat bulan bergerak.
  3. Jalankan aplikasi sekarang dan pilih Langkah 3. Anda akan melihat bahwa bulan mengikuti jalur linear (atau garis lurus) dari awal hingga akhir saat Anda mengklik bulan.

Langkah 2: Aktifkan penelusuran bug jalur

Sebelum menambahkan busur ke gerakan bulan, sebaiknya aktifkan proses debug jalur di MotionLayout.

Untuk membantu mengembangkan animasi kompleks dengan MotionLayout, Anda dapat menggambar jalur animasi setiap tampilan. Hal ini berguna saat Anda ingin memvisualisasikan animasi, dan untuk menyempurnakan detail kecil gerakan.

  1. Untuk mengaktifkan jalur penelusuran bug, buka layout/activity_step3.xml dan tambahkan motion:motionDebug="SHOW_PATH" ke tag MotionLayout.

activity_step3.xml

<!-- Add motion:motionDebug="SHOW_PATH" -->

<androidx.constraintlayout.motion.widget.MotionLayout
       ...
       motion:motionDebug="SHOW_PATH" >

Setelah mengaktifkan penelusuran bug jalur, saat menjalankan aplikasi lagi, Anda akan melihat jalur semua tampilan yang divisualisasikan dengan garis putus-putus.

23bbb604f456f65c.png

  • Lingkaran mewakili posisi awal atau akhir satu tampilan.
  • Garis mewakili jalur satu tampilan.
  • Berlian mewakili KeyPosition yang mengubah jalur.

Misalnya, dalam animasi ini, lingkaran tengah adalah posisi teks kredit.

Langkah 3: Mengubah jalur

Semua animasi di MotionLayout ditentukan oleh ConstraintSet awal dan akhir yang menentukan tampilan layar sebelum animasi dimulai dan setelah animasi selesai. Secara default, MotionLayout memetakan jalur linear (garis lurus) antara posisi awal dan akhir setiap tampilan yang berubah posisi.

Untuk membuat jalur yang kompleks seperti busur bulan dalam contoh ini, MotionLayout menggunakan KeyPosition untuk mengubah jalur yang diambil tampilan antara awal dan akhir.

  1. Buka xml/step3.xml dan tambahkan KeyPosition ke adegan. Tag KeyPosition ditempatkan di dalam tag Transition.

eae4dae9a12d0410.png

step3.xml

<!-- TODO: Add KeyFrameSet and KeyPosition -->
<KeyFrameSet>
   <KeyPosition
           motion:framePosition="50"
           motion:motionTarget="@id/moon"
           motion:keyPositionType="parentRelative"
           motion:percentY="0.5"
   />
</KeyFrameSet>

KeyFrameSet adalah turunan dari Transition, dan merupakan kumpulan semua KeyFrames, seperti KeyPosition, yang harus diterapkan selama transisi.

Saat MotionLayout menghitung jalur untuk bulan antara awal dan akhir, MotionLayout akan mengubah jalur berdasarkan KeyPosition yang ditentukan dalam KeyFrameSet. Anda dapat melihat cara ini mengubah jalur dengan menjalankan aplikasi lagi.

KeyPosition memiliki beberapa atribut yang menjelaskan cara memodifikasi jalur. Yang paling penting adalah:

  • framePosition adalah angka antara 0 dan 100. Menentukan kapan KeyPosition ini harus diterapkan dalam animasi, dengan 1 adalah 1% melalui animasi, dan 99 adalah 99% melalui animasi. Jadi, jika nilainya 50, Anda menerapkannya tepat di tengah.
  • motionTarget adalah tampilan yang jalur pergerakannya diubah oleh KeyPosition ini.
  • keyPositionType adalah cara KeyPosition ini mengubah jalur. Nilainya dapat berupa parentRelative, pathRelative, atau deltaRelative (seperti yang dijelaskan pada langkah berikutnya).
  • percentX | percentY adalah seberapa banyak jalur harus diubah pada framePosition (nilai antara 0,0 dan 1,0, dengan nilai negatif dan nilai >1 diizinkan).

Anda dapat memikirkannya seperti ini: "Pada framePosition ubah jalur motionTarget dengan memindahkannya sebesar percentX atau percentY sesuai dengan koordinat yang ditentukan oleh keyPositionType."

Secara default, MotionLayout akan membulatkan sudut yang muncul akibat modifikasi jalur. Jika melihat animasi yang baru saja Anda buat, Anda dapat melihat bahwa bulan mengikuti jalur melengkung di tikungan. Untuk sebagian besar animasi, inilah yang Anda inginkan, dan jika tidak, Anda dapat menentukan atribut curveFit untuk menyesuaikannya.

Coba

Jika menjalankan aplikasi lagi, Anda akan melihat animasi untuk langkah ini.

46b179c01801f19e.gif

Bulan mengikuti busur karena melewati KeyPosition yang ditentukan dalam Transition.

<KeyPosition
       motion:framePosition="50"
       motion:motionTarget="@id/moon"
       motion:keyPositionType="parentRelative"
       motion:percentY="0.5"
/>

Anda dapat membaca KeyPosition ini sebagai: "Pada framePosition 50 (setengah jalan animasi), ubah jalur motionTarget @id/moon dengan memindahkannya sebesar 50% Y (setengah jalan ke bawah layar) sesuai dengan koordinat yang ditentukan oleh parentRelative (seluruh MotionLayout)."

Jadi, di tengah animasi, bulan harus melewati KeyPosition yang 50% ke bawah di layar. KeyPosition ini tidak mengubah gerakan X sama sekali, sehingga bulan akan tetap bergerak dari awal hingga akhir secara horizontal. MotionLayout akan menentukan jalur yang mulus yang melewati KeyPosition ini saat bergerak antara awal dan akhir.

Jika Anda melihat lebih dekat, teks kredit dibatasi oleh posisi bulan. Mengapa tidak bergerak secara vertikal juga?

1c7cf779931e45cc.gif

<Constraint
       android:id="@id/credits"
       ...
       motion:layout_constraintBottom_toBottomOf="@id/moon"
       motion:layout_constraintTop_toTopOf="@id/moon"
/>

Ternyata, meskipun Anda mengubah jalur yang dilalui bulan, posisi awal dan akhir bulan tidak bergerak secara vertikal sama sekali. KeyPosition tidak mengubah posisi awal atau akhir, sehingga teks kredit dibatasi ke posisi akhir bulan.

Jika ingin kredit bergerak bersama bulan, Anda dapat menambahkan KeyPosition ke kredit, atau mengubah batasan awal pada @id/credits.

Di bagian berikutnya, Anda akan mempelajari berbagai jenis keyPositionType di MotionLayout.

6. Memahami keyPositionType

Di langkah terakhir, Anda menggunakan jenis keyPosition dari parentRelative untuk mengimbangi jalur sebesar 50% dari layar. Atribut keyPositionType menentukan cara MotionLayout akan mengubah jalur sesuai dengan percentX atau percentY.

<KeyFrameSet>
   <KeyPosition
           motion:framePosition="50"
           motion:motionTarget="@id/moon"
           motion:keyPositionType="parentRelative"
           motion:percentY="0.5"
   />
</KeyFrameSet>

Ada tiga jenis keyPosition yang mungkin: parentRelative, pathRelative, dan deltaRelative. Menentukan jenis akan mengubah sistem koordinat yang digunakan untuk menghitung percentX dan percentY.

Apa itu sistem koordinat?

Sistem koordinat memberikan cara untuk menentukan titik dalam ruang. Batas juga berguna untuk mendeskripsikan posisi di layar.

Sistem koordinat MotionLayout adalah sistem koordinat kartesius. Artinya, mereka memiliki sumbu X dan Y yang ditentukan oleh dua garis tegak lurus. Perbedaan utama di antara keduanya adalah posisi sumbu X pada layar (sumbu Y selalu tegak lurus dengan sumbu X).

Semua sistem koordinat di MotionLayout menggunakan nilai antara 0.0 dan 1.0 pada sumbu X dan Y. Nilai negatif dan nilai yang lebih besar dari 1.0 diizinkan. Jadi, misalnya, nilai percentX sebesar -2.0 berarti bergerak ke arah yang berlawanan dengan sumbu X dua kali.

Jika semua itu terdengar seperti pelajaran Aljabar, lihat gambar di bawah ini.

Koordinat parentRelative

a7b7568d46d9dec7.png

keyPositionType dari parentRelative menggunakan sistem koordinat yang sama dengan layar. Menentukan (0, 0) ke kiri atas seluruh MotionLayout, dan (1, 1) ke kanan bawah.

Anda dapat menggunakan parentRelative kapan pun Anda ingin membuat animasi yang bergerak di seluruh MotionLayout – seperti busur bulan dalam contoh ini.

Namun, jika Anda ingin mengubah jalur relatif terhadap gerakan, misalnya membuatnya sedikit melengkung, dua sistem koordinat lainnya adalah pilihan yang lebih baik.

Koordinat deltaRelatif

5680bf553627416c.png

Delta adalah istilah matematika untuk perubahan, jadi deltaRelative adalah cara untuk mengatakan "perubahan relatif". Dalam deltaRelative koordinat(0,0) adalah posisi awal tampilan, dan (1,1) adalah posisi akhir. Sumbu X dan Y sejajar dengan layar.

Sumbu X selalu horizontal di layar, dan sumbu Y selalu vertikal di layar. Dibandingkan dengan parentRelative, perbedaan utamanya adalah koordinat hanya menjelaskan bagian layar tempat tampilan akan bergerak.

deltaRelative adalah sistem koordinat yang bagus untuk mengontrol gerakan horizontal atau vertikal secara terpisah. Misalnya, Anda dapat membuat animasi yang menyelesaikan gerakan vertikal (Y) pada 50%, dan terus menganimasikan secara horizontal (X).

pathRelative koordinat

f3aaadaac8b4a93f.png

Sistem koordinat terakhir di MotionLayout adalah pathRelative. Animasi ini cukup berbeda dari dua lainnya karena sumbu X mengikuti jalur gerakan dari awal hingga akhir. Jadi, (0,0) adalah posisi awal, dan (1,0) adalah posisi akhir.

Mengapa Anda menginginkan hal ini? Pada pandangan pertama, hal ini cukup mengejutkan, terutama karena sistem koordinat ini bahkan tidak selaras dengan sistem koordinat layar.

Ternyata pathRelative sangat berguna untuk beberapa hal.

  • Mempercepat, memperlambat, atau menghentikan tampilan selama sebagian animasi. Karena dimensi X akan selalu cocok dengan jalur yang diambil tampilan secara persis, Anda dapat menggunakan pathRelative KeyPosition untuk mengubah framePosition mana yang dicapai pada titik tertentu dalam jalur tersebut. Jadi, KeyPosition pada framePosition="50" dengan percentX="0.1" akan menyebabkan animasi memerlukan waktu 50% untuk menempuh 10% pertama gerakan.
  • Menambahkan busur halus ke jalur. Karena dimensi Y selalu tegak lurus terhadap gerakan, mengubah Y akan mengubah jalur ke kurva relatif terhadap gerakan keseluruhan.
  • Menambahkan dimensi kedua saat deltaRelative tidak akan berfungsi. Untuk gerakan horizontal dan vertikal sepenuhnya, deltaRelative hanya akan membuat satu dimensi yang berguna. Namun, pathRelative akan selalu membuat koordinat X dan Y yang dapat digunakan.

Di langkah berikutnya, Anda akan mempelajari cara membuat jalur yang lebih kompleks menggunakan lebih dari satu KeyPosition.

7. Membangun jalur yang kompleks

Melihat animasi yang Anda buat di langkah terakhir, animasi tersebut memang menciptakan kurva yang mulus, tetapi bentuknya bisa lebih menyerupai "bulan".

Mengubah jalur dengan beberapa elemen KeyPosition

MotionLayout dapat mengubah jalur lebih lanjut dengan menentukan KeyPosition sebanyak yang diperlukan untuk mendapatkan gerakan apa pun. Untuk animasi ini, Anda akan membuat busur, tetapi Anda dapat membuat bulan melompat naik dan turun di tengah layar, jika Anda mau.

  1. Buka xml/step4.xml. Anda akan melihat bahwa video tersebut memiliki jumlah penayangan yang sama dan KeyFrame yang Anda tambahkan pada langkah terakhir.
  2. Untuk melengkapi bagian atas kurva, tambahkan dua KeyPositions lagi ke jalur @id/moon, satu tepat sebelum mencapai bagian atas, dan satu lagi setelahnya.

500b5ac2db48ef87.png

step4.xml

<!-- TODO: Add two more KeyPositions to the KeyFrameSet here -->
<KeyPosition
       motion:framePosition="25"
       motion:motionTarget="@id/moon"
       motion:keyPositionType="parentRelative"
       motion:percentY="0.6"
/>
<KeyPosition
       motion:framePosition="75"
       motion:motionTarget="@id/moon"
       motion:keyPositionType="parentRelative"
       motion:percentY="0.6"
/>

KeyPositions ini akan diterapkan 25% dan 75% selama animasi, dan menyebabkan @id/moon bergerak melalui jalur yang berjarak 60% dari bagian atas layar. Jika digabungkan dengan KeyPosition yang sudah ada sebesar 50%, hal ini akan menciptakan busur yang mulus untuk diikuti bulan.

Di MotionLayout, Anda dapat menambahkan KeyPositions sebanyak yang diperlukan untuk mendapatkan jalur gerakan yang diinginkan. MotionLayout akan menerapkan setiap KeyPosition pada framePosition yang ditentukan, dan mencari tahu cara membuat gerakan halus yang melewati semua KeyPositions.

Coba

  1. Jalankan kembali aplikasi. Buka Langkah 4 untuk melihat cara kerja animasi. Saat Anda mengklik bulan, bulan akan mengikuti jalur dari awal hingga akhir, melewati setiap KeyPosition yang ditentukan dalam KeyFrameSet.

Menjelajahi sendiri

Sebelum melanjutkan ke jenis KeyFrame lainnya, coba tambahkan beberapa KeyPositions lagi ke KeyFrameSet untuk melihat jenis efek yang dapat Anda buat hanya dengan menggunakan KeyPosition.

Berikut adalah salah satu contoh yang menunjukkan cara membuat jalur kompleks yang bergerak maju mundur selama animasi.

cd9faaffde3dfef.png

step4.xml

<!-- Complex paths example: Dancing moon -->
<KeyFrameSet>
   <KeyPosition
           motion:framePosition="25"
           motion:motionTarget="@id/moon"
           motion:keyPositionType="parentRelative"
           motion:percentY="0.6"
           motion:percentX="0.1"
   />
   <KeyPosition
           motion:framePosition="50"
           motion:motionTarget="@id/moon"
           motion:keyPositionType="parentRelative"
           motion:percentY="0.5"
           motion:percentX="0.3"
   />
   <KeyPosition
           motion:framePosition="75"
           motion:motionTarget="@id/moon"
           motion:keyPositionType="parentRelative"
           motion:percentY="0.6"
           motion:percentX="0.1"
   />
</KeyFrameSet>

Setelah selesai menjelajahi KeyPosition, di langkah berikutnya Anda akan mempelajari jenis KeyFrames lainnya.

8. Mengubah atribut selama gerakan

Membangun animasi dinamis sering kali berarti mengubah size, rotation, atau alpha tampilan saat animasi berlangsung. MotionLayout mendukung animasi banyak atribut pada tampilan apa pun menggunakan KeyAttribute.

Pada langkah ini, Anda akan menggunakan KeyAttribute untuk membuat skala dan rotasi bulan. Anda juga akan menggunakan KeyAttribute untuk menunda kemunculan teks hingga bulan hampir menyelesaikan perjalanannya.

Langkah 1: Mengubah ukuran dan memutar dengan KeyAttribute

  1. Buka xml/step5.xml yang berisi animasi yang sama dengan yang Anda buat pada langkah terakhir. Untuk variasi, layar ini menggunakan gambar luar angkasa yang berbeda sebagai latar belakang.
  2. Untuk membuat ukuran bulan bertambah besar dan berputar, tambahkan dua tag KeyAttribute di KeyFrameSet pada keyFrame="50" dan keyFrame="100"

bbae524a2898569.png

step5.xml

<!-- TODO: Add KeyAttributes to rotate and resize @id/moon -->

<KeyAttribute
       motion:framePosition="50"
       motion:motionTarget="@id/moon"
       android:scaleY="2.0"
       android:scaleX="2.0"
       android:rotation="-360"
/>
<KeyAttribute
       motion:framePosition="100"
       motion:motionTarget="@id/moon"
       android:rotation="-720"
/>

KeyAttributes ini diterapkan pada 50% dan 100% animasi. KeyAttribute pertama pada 50% akan terjadi di bagian atas busur, dan menyebabkan tampilan berukuran dua kali lipat serta berputar -360 derajat (atau satu lingkaran penuh). KeyAttribute kedua akan menyelesaikan rotasi kedua ke -720 derajat (dua lingkaran penuh) dan mengecilkan ukuran kembali ke ukuran reguler karena nilai scaleX dan scaleY secara default adalah 1,0.

Sama seperti KeyPosition, KeyAttribute menggunakan framePosition dan motionTarget untuk menentukan kapan KeyFrame diterapkan, dan tampilan mana yang akan diubah. MotionLayout akan melakukan interpolasi antara KeyPositions untuk membuat animasi yang lancar.

Dukungan KeyAttributes atribut yang dapat diterapkan ke semua tampilan. Atribut ini mendukung perubahan atribut dasar seperti visibility, alpha, atau elevation. Anda juga dapat mengubah rotasi seperti yang Anda lakukan di sini, memutar dalam tiga dimensi dengan rotateX dan rotateY, menskalakan ukuran dengan scaleX dan scaleY, atau menerjemahkan posisi tampilan dalam X, Y, atau Z.

Langkah 2: Menunda kemunculan kredit

Salah satu tujuan langkah ini adalah memperbarui animasi sehingga teks kredit tidak muncul hingga animasi hampir selesai.

  1. Untuk menunda kemunculan kredit, tentukan KeyAttribute lain yang memastikan bahwa alpha akan tetap 0 hingga keyPosition="85". MotionLayout akan tetap bertransisi dengan lancar dari alfa 0 hingga 100, tetapi akan melakukannya selama 15% terakhir animasi.

step5.xml

<!-- TODO: Add KeyAttribute to delay the appearance of @id/credits -->

<KeyAttribute
       motion:framePosition="85"
       motion:motionTarget="@id/credits"
       android:alpha="0.0"
/>

KeyAttribute ini mempertahankan alpha @id/credits pada 0,0 untuk 85% pertama animasi. Karena dimulai dengan nilai alfa 0, artinya animasi ini tidak akan terlihat selama 85% pertama durasi animasi.

Efek akhir dari KeyAttribute ini adalah kredit muncul di akhir animasi. Hal ini memberikan kesan bahwa mereka berkoordinasi dengan bulan yang turun di sudut kanan layar.

Dengan menunda animasi pada satu tampilan saat tampilan lain bergerak seperti ini, Anda dapat membuat animasi mengesankan yang terasa dinamis bagi pengguna.

Coba

  1. Jalankan aplikasi lagi dan buka Langkah 5 untuk melihat cara kerja animasi. Saat Anda mengklik bulan, bulan akan mengikuti jalur dari awal hingga akhir, melewati setiap KeyAttribute yang ditentukan dalam KeyFrameSet.

2f4bfdd681c1fa98.gif

Karena Anda memutar bulan dua lingkaran penuh, bulan akan melakukan putaran ganda ke belakang, dan kredit akan ditunda kemunculannya hingga animasi hampir selesai.

Jelajahi sendiri

Sebelum Anda beralih ke jenis KeyFrame terakhir, coba ubah atribut standar lainnya di KeyAttributes. Misalnya, coba ubah rotation menjadi rotationX untuk melihat animasi yang dihasilkan.

Berikut adalah daftar atribut standar yang dapat Anda coba:

  • android:visibility
  • android:alpha
  • android:elevation
  • android:rotation
  • android:rotationX
  • android:rotationY
  • android:scaleX
  • android:scaleY
  • android:translationX
  • android:translationY
  • android:translationZ

9. Mengubah atribut khusus

Animasi kaya melibatkan perubahan warna atau atribut tampilan lainnya. Meskipun MotionLayout dapat menggunakan KeyAttribute untuk mengubah atribut standar yang tercantum dalam tugas sebelumnya, Anda menggunakan CustomAttribute untuk menentukan atribut lainnya.

CustomAttribute dapat digunakan untuk menetapkan nilai apa pun yang memiliki setter. Misalnya, Anda dapat menetapkan backgroundColor pada View menggunakan CustomAttribute. MotionLayout akan menggunakan refleksi untuk menemukan setter, lalu memanggilnya berulang kali untuk menganimasikan tampilan.

Pada langkah ini, Anda akan menggunakan CustomAttribute untuk menetapkan atribut colorFilter pada bulan untuk membuat animasi yang ditunjukkan di bawah.

5fb6792126a09fda.gif

Menentukan atribut kustom

  1. Untuk memulai, buka xml/step6.xml yang berisi animasi yang sama dengan yang Anda buat pada langkah terakhir.
  2. Untuk membuat bulan berubah warna, tambahkan dua KeyAttribute dengan CustomAttribute di KeyFrameSet pada keyFrame="0", keyFrame="50", dan keyFrame="100".

214699d5fdd956da.png

step6.xml

<!-- TODO: Add Custom attributes here -->
<KeyAttribute
       motion:framePosition="0"
       motion:motionTarget="@id/moon">
   <CustomAttribute
           motion:attributeName="colorFilter"
           motion:customColorValue="#FFFFFF"
   />
</KeyAttribute>
<KeyAttribute
       motion:framePosition="50"
       motion:motionTarget="@id/moon">
   <CustomAttribute
           motion:attributeName="colorFilter"
           motion:customColorValue="#FFB612"
   />
</KeyAttribute>
<KeyAttribute
       motion:framePosition="100"
       motion:motionTarget="@id/moon">
   <CustomAttribute
           motion:attributeName="colorFilter"
           motion:customColorValue="#FFFFFF"
   />
</KeyAttribute>

Anda menambahkan CustomAttribute di dalam KeyAttribute. CustomAttribute akan diterapkan pada framePosition yang ditentukan oleh KeyAttribute.

Di dalam CustomAttribute, Anda harus menentukan attributeName dan satu nilai yang akan ditetapkan.

  • motion:attributeName adalah nama setter yang akan dipanggil oleh atribut kustom ini. Dalam contoh ini, setColorFilter di Drawable akan dipanggil.
  • motion:custom*Value adalah nilai kustom dari jenis yang tercantum dalam nama, dalam contoh ini nilai kustom adalah warna yang ditentukan.

Nilai kustom dapat memiliki salah satu jenis berikut:

  • Warna
  • Bilangan bulat
  • Float
  • String
  • Dimensi
  • Boolean

Dengan menggunakan API ini, MotionLayout dapat menganimasikan apa pun yang menyediakan setter pada tampilan apa pun.

Coba

  1. Jalankan aplikasi lagi dan buka Langkah 6 untuk melihat cara kerja animasi. Saat Anda mengklik bulan, bulan akan mengikuti jalur dari awal hingga akhir, melewati setiap KeyAttribute yang ditentukan dalam KeyFrameSet.

5fb6792126a09fda.gif

Saat Anda menambahkan lebih banyak KeyFrames, MotionLayout mengubah jalur bulan dari garis lurus menjadi kurva kompleks, menambahkan salto ganda, pengubahan ukuran, dan perubahan warna di tengah animasi.

Dalam animasi nyata, Anda sering kali menganimasikan beberapa tampilan secara bersamaan, mengontrol gerakannya di sepanjang jalur dan kecepatan yang berbeda. Dengan menentukan KeyFrame yang berbeda untuk setiap tampilan, Anda dapat mengatur koreografi animasi kompleks yang menganimasikan beberapa tampilan dengan MotionLayout.

10. Peristiwa tarik dan jalur kompleks

Pada langkah ini, Anda akan mempelajari cara menggunakan OnSwipe dengan jalur yang kompleks. Sejauh ini, animasi bulan telah dipicu oleh pemroses OnClick dan berjalan selama durasi tetap.

Mengontrol animasi yang memiliki jalur kompleks menggunakan OnSwipe, seperti animasi bulan yang telah Anda buat dalam beberapa langkah terakhir, memerlukan pemahaman tentang cara kerja OnSwipe.

Langkah 1: Jelajahi perilaku OnSwipe

  1. Buka xml/step7.xml dan temukan deklarasi OnSwipe yang ada.

step7.xml

<!-- Fix OnSwipe by changing touchAnchorSide →

<OnSwipe
       motion:touchAnchorId="@id/moon"
       motion:touchAnchorSide="bottom"
/>
  1. Jalankan aplikasi di perangkat Anda dan buka Langkah 7. Lihat apakah Anda dapat menghasilkan animasi yang lancar dengan menarik bulan di sepanjang jalur busur.

Saat Anda menjalankan animasi ini, hasilnya tidak terlalu bagus. Setelah bulan mencapai puncak busur, bulan akan mulai melompat-lompat.

ed96e3674854a548.gif

Untuk memahami bug, pertimbangkan apa yang terjadi saat pengguna menyentuh tepat di bawah bagian atas busur. Karena tag OnSwipe memiliki motion:touchAnchorSide="bottom" MotionLayout akan mencoba membuat jarak antara jari dan bagian bawah tampilan tetap konstan selama animasi.

Namun, karena bagian bawah bulan tidak selalu bergerak ke arah yang sama, ia akan naik lalu turun kembali, MotionLayout tidak tahu apa yang harus dilakukan saat pengguna baru saja melewati bagian atas busur. Untuk mempertimbangkan hal ini, karena Anda melacak bagian bawah bulan, di mana seharusnya bulan ditempatkan saat pengguna menyentuh di sini?

56cd575c5c77eddd.png

Langkah 2: Gunakan sisi kanan

Untuk menghindari bug seperti ini, penting untuk selalu memilih touchAnchorId dan touchAnchorSide yang selalu bergerak ke satu arah selama durasi seluruh animasi.

Dalam animasi ini, sisi right dan sisi left bulan akan bergerak melintasi layar dalam satu arah.

Namun, bottom dan top akan berbalik arah. Saat OnSwipe mencoba melacaknya, OnSwipe akan bingung saat arahnya berubah.

  1. Agar animasi ini mengikuti peristiwa sentuh, ubah touchAnchorSide menjadi right.

step7.xml

<!-- Fix OnSwipe by changing touchAnchorSide →

<OnSwipe
       motion:touchAnchorId="@id/moon"
       motion:touchAnchorSide="right"
/>

Langkah 3: Gunakan dragDirection

Anda juga dapat menggabungkan dragDirection dengan touchAnchorSide untuk membuat jalur samping bergerak ke arah yang berbeda dari biasanya. touchAnchorSide masih penting untuk hanya berjalan dalam satu arah, tetapi Anda dapat memberi tahu MotionLayout arah yang harus dilacak. Misalnya, Anda dapat mempertahankan touchAnchorSide="bottom", tetapi menambahkan dragDirection="dragRight". Tindakan ini akan menyebabkan MotionLayout melacak posisi bagian bawah tampilan, tetapi hanya mempertimbangkan lokasinya saat bergerak ke kanan (mengabaikan gerakan vertikal). Jadi, meskipun bagian bawah naik dan turun, bagian tersebut akan tetap dianimasikan dengan benar menggunakan OnSwipe.

  1. Perbarui OnSwipe untuk melacak pergerakan bulan dengan benar.

step7.xml

<!-- Using dragDirection to control the direction of drag tracking →

<OnSwipe
       motion:touchAnchorId="@id/moon"
       motion:touchAnchorSide="bottom"
       motion:dragDirection="dragRight"
/>

Coba

  1. Jalankan aplikasi lagi dan coba tarik bulan di sepanjang jalur. Meskipun mengikuti alur yang kompleks, MotionLayout akan dapat memproses animasi sebagai respons terhadap peristiwa geser.

5458dff382261427.gif

11. Menjalankan gerakan dengan kode

MotionLayout dapat digunakan untuk membuat animasi yang kaya saat digunakan dengan CoordinatorLayout. Pada langkah ini, Anda akan membuat header yang dapat diciutkan menggunakan MotionLayout.

Langkah 1: Pelajari kode yang ada

  1. Untuk memulai, buka layout/activity_step8.xml.
  2. Di layout/activity_step8.xml, Anda melihat bahwa CoordinatorLayout dan AppBarLayout yang berfungsi sudah dibuat.

activity_step8.xml

<androidx.coordinatorlayout.widget.CoordinatorLayout
       ...>
   <com.google.android.material.appbar.AppBarLayout
           android:id="@+id/appbar_layout"
           android:layout_width="match_parent"
           android:layout_height="180dp">
       <androidx.constraintlayout.motion.widget.MotionLayout
               android:id="@+id/motion_layout"
               ... >
           ...
       </androidx.constraintlayout.motion.widget.MotionLayout>
   </com.google.android.material.appbar.AppBarLayout>
  
   <androidx.core.widget.NestedScrollView
           ...
           motion:layout_behavior="@string/appbar_scrolling_view_behavior" >
           ...
   </androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

Tata letak ini menggunakan CoordinatorLayout untuk membagikan informasi scrolling antara NestedScrollView dan AppBarLayout. Jadi, saat NestedScrollView di-scroll ke atas, NestedScrollView akan memberi tahu AppBarLayout tentang perubahan tersebut. Begitulah cara Anda menerapkan toolbar yang menyusut seperti ini di Android—scroll teks akan "dikoordinasikan" dengan header yang menyusut.

Scene gerakan yang dituju @id/motion_layout mirip dengan scene gerakan di langkah terakhir. Namun, deklarasi OnSwipe dihapus agar dapat berfungsi dengan CoordinatorLayout.

  1. Jalankan aplikasi, lalu buka Langkah 8. Anda akan melihat bahwa saat Anda men-scroll teks, bulan tidak bergerak.

Langkah 2: Membuat MotionLayout dapat di-scroll

  1. Untuk membuat tampilan MotionLayout men-scroll segera setelah NestedScrollView men-scroll, tambahkan motion:minHeight dan motion:layout_scrollFlags ke MotionLayout.

activity_step8.xml

<!-- Add minHeight and layout_scrollFlags to the MotionLayout -->

<androidx.constraintlayout.motion.widget.MotionLayout
       android:id="@+id/motion_layout"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       motion:layoutDescription="@xml/step8"
       motion:motionDebug="SHOW_PATH"
       android:minHeight="80dp"
       motion:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed"  >
  1. Jalankan kembali aplikasi dan buka Langkah 8. Anda akan melihat bahwa MotionLayout menciut saat Anda men-scroll ke atas. Namun, animasi belum berjalan berdasarkan perilaku scroll.

Langkah 3: Menggerakkan gerakan dengan kode

  1. Buka Step8Activity.kt . Edit fungsi coordinateMotion() untuk memberi tahu MotionLayout tentang perubahan posisi scroll.

Step8Activity.kt

// TODO: set progress of MotionLayout based on an AppBarLayout.OnOffsetChangedListener

private fun coordinateMotion() {
    val appBarLayout: AppBarLayout = findViewById(R.id.appbar_layout)
    val motionLayout: MotionLayout = findViewById(R.id.motion_layout)

    val listener = AppBarLayout.OnOffsetChangedListener { unused, verticalOffset ->
        val seekPosition = -verticalOffset / appBarLayout.totalScrollRange.toFloat()
        motionLayout.progress = seekPosition
    }

    appBarLayout.addOnOffsetChangedListener(listener)
}

Kode ini akan mendaftarkan OnOffsetChangedListener yang akan dipanggil setiap kali pengguna men-scroll dengan offset scroll saat ini.

MotionLayout mendukung pencarian transisinya dengan menyetel properti progres. Untuk mengonversi antara verticalOffset dan progres persentase, bagi dengan total rentang scroll.

Coba

  1. Deploy aplikasi lagi dan jalankan animasi Langkah 8. Anda akan melihat bahwa MotionLayout akan memajukan animasi berdasarkan posisi scroll.

ee5ce4d9e33a59ca.gif

Anda dapat membuat animasi toolbar yang menyusut dinamis kustom menggunakan MotionLayout. Dengan menggunakan urutan KeyFrames, Anda dapat mencapai efek yang sangat berani.

12. Selamat

Codelab ini membahas API dasar MotionLayout.

Untuk melihat contoh penerapan MotionLayout lainnya, lihat contoh resmi. Pastikan juga untuk melihat dokumentasi.

Pelajari Lebih Lanjut

MotionLayout mendukung lebih banyak fitur yang tidak dibahas dalam codelab ini, seperti KeyCycle, yang memungkinkan Anda mengontrol jalur atau atribut dengan siklus berulang, dan KeyTimeCycle, yang memungkinkan Anda membuat animasi berdasarkan waktu jam. Lihat contoh untuk melihat contoh masing-masing.

Untuk link ke codelab lain dalam kursus ini, lihat halaman landing codelab Android Lanjutan di Kotlin.