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 mempelajari codelab secara berurutan, tetapi tidak 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. Codelab ini didasarkan pada ConstraintLayout, dan memungkinkan Anda menganimasikan apa pun yang dapat dibangun menggunakan ConstraintLayout.

Anda dapat menggunakan MotionLayout untuk menganimasikan lokasi, ukuran, visibilitas, alfa, warna, ketinggian, rotasi, dan atribut lain dari beberapa tampilan sekaligus. Dengan menggunakan XML deklaratif, Anda bisa 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 perubahan status memungkinkan pengguna melacak perubahan di UI Anda secara alami.
  • Menarik perhatian—gunakan animasi untuk menarik perhatian ke elemen UI yang penting.
  • Buat desain yang indah—gerakan yang efektif dalam desain membuat aplikasi terlihat bagus.

Prasyarat

Codelab ini dirancang untuk developer yang memiliki 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 Tata Letak Batasan untuk mempelajari ConstraintLayout lebih lanjut.

Yang akan Anda lakukan

  • Menentukan animasi dengan ConstraintSets dan MotionLayout
  • Menganimasikan berdasarkan peristiwa tarik
  • Ubah animasi dengan KeyPosition
  • Ubah 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 versi Android Studio 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 ujung bawah sebagai respons terhadap klik pengguna.

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

  • MotionLayout, yang merupakan subclass 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 setiap elemen ini satu per satu, dimulai dengan MotionLayout.

Langkah 1: Pelajari kode yang ada

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

  1. Di res/layout, buka activity_step1.xml. Di sini Anda memiliki ConstraintLayout dengan satu ImageView bintang, dengan tint 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 menjalankan aplikasi sekarang, Anda akan melihat bintang ditampilkan tanpa batasan, yang berarti bintang tersebut akan diposisikan di lokasi yang tidak diketahui. Android Studio akan memberikan peringatan tentang tidak adanya batasan.

Langkah 2: Konversi ke Tata Letak Bergerak

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

Agar tata letak Anda dapat menggunakan adegan gerakan, tata letak harus mengarah ke sana.

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

a2beea710c2decb7.png

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

4fa936a98a8393b9.pngS

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">

Adegan gerakan adalah satu file XML yang menjelaskan animasi dalam MotionLayout.

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

66d0e80d5ab4daf8.pngS

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. Dalam gambar ini, informasi start ConstraintSet ditampilkan di jendela pilihan.
  3. Atribut – Panel atribut ditampilkan dan memungkinkan Anda mengedit atribut item yang dipilih saat ini dari ringkasan atau jendela pilihan. Dalam gambar ini, atribut untuk start ConstraintSet ditampilkan.

Langkah 3: Tentukan batasan awal dan akhir

Semua animasi dapat ditentukan dalam bentuk awal dan akhir. Bagian awal menggambarkan tampilan layar sebelum animasi, dan bagian 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 termasuk batasan lebar, tinggi, dan ConstraintLayout. Ini juga mencakup beberapa atribut seperti alpha. Kolom ini tidak berisi tampilan itu sendiri, hanya batasan pada tampilan tersebut.

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

Pada langkah ini, Anda akan membatasi tampilan bintang untuk dimulai dari awal atas layar, dan finish di ujung bawah layar.

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

  1. Pilih start ConstraintSet di panel ringkasan

6e57661ed358b860.pngS

  1. Di panel selection, pilih red_star. Saat ini, contoh ini menampilkan Sumber layout – artinya, kode tidak dibatasi dalam ConstraintSet ini. Gunakan ikon pensil di kanan atas untuk Create Constraint

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 Constraint di bagian atas dan mulailah dengan mengklik tombol + berwarna biru.

2fce076cd7b04bd.pngS

  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, fungsi tersebut hanya memerlukan satu Constraint.

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

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

  1. Pilih end ConstraintSet di panel ringkasan.

346e1248639b6f1e.pngS

  1. Ikuti langkah yang sama seperti yang Anda lakukan 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 + biru.

