Saat aplikasi Anda digunakan, informasi baru akan muncul di layar dan informasi lama akan dihapus. Mengubah tampilan di layar secara langsung dapat mengganggu, dan pengguna dapat melewatkan konten baru yang muncul tiba-tiba. Animasi memperlambat perubahan dan menarik perhatian pengguna dengan gerakan sehingga pembaruan lebih jelas.
.Ada tiga animasi umum yang dapat Anda gunakan untuk menampilkan atau menyembunyikan tampilan: animasi reveal, animasi crossfade, dan animasi cardflip.
Membuat animasi crossfade
Animasi crossfade—juga dikenal sebagai dissolve—secara bertahap memudarkan
satu View atau
ViewGroup, lalu memperjelas yang lain secara bersamaan. Animasi ini berguna ketika Anda ingin
beralih ke konten atau tampilan lain di aplikasi Anda. Animasi crossfade yang ditunjukkan di sini menggunakan
ViewPropertyAnimator,
yang tersedia untuk Android 3.1 (API level 12) dan yang lebih tinggi.
Berikut adalah contoh crossfade dari indikator progres ke konten teks:
Membuat tampilan
Buat dua tampilan yang ingin di-crossfade. Contoh berikut akan membuat indikator progres dan tampilan teks yang dapat di-scroll:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView style="?android:textAppearanceMedium"
android:lineSpacingMultiplier="1.2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lorem_ipsum"
android:padding="16dp" />
</ScrollView>
<ProgressBar android:id="@+id/loading_spinner"
style="?android:progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</FrameLayout>
Menyiapkan animasi crossfade
Untuk menyiapkan animasi crossfade, lakukan hal berikut:
- Buat variabel anggota untuk tampilan yang ingin di-crossfade. Anda memerlukan referensi ini nanti saat memodifikasi tampilan selama animasi berjalan.
- Tetapkan visibilitas tampilan yang akan diperjelas ke
GONE. Cara ini mencegah tampilan tersebut menggunakan ruang tata letak dan menghilangkannya dari penghitungan tata letak, sehingga pemrosesan akan menjadi lebih cepat - Cache properti sistem
config_shortAnimTimedalam variabel anggota. Properti ini menentukan durasi "pendek" standar untuk animasi. Durasi ini ideal untuk animasi halus atau animasi yang sering diputar.config_longAnimTimedanconfig_mediumAnimTimejuga tersedia.
Berikut adalah contoh penggunaan tata letak dari cuplikan kode sebelumnya sebagai tampilan konten aktivitas:
Kotlin
class CrossfadeActivity : Activity() { private lateinit var contentView: View private lateinit var loadingView: View private var shortAnimationDuration: Int = 0 ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_crossfade) contentView = findViewById(R.id.content) loadingView = findViewById(R.id.loading_spinner) // Initially hide the content view. contentView.visibility = View.GONE // Retrieve and cache the system's default "short" animation time. shortAnimationDuration = resources.getInteger(android.R.integer.config_shortAnimTime) } ... }
Java
public class CrossfadeActivity extends Activity { private View contentView; private View loadingView; private int shortAnimationDuration; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_crossfade); contentView = findViewById(R.id.content); loadingView = findViewById(R.id.loading_spinner); // Initially hide the content view. contentView.setVisibility(View.GONE); // Retrieve and cache the system's default "short" animation time. shortAnimationDuration = getResources().getInteger( android.R.integer.config_shortAnimTime); } ... }
Meng-crossfade tampilan
Setelah tampilan disiapkan dengan benar, tambahkan fungsi crossfade dengan cara berikut:
- Untuk tampilan yang diperjelas, tetapkan nilai alfanya ke 0 dan visibilitasnya
ke
VISIBLEdari setelan awalnyaGONE. Ini membuat tampilan terlihat tetapi transparan. - Untuk tampilan yang diperjelas, animasikan nilai alfanya dari 0 menjadi 1. Untuk tampilan yang dipudarkan, animasikan nilai alfanya dari 1 menjadi 0.
- Menggunakan
onAnimationEnd()dalamAnimator.AnimatorListener, tetapkan visibilitas tampilan yang dipudarkan menjadiGONE. Meskipun nilai alfanya adalah 0, menetapkan visibilitas tampilan ini menjadiGONEakan mencegah tampilan memenuhi ruang tata letak dan menghilangkannya dari penghitungan tata letak, sehingga pemrosesan akan menjadi lebih cepat.
Metode berikut menunjukkan contoh cara melakukannya:
Kotlin
class CrossfadeActivity : Activity() { private lateinit var contentView: View private lateinit var loadingView: View private var shortAnimationDuration: Int = 0 ... private fun crossfade() { contentView.apply { // Set the content view to 0% opacity but visible, so that it is // visible but fully transparent during the animation. alpha = 0f visibility = View.VISIBLE // Animate the content view to 100% opacity and clear any animation // listener set on the view. animate() .alpha(1f) .setDuration(shortAnimationDuration.toLong()) .setListener(null) } // Animate the loading view to 0% opacity. After the animation ends, // set its visibility to GONE as an optimization step so it doesn't // participate in layout passes. loadingView.animate() .alpha(0f) .setDuration(shortAnimationDuration.toLong()) .setListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { loadingView.visibility = View.GONE } }) } }
Java
public class CrossfadeActivity extends Activity { private View contentView; private View loadingView; private int shortAnimationDuration; ... private void crossfade() { // Set the content view to 0% opacity but visible, so that it is // visible but fully transparent during the animation. contentView.setAlpha(0f); contentView.setVisibility(View.VISIBLE); // Animate the content view to 100% opacity and clear any animation // listener set on the view. contentView.animate() .alpha(1f) .setDuration(shortAnimationDuration) .setListener(null); // Animate the loading view to 0% opacity. After the animation ends, // set its visibility to GONE as an optimization step so it doesn't // participate in layout passes. loadingView.animate() .alpha(0f) .setDuration(shortAnimationDuration) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { loadingView.setVisibility(View.GONE); } }); } }
Membuat animasi card flip
Card flip beralih di antara tampilan konten dengan menampilkan animasi yang menyerupai kartu yang sedang dibalik. Animasi card flip yang ditunjukkan di sini menggunakan
FragmentTransaction.
Seperti inilah tampilan animasi card flip:
Membuat objek animator
Untuk membuat animasi card flip, Anda memerlukan empat animator. Dua animator digunakan saat bagian depan kartu dianimasikan keluar dan ke kiri serta saat dianimasikan masuk dan dari kiri. Dua animator lainnya digunakan saat bagian belakang kartu dianimasikan masuk dan dari kanan serta saat dianimasikan keluar dan ke kanan.
card_flip_left_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Before rotating, immediately set the alpha to 0. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:duration="0" />
<!-- Rotate. -->
<objectAnimator
android:valueFrom="-180"
android:valueTo="0"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" />
<!-- Halfway through the rotation, set the alpha to 1. See startOffset. -->
<objectAnimator
android:valueFrom="0.0"
android:valueTo="1.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" />
</set>
card_flip_left_out.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Rotate. -->
<objectAnimator
android:valueFrom="0"
android:valueTo="180"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" />
<!-- Halfway through the rotation, set the alpha to 0. See startOffset. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" />
</set>
card_flip_right_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Before rotating, immediately set the alpha to 0. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:duration="0" />
<!-- Rotate. -->
<objectAnimator
android:valueFrom="180"
android:valueTo="0"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" />
<!-- Halfway through the rotation, set the alpha to 1. See startOffset. -->
<objectAnimator
android:valueFrom="0.0"
android:valueTo="1.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" />
</set>
card_flip_right_out.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Rotate. -->
<objectAnimator
android:valueFrom="0"
android:valueTo="-180"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" />
<!-- Halfway through the rotation, set the alpha to 0. See startOffset. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" />
</set>
Membuat tampilan
Setiap sisi kartu adalah tata letak terpisah dan dapat berisi konten apa pun yang Anda inginkan, seperti dua tampilan teks, dua gambar, atau kombinasi tampilan apa pun untuk dibalik. Gunakan dua tata letak dalam fragmen yang akan Anda animasikan nanti. Tata letak berikut membuat satu sisi kartu yang menampilkan teks:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#a6c"
android:padding="16dp"
android:gravity="bottom">
<TextView android:id="@android:id/text1"
style="?android:textAppearanceLarge"
android:textStyle="bold"
android:textColor="#fff"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/card_back_title" />
<TextView style="?android:textAppearanceSmall"
android:textAllCaps="true"
android:textColor="#80ffffff"
android:textStyle="bold"
android:lineSpacingMultiplier="1.2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/card_back_description" />
</LinearLayout>
Tata letak berikutnya membuat sisi lain kartu yang menampilkan
ImageView:
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/image1"
android:scaleType="centerCrop"
android:contentDescription="@string/description_image_1" />
Membuat fragmen
Buat class fragmen untuk bagian depan dan belakang kartu. Di class fragmen, tampilkan tata letak yang Anda buat dari
onCreateView()
metode. Anda dapat membuat instance fragmen ini dalam aktivitas induk ketika ingin menunjukkan kartu.
Contoh berikut menunjukkan class fragmen bertingkat di dalam aktivitas induk yang menggunakannya:
Kotlin
class CardFlipActivity : FragmentActivity() { ... /** * A fragment representing the front of the card. */ class CardFrontFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View = inflater.inflate(R.layout.fragment_card_front, container, false) } /** * A fragment representing the back of the card. */ class CardBackFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View = inflater.inflate(R.layout.fragment_card_back, container, false) } }
Java
public class CardFlipActivity extends FragmentActivity { ... /** * A fragment representing the front of the card. */ public class CardFrontFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_card_front, container, false); } } /** * A fragment representing the back of the card. */ public class CardBackFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_card_back, container, false); } } }
Menganimasikan card flip
Tampilkan fragmen di dalam aktivitas induk. Untuk melakukannya, buat tata letak untuk aktivitas Anda. Contoh berikut membuat
FrameLayout yang dapat Anda tambahkan
fragmen saat waktu proses:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Dalam kode aktivitas, tetapkan tampilan konten menjadi tata letak yang Anda buat. Sebaiknya tampilkan fragmen default saat aktivitas dibuat. Aktivitas contoh berikut menunjukkan cara menampilkan bagian depan kartu secara default:
Kotlin
class CardFlipActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_activity_card_flip) if (savedInstanceState == null) { supportFragmentManager.beginTransaction() .add(R.id.container, CardFrontFragment()) .commit() } } ... }
Java
public class CardFlipActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_activity_card_flip); if (savedInstanceState == null) { getSupportFragmentManager() .beginTransaction() .add(R.id.container, new CardFrontFragment()) .commit(); } } ... }
Dengan bagian depan kartu ditampilkan, Anda dapat menampilkan bagian belakang kartu dengan animasi flip pada waktu yang tepat. Buat metode untuk menampilkan sisi lain kartu yang melakukan hal berikut:
- Menetapkan animasi kustom yang Anda buat untuk transisi fragmen.
- Mengganti fragmen yang ditampilkan dengan fragmen baru dan menganimasikan peristiwa ini dengan animasi kustom yang Anda buat.
- Menambahkan fragmen yang sebelumnya ditampilkan ke stack belakang fragmen sehingga ketika pengguna mengetuk tombol Kembali, kartu akan membalik kembali.
Kotlin
class CardFlipActivity : FragmentActivity() { ... private fun flipCard() { if (showingBack) { supportFragmentManager.popBackStack() return } // Flip to the back. showingBack = true // Create and commit a new fragment transaction that adds the fragment // for the back of the card, uses custom animations, and is part of the // fragment manager's back stack. supportFragmentManager.beginTransaction() // Replace the default fragment animations with animator // resources representing rotations when switching to the back // of the card, as well as animator resources representing // rotations when flipping back to the front, such as when the // system Back button is tapped. .setCustomAnimations( R.animator.card_flip_right_in, R.animator.card_flip_right_out, R.animator.card_flip_left_in, R.animator.card_flip_left_out ) // Replace any fragments in the container view with a fragment // representing the next page, indicated by the just-incremented // currentPage variable. .replace(R.id.container, CardBackFragment()) // Add this transaction to the back stack, letting users press // the Back button to get to the front of the card. .addToBackStack(null) // Commit the transaction. .commit() } }
Java
public class CardFlipActivity extends FragmentActivity { ... private void flipCard() { if (showingBack) { getSupportFragmentManager().popBackStack(); return; } // Flip to the back. showingBack = true; // Create and commit a new fragment transaction that adds the fragment // for the back of the card, uses custom animations, and is part of the // fragment manager's back stack. getSupportFragmentManager() .beginTransaction() // Replace the default fragment animations with animator // resources representing rotations when switching to the back // of the card, as well as animator resources representing // rotations when flipping back to the front, such as when the // system Back button is pressed. .setCustomAnimations( R.animator.card_flip_right_in, R.animator.card_flip_right_out, R.animator.card_flip_left_in, R.animator.card_flip_left_out) // Replace any fragments in the container view with a fragment // representing the next page, indicated by the just-incremented // currentPage variable. .replace(R.id.container, new CardBackFragment()) // Add this transaction to the back stack, letting users press // Back to get to the front of the card. .addToBackStack(null) // Commit the transaction. .commit(); } }
Membuat animasi reveal melingkar
Animasi reveal memberikan kontinuitas visual kepada pengguna saat menampilkan atau menyembunyikan sekelompok elemen UI. Metode
ViewAnimationUtils.createCircularReveal()
memungkinkan Anda menganimasikan lingkaran pemotongan untuk menampilkan atau menyembunyikan tampilan. Animasi ini disediakan dalam class
ViewAnimationUtils,
yang tersedia untuk Android 5.0 (API level 21) dan yang lebih tinggi.
Berikut adalah contoh yang menunjukkan cara menampilkan tampilan yang sebelumnya tidak terlihat:
Kotlin
// A previously invisible view. val myView: View = findViewById(R.id.my_view) // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. val cx = myView.width / 2 val cy = myView.height / 2 // Get the final radius for the clipping circle. val finalRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat() // Create the animator for this view. The start radius is 0. val anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0f, finalRadius) // Make the view visible and start the animation. myView.visibility = View.VISIBLE anim.start() } else { // Set the view to invisible without a circular reveal animation below // Android 5.0. myView.visibility = View.INVISIBLE }
Java
// A previously invisible view. View myView = findViewById(R.id.my_view); // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. int cx = myView.getWidth() / 2; int cy = myView.getHeight() / 2; // Get the final radius for the clipping circle. float finalRadius = (float) Math.hypot(cx, cy); // Create the animator for this view. The start radius is 0. Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0f, finalRadius); // Make the view visible and start the animation. myView.setVisibility(View.VISIBLE); anim.start(); } else { // Set the view to invisible without a circular reveal animation below // Android 5.0. myView.setVisibility(View.INVISIBLE); }
Animasi ViewAnimationUtils.createCircularReveal() memerlukan lima parameter.
Parameter pertama adalah tampilan yang ingin Anda sembunyikan atau tampilkan di layar. Dua parameter berikutnya adalah koordinat X dan Y untuk pusat lingkaran pemotongan. Biasanya, koordinat ini adalah pusat tampilan, tetapi Anda juga dapat menggunakan titik yang diketuk pengguna sehingga animasi dimulai di tempat yang mereka pilih. Parameter keempat adalah jari-jari awal dari lingkaran pemangkas.
Dalam contoh sebelumnya, jari-jari awal ditetapkan ke nol sehingga tampilan yang ditampilkan disembunyikan oleh lingkaran. Parameter terakhir adalah jari-jari akhir lingkaran. Saat akan menampilkan tampilan, pastikan jari-jari akhir lebih besar dari tampilan itu sendiri sehingga tampilan tersebut dapat ditampilkan sepenuhnya sebelum animasi selesai.
Untuk menyembunyikan tampilan yang sebelumnya terlihat, lakukan hal berikut:
Kotlin
// A previously visible view. val myView: View = findViewById(R.id.my_view) // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. val cx = myView.width / 2 val cy = myView.height / 2 // Get the initial radius for the clipping circle. val initialRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat() // Create the animation. The final radius is 0. val anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0f) // Make the view invisible when the animation is done. anim.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { super.onAnimationEnd(animation) myView.visibility = View.INVISIBLE } }) // Start the animation. anim.start() } else { // Set the view to visible without a circular reveal animation below // Android 5.0. myView.visibility = View.VISIBLE }
Java
// A previously visible view. final View myView = findViewById(R.id.my_view); // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. int cx = myView.getWidth() / 2; int cy = myView.getHeight() / 2; // Get the initial radius for the clipping circle. float initialRadius = (float) Math.hypot(cx, cy); // Create the animation. The final radius is 0. Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0f); // Make the view invisible when the animation is done. anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); myView.setVisibility(View.INVISIBLE); } }); // Start the animation. anim.start(); } else { // Set the view to visible without a circular reveal animation below Android // 5.0. myView.setVisibility(View.VISIBLE); }
Dalam hal ini, jari-jari awal dari lingkaran pemotongan disetel agar menjadi sebesar tampilan sehingga tampilan akan terlihat sebelum animasi dimulai. Jari-jari terakhir ditetapkan ke nol sehingga tampilan akan disembunyikan ketika animasi selesai.
Tambahkan pemroses ke animasi agar visibilitas tampilan dapat ditetapkan ke
INVISIBLE saat animasi
selesai.