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 mempelajariConstraintLayout
lebih lanjut.
Yang akan Anda lakukan
- Menentukan animasi dengan
ConstraintSets
danMotionLayout
- 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 subclassConstraintLayout
. Anda menentukan semua tampilan yang akan dianimasikan di dalam tagMotionLayout
.MotionScene,
yang merupakan file XML yang menjelaskan animasi untukMotionLayout.
Transition,
yang merupakan bagian dariMotionScene
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.
- Di
res/layout
, bukaactivity_step1.xml.
Di sini Anda memilikiConstraintLayout
dengan satuImageView
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.
- 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.
- Setelah membuka platform desain, klik kanan pratinjau, lalu pilih Convert to MotionLayout.
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
Ada tiga elemen UI baru di Motion Editor:
- Ringkasan – Ini adalah pilihan modal yang memungkinkan Anda memilih berbagai bagian animasi. Dalam gambar ini,
start
ConstraintSet
dipilih. Anda juga dapat memilih transisi antarastart
danend
dengan mengklik panah di antara keduanya. - 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. - 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.
- Pilih
start
ConstraintSet di panel ringkasan
- Di panel selection, pilih
red_star
. Saat ini, contoh ini menampilkan Sumberlayout
– artinya, kode tidak dibatasi dalamConstraintSet
ini. Gunakan ikon pensil di kanan atas untuk Create Constraint
- Pastikan
red_star
menampilkan Sumberstart
saatstart
ConstraintSet
dipilih di panel ringkasan. - Di panel Attributes, dengan
red_star
dipilih distart
ConstraintSet
, tambahkan Constraint di bagian atas dan mulailah dengan mengklik tombol + berwarna biru.
- 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.
- Pilih
end
ConstraintSet di panel ringkasan.
- Ikuti langkah yang sama seperti yang Anda lakukan sebelumnya untuk menambahkan
Constraint
untukred_star
diend
ConstraintSet
. - Untuk menggunakan Motion Editor guna menyelesaikan langkah ini, tambahkan batasan ke
bottom
danend
dengan mengklik tombol + biru.
- 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.
- 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
Animasi: Video memutar pratinjau transisi di Motion Editor
- Buka Motion Editor dan pilih transisi dengan mengklik panah antara
start
danend
di panel ringkasan.
- Panel selection menampilkan kontrol pemutaran dan scrub bar saat transisi dipilih. Klik putar atau tarik posisi saat ini untuk melihat pratinjau animasi.
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
.
- Buka editor gerakan dan pilih transisi dengan mengklik panah antara awal dan akhir di panel ringkasan.
- Klik Buat pengendali klik atau geser di toolbar untuk panel ringkasan . Tindakan ini akan menambahkan pengendali yang akan memulai transisi.
- Pilih Pengendali Klik dari pop-up tersebut
- Ubah View To Click menjadi
red_star
.
- Klik Add untuk pengendali klik dilambangkan dengan titik kecil pada Transition in Motion Editor.
- Dengan transisi yang dipilih di panel ringkasan, tambahkan atribut
clickAction
daritoggle
ke pengendali OnClick yang baru saja Anda tambahkan di panel atribut.
- 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
daritoggle
akan beralih antara status awal dan akhir saat diklik. Anda dapat melihat opsi lain untukclickAction
dalam dokumentasi.
- 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.
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
- Untuk memulai, buka file tata letak
activity_step2.xml
yang sudah memilikiMotionLayout
. 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
- Buka scene gerakan
xml/step2.xml
untuk menentukan animasi. - Tambahkan batasan untuk batasan awal
start
. Di bagian awal, ketiga bintang berada di tengah bagian bawah layar. Bintang kanan dan kiri memiliki nilaialpha
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
- Tentukan batasan akhir untuk menggunakan rantai untuk memosisikan ketiga bintang bersama-sama di bawah
@id/credits
. Selain itu, nilai akhiralpha
bintang kiri dan kanan ke1.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
.
- 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
- Jalankan kembali aplikasi, lalu buka layar Langkah 2. Anda akan melihat animasinya.
- Coba "ayunkan" atau melepaskan jari Anda di tengah-tengah animasi untuk mempelajari cara
MotionLayout
menampilkan animasi berbasis fisika yang lancar.
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
- Buka
layout/activity_step3.xml
danxml/step3.xml
untuk melihat tata letak dan adegan gerakan yang ada.ImageView
danTextView
menampilkan bulan dan teks kredit. - Buka file adegan gerakan (
xml/step3.xml
). Anda melihat bahwaTransition
dari@id/start
hingga@id/end
telah ditentukan. Animasi ini memindahkan gambar bulan dari kiri bawah layar ke kanan bawah layar menggunakan duaConstraintSets
. Teks kredit perlahan memudar darialpha="0.0"
menjadialpha="1.0"
saat bulan bergerak. - 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.
- Untuk mengaktifkan jalur proses debug, buka
layout/activity_step3.xml
dan tambahkanmotion:motionDebug="SHOW_PATH"
ke tagMotionLayout
.
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.
- 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.
- Buka
xml/step3.xml
dan tambahkanKeyPosition
ke scene. TagKeyPosition
ditempatkan di dalam tagTransition
.
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 tempatKeyPosition
ini mengubah jalurnya.keyPositionType
adalah caraKeyPosition
ini mengubah jalur tersebut. Token tersebut dapat berupaparentRelative
,pathRelative
, ataudeltaRelative
(seperti yang dijelaskan pada langkah berikutnya).percentX | percentY
adalah seberapa banyak perubahan jalur diframePosition
(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.
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?
<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
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
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
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 mengubahframePosition
titik tertentu dalam jalur tersebut yang dicapai. Jadi,KeyPosition
padaframePosition="50"
denganpercentX="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.
- Buka
xml/step4.xml
. Anda melihatnya memiliki tampilan yang sama danKeyFrame
yang Anda tambahkan di langkah terakhir. - Untuk membulatkan bagian atas kurva, tambahkan dua
KeyPositions
lagi ke jalur@id/moon
, satu tepat sebelum mencapai bagian atas, dan satu setelahnya.
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
- 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 dalamKeyFrameSet
.
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.
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
- 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. - Agar bulan bertambah besar dan berputar, tambahkan dua tag
KeyAttribute
dalamKeyFrameSet
padakeyFrame="50"
dankeyFrame="100"
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.
- Untuk menunda kemunculan kredit, tentukan satu
KeyAttribute
lagi yang memastikan bahwaalpha
akan tetap 0 hinggakeyPosition="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
- 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 dalamKeyFrameSet
.
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.
Menentukan atribut khusus
- Untuk memulai, buka
xml/step6.xml
yang berisi animasi yang sama dengan yang Anda bangun di langkah terakhir. - Untuk membuat bulan berubah warna, tambahkan dua
KeyAttribute
denganCustomAttribute
diKeyFrameSet
padakeyFrame="0"
,keyFrame="50"
, dankeyFrame="100".
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
diDrawable
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
- 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 dalamKeyFrameSet
.
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
- Buka
xml/step7.xml
dan temukan deklarasiOnSwipe
yang ada.
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="bottom"
/>
- 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.
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?
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.
- Untuk membuat animasi ini, ikuti peristiwa sentuh, ubah
touchAnchorSide
menjadiright
.
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
.
- 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
- 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.
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
- Untuk memulai, buka
layout/activity_step8.xml
. - Di
layout/activity_step8.xml
, Anda melihat bahwaCoordinatorLayout
danAppBarLayout
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
.
- Jalankan aplikasi dan lanjutkan ke Langkah 8. Anda melihat bahwa ketika Anda menggulir teks, bulan tidak bergerak.
Langkah 2: Scroll MotionLayout
- Untuk membuat tampilan
MotionLayout
di-scroll segera setelahNestedScrollView
di-scroll, tambahkanmotion:minHeight
danmotion:layout_scrollFlags
keMotionLayout
.
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" >
- 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
- Buka
Step8Activity.kt
. Edit fungsicoordinateMotion()
untuk memberi tahuMotionLayout
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
- Deploy aplikasi lagi dan jalankan animasi Langkah 8. Anda akan melihat bahwa
MotionLayout
akan melanjutkan animasi berdasarkan posisi scroll.
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.