fd33c779ff83c80a.png

  1. Kode dalam XML 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, fungsi ini membatasinya ke bagian ujung bawah layar.

Anda tidak perlu menamainya @id/start dan @id/end, tetapi lebih mudah untuk melakukannya.

Langkah 4: Tentukan transisi

Setiap MotionScene juga harus menyertakan minimal satu transisi. Transisi mendefinisikan setiap bagian dari sebuah animasi, dari awal hingga akhir.

Transisi harus menentukan ConstraintSet awal dan akhir untuk transisi. Transisi juga dapat menentukan cara memodifikasi animasi dengan cara lain, seperti berapa lama animasi akan dijalankan 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 dibuat.

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>

Ini adalah semua yang diperlukan MotionLayout untuk membuat animasi. Dengan melihat setiap atribut:

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

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

Langkah 5: Lihat pratinjau animasi di Motion Editor

dff9ecdc1f4a0740.gif

Animasi: Video memutar pratinjau transisi di Motion Editor

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

1dc541ae8c43b250.pngS

  1. Panel selection menampilkan kontrol pemutaran dan scrub bar saat transisi dipilih. Klik putar atau tarik posisi saat ini untuk melihat pratinjau animasi.

a0fd2593384dfb36.png

Langkah 6: Menambahkan pengendali saat klik

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

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

b6f94b344ce65290.png

  1. Klik 699f7ae04024ccf6.pngS Buat pengendali klik atau geser di toolbar untuk panel ringkasan . Tindakan ini akan menambahkan pengendali yang akan memulai transisi.
  2. Pilih Pengendali Klik dari pop-up tersebut

ccf92d06335105fe.png

  1. Ubah View To Click menjadi red_star.

b0d3f0c970604f01.png

  1. Klik Add untuk pengendali klik dilambangkan dengan titik kecil pada Transition in Motion Editor.

cec3913e67fb4105.png

  1. Dengan transisi yang 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 dibuat 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>. Dengan 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 dalam dokumentasi.
  1. Jalankan kode, klik Langkah 1, lalu klik bintang merah dan lihat animasinya.

Langkah 5: Penerapan animasi

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

7ba88af963fdfe10.gif

File suasana gerakan yang sudah selesai menentukan satu Transition yang mengarah ke ConstraintSet awal dan akhir.

Di awal animasi (@id/start), ikon bintang dibatasi hingga bagian awal atas layar. Di akhir animasi (@id/end), ikon bintang dibatasi hingga 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. Menganimasikan berdasarkan peristiwa tarik

Untuk langkah ini, Anda akan membangun animasi yang merespons peristiwa tarik pengguna (saat pengguna menggeser layar) untuk menjalankan animasi. MotionLayout mendukung pelacakan peristiwa sentuh untuk memindahkan tampilan, serta gestur ayunkan jari berbasis fisika untuk membuat gerakan menjadi lancar.

Langkah 1: Periksa kode awal

  1. Untuk memulai, buka file tata letak activity_step2.xml yang sudah memiliki MotionLayout. 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 mendefinisikan semua tampilan untuk animasi. Ikon tiga bintang tidak dibatasi dalam tata letak karena akan dianimasikan dalam tampilan gerakan.

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

Langkah 2: Menganimasikan suasana

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

Menentukan ConstraintSet awal

  1. Buka scene gerakan xml/step2.xml untuk menentukan animasi.
  2. Tambahkan batasan untuk batasan awal start. Di bagian awal, 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 ditempatkan di tengah bagian bawah layar menggunakan batasan awal, akhir, dan bawah. Dua bintang @id/left_star dan @id/right_star memiliki nilai alfa tambahan yang membuatnya tidak terlihat dan akan diterapkan di awal animasi.

