בזמן השימוש באפליקציה, מופיע מידע חדש במסך ומוצג מידע ישן יוסר מידע. אם משנים את התוכן שמוצג במסך באופן מיידי, צורם, ומשתמשים עלולים לפספס תוכן חדש שמופיע פתאום. אנימציות איטיות למטה את השינויים ולמשוך את תשומת הלב של המשתמש בתנועה, כך שהעדכונים לברורים יותר.
יש שלוש אנימציות נפוצות שבהן אפשר להשתמש כדי להציג או להסתיר תצוגה: חשיפה אנימציות, אנימציות עמעום הדרגתי ואנימציות של קלפים.
יצירת אנימציה של עמעום הדרגתי
אנימציה של עמעום הדרגתי, שנקראת גם הפוגה, נעלמת בהדרגה
אחת View
או
ViewGroup
בו-זמנית
או להיעלם לשניים. האנימציה הזו שימושית במצבים שבהם רוצים
להחליף תוכן או תצוגות באפליקציה. האנימציה של עמעום הדרגתי שמוצגת כאן משתמשת
ViewPropertyAnimator
עבור Android 3.1 (רמת API 12) ומעלה.
הנה דוגמה של עמעום הדרגתי ממחוון התקדמות לתוכן טקסט:
יצירת התצוגות
יוצרים את שתי התצוגות שרוצים לעמעום הדרגתי. הדוגמה הבאה יוצרת מחוון התקדמות ותצוגת טקסט שניתן לגלול:
<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>
הגדרת האנימציה של עמעום הדרגתי
כדי להגדיר את האנימציה של עמעום הדרגתי, מבצעים את הפעולות הבאות:
- יוצרים משתני חברות עבור התצוגות שבהן רוצים לבצע עמעום הדרגתי. צריך את ההפניות האלה מאוחר יותר כשמשנים את התצוגות במהלך האנימציה.
- הגדרת החשיפה של התצוגה המטושטשת לערך
GONE
הפעולה הזו מונעת את התצוגה משימוש במרחב פריסה ומשמיט אותו מחישובי פריסה, שמאיץ בתהליך עיבוד - שמירת
config_shortAnimTime
במטמון מאפיין system במשתנה חבר. בנכס הזה מוגדר שם של סרטון קצר (Short) סטנדרטי משך האנימציה. משך הזמן הזה אידיאלי לאנימציות עדינות אנימציות שמופיעות לעיתים קרובות.config_longAnimTime
וגםconfig_mediumAnimTime
זמינים גם כן.
הנה דוגמה שבה נעשה שימוש בפריסה מקטע הקוד הקודם בתור ה- תצוגת תוכן של פעילות:
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); } ... }
עמעום הדרגתי של הצפיות
לאחר שהתצוגות מוגדרות כראוי, הצלבה אותן באמצעות ביצוע הפעולות הבאות:
- בתצוגה הדרגתית, הגדירו את ערך האלפא ל-0 ואת החשיפה
אל
VISIBLE
הגדרה שלGONE
. כך התצוגה תהיה גלויה אבל שקופה. - לתצוגה הדרגתית, הגדר אנימציה של ערך האלפא שלה מ-0 עד 1. עבור בתצוגה הדרגתית, הציגו אנימציה של ערך האלפא מ-1 ל-0.
- באמצעות
onAnimationEnd()
תוך שימושAnimator.AnimatorListener
, הגדרת החשיפה של התצוגה הנעלמת ל-GONE
. למרות ערך אלפא הוא 0. הגדרת הרשאות הגישה של התצוגה ל-GONE
מונעת את התצוגה משימוש במרחב פריסה ומשמיט אותו מחישובי פריסה, שמאיץ בתהליך עיבוד.
הדוגמה הבאה מראה איך לעשות את זה:
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); } }); } }
יצירת אנימציה של הפיכת קלפים
החלפת כרטיסים בין תצוגות התוכן על ידי הצגת אנימציה שמבצעת אמולציה
של כרטיס שמתחלף. האנימציה של היפוך הכרטיסים שמוצגת כאן משתמשת
FragmentTransaction
כך נראית הפיכת כרטיס:
יצירת אובייקטים של אנימטורים
כדי ליצור את האנימציה של היפוך הקלפים, צריך ארבעה אנימטורים. שני אנימטורים למצב שבו החלק הקדמי של הכרטיס כולל אנימציה החוצה ושמאלה, וכשהיא כוללת אנימציה ומצד ימין. שני האנימטורים האחרים מיועדים לגב הכרטיס יוצרת אנימציה כלפי פנים ומצד ימין, וכשהיא יוצרת אנימציה כלפי חוץ וימינה.
<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>
<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>
<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>
<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>
יצירת התצוגות
כל צד של הכרטיס הוא פריסה נפרדת שיכולה להכיל כל תוכן כמו שתי צפיות בטקסט, שתי תמונות או כל שילוב של תצוגות שניתן להפוך ביניהם. משתמשים בשתי הפריסות במקטעים שיניבו אנימציה מאוחר יותר. הפריסה הבאה יוצרת צד אחד של כרטיס, שבו מוצג טקסט:
<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>
והפריסה הבאה יוצרת את הצד השני של הכרטיס,
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" />
יצירת המקטעים
יוצרים כיתות מקטעים עבור הצד הקדמי והצד האחורי של הכרטיס. בקטע שלך
את הפריסות שיצרתם, תחזירו את הפריסות שיצרתם
onCreateView()
. לאחר מכן אפשר ליצור מופעים של המקטע הזה בפעילות ההורה
שבו אתם רוצים להציג את הכרטיס.
בדוגמה הבאה מוצגות מחלקות מקוננות של מקטעים בתוך פעילות ההורה. שמשתמשת בהם:
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); } } }
אנימציה של היפוך הקלפים
הצגת המקטעים בתוך פעילות הורה. כדי לעשות את זה, יוצרים את הפריסה.
לפעילות שלכם. הדוגמה הבאה יוצרת
FrameLayout
שאפשר להוסיף
מקטעים עד בזמן הריצה:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
בקוד הפעילות, מגדירים את תצוגת התוכן להיות הפריסה שיצרתם. מומלץ להציג מקטע ברירת מחדל כשהפעילות נוצרת. בפעילות לדוגמה הבאה אפשר לראות איך להציג את החלק הקדמי של הכרטיס באמצעות ברירת מחדל:
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(); } } ... }
כשחזית הכרטיס מוצגת, אפשר להציג את גב הכרטיס עם להפוך את האנימציה בזמן המתאים. צור שיטה כדי להציג את הצד השני של הכרטיס שמבצע את הפעולות הבאות:
- מגדיר את האנימציות המותאמות אישית שיצרת עבור מעברי המקטעים.
- החלפת המקטע המוצג במקטע חדש ואנימציה של האירוע הזה באמצעות האנימציות בהתאמה אישית שיצרתם.
- מוסיף את המקטע שהוצג קודם למקבץ המקטעים האחורי, כך ש המשתמש מקיש על הלחצן 'הקודם', והכרטיס מתחלף בחזרה.
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(); } }
יצירה של אנימציית חשיפה מעגלית
הצגת אנימציות מספקות למשתמשים המשכיות ויזואלית כשמציגים או מסתירים קבוצה
של רכיבים בממשק המשתמש.
ViewAnimationUtils.createCircularReveal()
מאפשרת להוסיף אנימציה של עיגול חיתוך כדי לחשוף או להסתיר תצוגה. הזה
ואנימציה
ViewAnimationUtils
כיתה,
עבור Android בגרסה 5.0 (רמת API 21) ומעלה.
לפניכם דוגמה שממחישה איך לחשוף תצוגה שהייתה מוסתרת בעבר:
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); }
האנימציה ViewAnimationUtils.createCircularReveal()
כוללת חמישה פרמטרים.
הפרמטר הראשון הוא התצוגה שרוצים להסתיר או להציג במסך.
שני הפרמטרים הבאים הם קואורדינטות ה-X וה-Y למרכז החיתוך
מעגל. בדרך כלל זהו מרכז התצוגה, אבל אפשר להשתמש גם
נקודה שהמשתמש יקיש עליה כדי שהאנימציה תתחיל מהמקום שבו הוא בחר.
הפרמטר הרביעי הוא הרדיוס ההתחלתי של מעגל החיתוך.
בדוגמה הקודמת, הרדיוס הראשוני נקבע לאפס, כדי שהתצוגה מוסתרת על ידי המעגל. הפרמטר האחרון הוא הרדיוס הסופי של המעגל. בעת הצגת תצוגה, הגדל את הרדיוס הסופי כך שניתן יהיה לחשוף את התצוגה במלואה לפני סיום האנימציה.
כדי להסתיר תצוגה שהייתה גלויה קודם לכן, מבצעים את הפעולות הבאות:
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); }
במקרה זה, הרדיוס הראשוני של עיגול החיתוך מוגדר לגודל של
את התצוגה כדי שהתצוגה תהיה גלויה לפני תחילת האנימציה. המשחק הסופי
הרדיוס מוגדר לאפס, כך שהתצוגה תוסתר בסיום האנימציה.
הוספת מקשיב לאנימציה כדי שניתן יהיה להגדיר את חשיפת התצוגה לערך
INVISIBLE
כשהאנימציה
שהושלמו.