Kumpulan batasan start dan end menentukan awal dan akhir animasi. Batasan di awal, seperti motion:layout_constraintStart_toStartOf akan membatasi awal tampilan ke awal tampilan lainnya. Pada awalnya, hal ini dapat membingungkan karena nama start digunakan untuk dan keduanya digunakan dalam konteks batasan. Untuk membantu membedakannya, start di layout_constraintStart mengacu pada "awal" tampilan, yakni bahasa kiri dalam bahasa kiri ke kanan dan kanan dalam bahasa kanan ke kiri. Kumpulan batasan start mengacu pada awal animasi.

Menentukan ConstraintSet akhir

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

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 disetel pada @id/right_start dan @id/left_star di kedua ConstraintSets, kedua tampilan akan memudar saat animasi berlangsung.

Menganimasikan berdasarkan gestur geser pengguna

MotionLayout dapat melacak peristiwa tarik pengguna, atau geser, untuk membuat "fling" berbasis fisika animasi. Ini berarti tampilan akan terus berjalan jika pengguna melemparkannya 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 yang bergerak saat merespons sentuhan. MotionLayout akan membuat tampilan ini tetap dalam jarak yang sama dari jari yang menggeser.
  • touchAnchorSide menentukan sisi tampilan mana yang harus dilacak. Hal ini penting untuk tampilan yang mengubah ukuran, mengikuti jalur kompleks, atau memiliki satu sisi yang bergerak lebih cepat dari yang lain.
  • dragDirection menentukan arah yang penting untuk animasi ini (atas, bawah, kiri, atau kanan).

Saat MotionLayout memproses peristiwa tarik, pemroses akan didaftarkan pada tampilan MotionLayout, bukan tampilan yang ditentukan oleh touchAnchorId. Saat pengguna memulai gestur di mana saja pada layar, MotionLayout akan menjaga jarak antara jari mereka dan touchAnchorSide tampilan touchAnchorId konstan. Misalnya, jika menyentuh 100 dp dari sisi anchor, MotionLayout akan menjauhkan sisi tersebut 100 dp dari jarinya untuk seluruh animasi.

Cobalah

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

fefcdd690a0dcaec.gif

MotionLayout dapat menganimasikan antara 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. Di bagian akhir, tiga tampilan diposisikan relatif terhadap @id/credits dalam sebuah rantai.

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

5. Mengubah jalur

Pada langkah ini, Anda akan membangun animasi yang mengikuti jalur kompleks selama animasi dan menganimasikan kredit selama gerakan. MotionLayout dapat mengubah jalur yang akan diambil 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 bulan dan teks kredit.
  2. Buka file adegan gerakan (xml/step3.xml). Anda melihat bahwa Transition dari @id/start hingga @id/end telah ditentukan. Animasi ini memindahkan gambar bulan dari kiri bawah layar ke kanan bawah layar menggunakan dua ConstraintSets. Teks kredit perlahan 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 proses debug jalur

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

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

  1. Untuk mengaktifkan jalur proses debug, 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 proses debug jalur, saat menjalankan aplikasi lagi, Anda akan melihat jalur semua tampilan yang divisualisasikan dengan garis putus-putus.

23bbb604f456f65c.pngS

  • Lingkaran mewakili posisi awal atau akhir dari 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 awal dan ConstraintSet 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 mengubah posisi.

Untuk mem-build jalur kompleks seperti busur bulan dalam contoh ini, MotionLayout menggunakan KeyPosition untuk mengubah jalur yang diambil oleh tampilan di antara titik awal dan akhir.

  1. Buka xml/step3.xml dan tambahkan KeyPosition ke scene. 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.

Karena MotionLayout menghitung jalur bulan antara titik awal dan akhir, jalur tersebut akan diubah berdasarkan KeyPosition yang ditentukan dalam KeyFrameSet. Anda dapat melihat bagaimana hal ini mengubah jalur dengan menjalankan aplikasi lagi.

KeyPosition memiliki beberapa atribut yang menjelaskan caranya mengubah jalur. Yang paling penting adalah:

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

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

Secara default, MotionLayout akan membulatkan setiap sudut yang diperkenalkan dengan mengubah jalur. Jika melihat animasi yang baru saja dibuat, 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 animasi), ubah jalur motionTarget @id/moon dengan memindahkannya sejauh 50% Y (setengah ke bawah layar) sesuai dengan koordinat yang ditentukan oleh parentRelative (seluruh MotionLayout)."

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

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

1c7cf779931e45cc.gif

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

Ternyata, meskipun Anda memodifikasi jalur yang diambil 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 akhir bulan.

Jika ingin kredit bergerak mengikuti bulan, Anda dapat menambahkan KeyPosition ke kredit, atau mengubah batasan awal di @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 di ruang. Mereka juga berguna untuk menggambarkan posisi pada layar.

Sistem koordinat MotionLayout adalah sistem koordinat kartesius. Ini berarti, plot itu memiliki sumbu X dan Y yang ditentukan oleh dua garis tegak lurus. Perbedaan utama di antara mereka adalah posisi sumbu X di layar (sumbu Y selalu tegak lurus terhadap sumbu X).

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

Jika semua itu terdengar terlalu mirip dengan kelas Aljabar, lihat gambar di bawah ini.

Koordinat relatif induk

a7b7568d46d9dec7.png

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

Anda dapat menggunakan parentRelative setiap kali ingin membuat animasi yang bergerak melalui 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 deltaRelative

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 pada layar, dan sumbu Y selalu vertikal pada layar. Dibandingkan dengan parentRelative, perbedaan utamanya adalah koordinat hanya mendeskripsikan bagian layar tempat tampilan akan digerakkan.

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

Koordinat pathRelative

f3aaadaac8b4a93f.png

Sistem koordinat terakhir di MotionLayout adalah pathRelative. Hal ini sangat 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 ini? Ini cukup mengejutkan pada pandangan pertama, terutama karena sistem koordinat ini bahkan tidak diselaraskan dengan sistem koordinat layar.

Ternyata pathRelative sangat berguna untuk beberapa hal.

  • Mempercepat, memperlambat, atau menghentikan tampilan selama bagian animasi. Karena dimensi X akan selalu sama persis dengan jalur yang diambil tampilan, Anda dapat menggunakan pathRelative KeyPosition untuk mengubah framePosition titik tertentu dalam jalur tersebut yang dicapai. Jadi, KeyPosition pada framePosition="50" dengan percentX="0.1" akan menyebabkan animasi memerlukan 50% waktu untuk melakukan perjalanan 10% pertama gerakan.
  • Menambahkan busur halus ke jalur. Karena dimensi Y selalu tegak lurus terhadap gerak, mengubah Y akan mengubah jalur ke kurva relatif terhadap keseluruhan gerak.
  • Menambahkan dimensi kedua jika 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.

Pada 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 pada langkah terakhir, animasi itu memang menghasilkan kurva yang halus, tetapi bentuknya bisa lebih "seperti bulan".

Mengubah jalur dengan beberapa elemen KeyPosition

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

  1. Buka xml/step4.xml. Anda melihatnya memiliki tampilan yang sama dan KeyFrame yang Anda tambahkan di langkah terakhir.
  2. Untuk membulatkan bagian atas kurva, tambahkan dua KeyPositions lagi ke jalur @id/moon, satu tepat sebelum mencapai bagian atas, dan satu setelahnya.

500b5ac2db48ef87.pngS

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% sepanjang animasi, dan menyebabkan @id/moon bergerak melalui jalur yang 60% dari bagian atas layar. Dikombinasikan dengan KeyPosition yang ada sebesar 50%, gerakan ini akan membuat busur halus untuk diikuti oleh bulan.

Di MotionLayout, Anda dapat menambahkan KeyPositions sebanyak yang Anda perlukan untuk mendapatkan jalur gerakan yang Anda inginkan. 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. Lanjutkan ke Langkah 4 untuk melihat cara kerja animasi. Saat diklik, bulan akan mengikuti jalur dari awal hingga akhir, melalui setiap KeyPosition yang ditentukan dalam KeyFrameSet.

Jelajahi sendiri

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

Berikut ini satu contoh yang menunjukkan cara membuat jalur kompleks yang bergerak bolak-balik 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, pada langkah berikutnya Anda akan beralih ke jenis KeyFrames lainnya.

8. Mengubah atribut selama gerakan

Membuat animasi dinamis sering kali berarti mengubah tampilan size, rotation, atau alpha saat animasi berlangsung. MotionLayout mendukung animasi banyak atribut di tampilan mana pun menggunakan KeyAttribute.

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

Langkah 1: Ubah ukuran dan rotasi dengan KeyAttribute

  1. Buka xml/step5.xml yang berisi animasi yang sama dengan yang Anda buat di langkah terakhir. Untuk variasi, layar ini menggunakan gambar ruang yang berbeda sebagai latar belakang.
  2. Agar bulan bertambah besar dan berputar, tambahkan dua tag KeyAttribute dalam 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 sebesar 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 ukurannya kembali ke reguler karena nilai scaleX dan scaleY secara default adalah 1,0.

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

KeyAttributes mendukung atribut yang dapat diterapkan ke semua tampilan. Fungsi 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 di X, Y, atau Z.

Langkah 2: Tunda tampilan kredit

Salah satu tujuan dari langkah ini adalah untuk memperbarui animasi sehingga teks kredit tidak muncul sampai sebagian besar animasi selesai.

  1. Untuk menunda kemunculan kredit, tentukan satu KeyAttribute lagi yang memastikan bahwa alpha akan tetap 0 hingga keyPosition="85". MotionLayout akan tetap bertransisi dengan lancar dari 0 ke 100 alfa, tetapi akan melakukannya pada 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 dari @id/credits pada 0,0 untuk 85% animasi pertama. Karena dimulai pada alfa 0, ini berarti animasi tidak akan terlihat untuk 85% pertama animasi.

Efek akhir KeyAttribute ini adalah kredit muncul di akhir animasi. Hal ini membuat gambar terlihat berkoordinasi dengan bulan yang berada di sudut kanan layar.

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

Coba

  1. Jalankan kembali aplikasi dan lanjutkan ke Langkah 5 untuk melihat cara kerja animasi. Saat diklik, bulan akan mengikuti jalur dari awal hingga akhir, melewati setiap KeyAttribute yang ditentukan dalam KeyFrameSet.

2f4bfdd681c1fa98.gif

Karena Anda memutar bulan dua lingkaran penuh, sekarang ini akan melakukan membalik ke belakang ganda, dan kredit akan menunda penampilannya hingga animasi hampir selesai.

Jelajahi secara mandiri

Sebelum Anda melanjutkan ke jenis akhir KeyFrame, coba ubah atribut standar lainnya di KeyAttributes. Misalnya, coba ubah rotation menjadi rotationX untuk melihat animasi yang dihasilkannya.

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 yang kaya meliputi perubahan warna atau atribut lain dari sebuah tampilan. Meskipun MotionLayout dapat menggunakan KeyAttribute untuk mengubah atribut standar apa pun yang tercantum di tugas sebelumnya, Anda menggunakan CustomAttribute untuk menentukan atribut lainnya.

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

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

5fb6792126a09fda.gif

Menentukan atribut khusus

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

214699d5fdd956da.pngS

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 untuk ditetapkan.

  • motion:attributeName adalah nama penyetel yang akan dipanggil oleh atribut khusus ini. Dalam contoh ini, setColorFilter di Drawable akan dipanggil.
  • motion:custom*Value adalah nilai kustom dari jenis yang disebutkan 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 penyetel di tampilan apa pun.

Coba

  1. Jalankan kembali aplikasi dan lanjutkan ke Langkah 6 untuk melihat cara kerja animasi. Saat diklik, bulan akan mengikuti jalur dari awal hingga akhir, melewati setiap KeyAttribute yang ditentukan dalam KeyFrameSet.

5fb6792126a09fda.gif

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

Dalam animasi nyata, Anda akan sering menganimasikan beberapa tampilan sekaligus mengontrol gerakannya di sepanjang jalur dan kecepatan yang berbeda. Dengan menentukan KeyFrame yang berbeda untuk setiap tampilan, Anda dapat mengkoreografikan animasi kaya yang menganimasikan beberapa tampilan dengan MotionLayout.

10. Peristiwa tarik dan jalur kompleks

Pada langkah ini, Anda akan mempelajari 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: Pelajari 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, lalu lanjutkan ke Langkah 7. Lihat apakah Anda dapat menghasilkan animasi yang mulus dengan menarik bulan di sepanjang jalur busur.

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

ed96e3674854a548.gif

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

Tapi, karena bagian bawah bulan tidak selalu mengarah ke arah yang sama, maka bulan naik lalu kembali turun, 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 harus ditempatkan saat pengguna menyentuh di sini?

56cd575c5c77eddd.pngS

Langkah 2: Gunakan sisi kanan

Untuk menghindari bug seperti ini, Anda harus selalu memilih touchAnchorId dan touchAnchorSide yang selalu berjalan ke satu arah di sepanjang durasi seluruh animasi.

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

Namun, bottom dan top akan membalikkan arah. Ketika OnSwipe mencoba melacaknya, ia akan bingung ketika arahnya berubah.

  1. Untuk membuat animasi ini, ikuti 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 trek samping ke arah yang berbeda dari biasanya. touchAnchorSide tetap harus berjalan dalam satu arah saja, tetapi Anda dapat memberi tahu MotionLayout arah mana yang harus dilacak. Misalnya, Anda dapat mempertahankan touchAnchorSide="bottom", tetapi menambahkan dragDirection="dragRight". Ini akan menyebabkan MotionLayout melacak posisi bagian bawah tampilan, tetapi hanya mempertimbangkan lokasinya saat bergerak ke kanan (ini mengabaikan gerakan vertikal). Jadi, meskipun bagian bawah naik dan turun, elemen ini akan tetap dianimasikan dengan benar menggunakan OnSwipe.

  1. Perbarui OnSwipe untuk melacak gerakan 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 lagi aplikasi dan coba tarik bulan melalui seluruh jalur. Meskipun mengikuti alur yang kompleks, MotionLayout akan dapat melanjutkan 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 berbagi informasi scroll antara NestedScrollView dan AppBarLayout. Jadi, saat di-scroll ke atas, NestedScrollView akan memberi tahu AppBarLayout tentang perubahan tersebut. Begitulah cara Anda menerapkan toolbar yang diciutkan seperti ini di Android—scroll teks akan "dikoordinasikan" dengan {i>header<i} yang menyusut.

Suasana gerakan yang ditunjuk @id/motion_layout mirip dengan adegan gerakan di langkah terakhir. Namun, deklarasi OnSwipe dihapus agar dapat berfungsi dengan CoordinatorLayout.

  1. Jalankan aplikasi dan lanjutkan ke Langkah 8. Anda melihat bahwa ketika Anda menggulir teks, bulan tidak bergerak.

Langkah 2: Scroll MotionLayout

  1. Untuk membuat tampilan MotionLayout di-scroll segera setelah NestedScrollView di-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 lanjutkan ke Langkah 8. Anda akan melihat bahwa MotionLayout akan diciutkan saat Anda men-scroll ke atas. Namun, animasi belum berkembang berdasarkan perilaku scroll.

Langkah 3: Memindahkan gerakan dengan kode

  1. Buka Step8Activity.kt . Edit fungsi coordinateMotion() untuk memberi tahu MotionLayout tentang perubahan pada 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 transisi 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 melanjutkan animasi berdasarkan posisi scroll.

ee5ce4d9e33a59ca.gif

Anda dapat membuat animasi toolbar diciutkan 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 praktik MotionLayout lainnya, lihat contoh resmi. Dan pastikan untuk membaca dokumentasi.

Pelajari Lebih Lanjut

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

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