מערכת האנימציה של מאפיינים היא מסגרת חזקה שמאפשרת לכם להוסיף אנימציה כמעט לכל דבר. אפשר להגדיר אנימציה שתשנה את המאפיינים של אובייקט מסוים לאורך זמן, בלי קשר לשאלה אם הוא מוצג על המסך או לא. אנימציה של מאפיין משנה את הערך של מאפיין (שדה באובייקט) לאורך פרק זמן מוגדר. כדי להנפיש משהו, מציינים את מאפיין האובייקט שרוצים להנפיש, כמו המיקום של האובייקט במסך, משך ההנפשה והערכים שרוצים להנפיש.
מערכת האנימציה של המאפיינים מאפשרת לכם להגדיר את המאפיינים הבאים של אנימציה:
- משך: אפשר לציין את משך האנימציה. אורך ברירת המחדל הוא 300 אלפיות השנייה.
- אינטרפולציה של זמן: אתם יכולים לציין איך הערכים של המאפיין מחושבים כפונקציה של הזמן שחלף מאז תחילת האנימציה.
- מספר החזרות וההתנהגות: אפשר לציין אם האנימציה תחזור על עצמה כשהיא תגיע לסוף משך הזמן שלה, וכמה פעמים היא תחזור על עצמה. אפשר גם לציין אם רוצים שהאנימציה תופעל הפוך. אם מגדירים את האפשרות הזו לערך הפוך, האנימציה תופעל קדימה ואז אחורה שוב ושוב, עד שמגיעים למספר החזרות שהוגדר.
- ערכות של אנימציות: אפשר לקבץ אנימציות לערכות לוגיות שמופעלות יחד, ברצף או אחרי השהיות מוגדרות.
- השהיה ברענון המסגרת: אתם יכולים לציין באיזו תדירות לרענן את המסגרות של האנימציה. הערך שמוגדר כברירת מחדל הוא רענון כל 10 אלפיות השנייה, אבל המהירות שבה האפליקציה יכולה לרענן את הפריימים תלויה בסופו של דבר בעומס הכולל על המערכת ובמהירות שבה המערכת יכולה לטפל בטיימר הבסיסי.
כדי לראות דוגמה מלאה של אנימציה של מאפיין, אפשר לעיין במחלקה ChangeColor בדוגמה CustomTransition ב-GitHub.
איך פועלת אנימציה של מאפיינים
קודם נסביר איך אנימציה פועלת באמצעות דוגמה פשוטה. איור 1 מציג אובייקט היפותטי עם אנימציה שנוצרת באמצעות המאפיין x שלו, שמייצג את המיקום האופקי שלו במסך. משך האנימציה מוגדר ל-40 אלפיות השנייה, והמרחק
למעבר הוא 40 פיקסלים. כל 10 אלפיות השנייה, שזה קצב רענון המסגרת שמוגדר כברירת מחדל, האובייקט זז אופקית ב-10 פיקסלים. בסוף 40 אלפיות השנייה, האנימציה נעצרת והאובייקט מגיע למיקום אופקי 40. זו דוגמה לאנימציה עם אינטרפולציה לינארית, כלומר האובייקט נע במהירות קבועה.
איור 1. דוגמה לאנימציה לינארית
אפשר גם לציין שהאנימציות יכללו אינטרפולציה לא לינארית. איור 2 מציג אובייקט היפותטי שמאיץ בתחילת האנימציה ומאט בסוף האנימציה. האובייקט עדיין נע 40 פיקסלים ב-40 אלפיות השנייה, אבל לא בצורה ליניארית. בתחילת האנימציה, המהירות שלה עולה עד אמצע הדרך, ואז היא יורדת מאמצע הדרך ועד סוף האנימציה. כפי שמוצג באיור 2, המרחק שעוברים בתחילת האנימציה ובסופה קטן יותר מהמרחק שעוברים באמצע.
איור 2. דוגמה לאנימציה לא לינארית
בואו נבחן בפירוט איך הרכיבים החשובים של מערכת האנימציה של המאפיינים מחשבים אנימציות כמו אלה שמוצגות באיור שלמעלה. איור 3 מציג את אופן הפעולה של המחלקות העיקריות זו עם זו.
איור 3. איך האנימציות מחושבות
האובייקט ValueAnimator עוקב אחרי התזמון של האנימציה, למשל כמה זמן האנימציה פועלת, והערך הנוכחי של המאפיין שהאנימציה מופעלת בו.
התג ValueAnimator כולל את התג TimeInterpolator, שמגדיר את האינטרפולציה של האנימציה, ואת התג TypeEvaluator, שמגדיר איך לחשב את הערכים של המאפיין שמונפש. לדוגמה, באיור 2, הערך של TimeInterpolator used יהיה
AccelerateDecelerateInterpolator והערך של TypeEvaluator יהיה IntEvaluator.
כדי להתחיל אנימציה, יוצרים אובייקט ValueAnimator ומגדירים בו את ערכי ההתחלה והסיום של המאפיין שרוצים להנפיש, וגם את משך האנימציה. כשמתקשרים אל start(), האנימציה מתחילה. במהלך האנימציה, ValueAnimator מחשב elapsed fraction
בין 0 ל-1, על סמך משך האנימציה והזמן שחלף. הערך
elapsed fraction מייצג את אחוז הזמן שחלף מתחילת האנימציה. 0 מייצג 0% ו-1 מייצג 100%. לדוגמה, באיור 1, השבר שחלף בזמן t = 10 ms יהיה .25
כי משך הזמן הכולל הוא t = 40 ms.
כשהפונקציה ValueAnimator מסיימת לחשב את השבר של הזמן שחלף, היא קוראת לפונקציה TimeInterpolator שהוגדרה כרגע, כדי לחשב שבר משוער. שבר שעבר אינטרפולציה ממפה את השבר שחלף לשבר חדש שמתחשב באינטרפולציה של הזמן שהוגדרה. לדוגמה, באיור 2,
מכיוון שהאנימציה מאיצה לאט, החלק המחושב, בערך 0 .15, קטן מהחלק שחלף, 0.25, בזמן t = 10 ms. באיור 1, החלק המחושב תמיד זהה לחלק שחלף.
כשמחושב השבר המחושב, הפונקציה ValueAnimator קוראת לפונקציה המתאימה TypeEvaluator כדי לחשב את ערך המאפיין שמונפש, על סמך השבר המחושב, ערך ההתחלה וערך הסיום של האנימציה. לדוגמה, באיור 2, השבר המשוער היה 0 .15 ב-t =
10 ms, ולכן הערך של המאפיין באותו זמן יהיה 0 .15 × (40 - 0), או 6.
מה ההבדל בין אנימציה של מאפיינים לבין אנימציה של תצוגה
מערכת האנימציה של התצוגה מאפשרת להנפיש רק אובייקטים מסוג View
, לכן אם רוצים להנפיש אובייקטים מסוג שאינו View, צריך להטמיע קוד משלכם. מערכת האנימציה של התצוגה מוגבלת גם בכך שהיא חושפת רק כמה היבטים של אובייקט מסוג View להנפשה, כמו שינוי הגודל והסיבוב של תצוגה, אבל לא את צבע הרקע, למשל.View
חיסרון נוסף של מערכת האנימציה של התצוגה הוא שהיא שינתה רק את המיקום שבו התצוגה צוירה, ולא את התצוגה עצמה. לדוגמה, אם יצרתם אנימציה של לחצן כדי להזיז אותו על המסך, הלחצן מצויר בצורה נכונה, אבל המיקום בפועל שבו אפשר ללחוץ על הלחצן לא משתנה, ולכן צריך להטמיע לוגיקה משלכם כדי לטפל בזה.
בעזרת מערכת האנימציה של המאפיינים, המגבלות האלה מוסרות לגמרי, ואפשר להנפיש כל מאפיין של כל אובייקט (תצוגות ולא תצוגות), והאובייקט עצמו משתנה בפועל. מערכת האנימציה של הנכס גם חזקה יותר באופן שבו היא מבצעת אנימציה. באופן כללי, אתם מקצים אנימטורים למאפיינים שאתם רוצים להנפיש, כמו צבע, מיקום או גודל, ויכולים להגדיר היבטים של האנימציה, כמו אינטרפולציה וסנכרון של כמה אנימטורים.
לעומת זאת, המערכת להנפשת תצוגות דורשת פחות זמן להגדרה ופחות קוד לכתיבה. אם אנימציית התצוגה מבצעת את כל מה שאתם צריכים, או אם הקוד הקיים כבר פועל כמו שאתם רוצים, אין צורך להשתמש במערכת אנימציית המאפיינים. יכול להיות שיהיה הגיוני להשתמש בשתי מערכות האנימציה במצבים שונים, אם יתעורר תרחיש שימוש כזה.
סקירה כללית על ממשקי API
רוב ממשקי ה-API של מערכת הנפשת המאפיינים נמצאים ב-android.animation. מכיוון שמערכת האנימציה של התצוגה כבר מגדירה הרבה פונקציות אינטרפולציה ב-android.view.animation, אפשר להשתמש בפונקציות האלה גם במערכת האנימציה של המאפיינים. בטבלאות הבאות מתוארים הרכיבים העיקריים של מערכת האנימציה של המאפיינים.
המחלקת Animator מספקת את המבנה הבסיסי ליצירת אנימציות. בדרך כלל לא משתמשים במחלקה הזו ישירות, כי היא מספקת רק פונקציונליות מינימלית שצריך להרחיב כדי לתמוך באופן מלא בהנפשת ערכים. מחלקות המשנה הבאות מרחיבות את Animator:
טבלה 1. אנימטורים
| דרגה | תיאור |
|---|---|
ValueAnimator |
מנוע התזמון הראשי לאנימציה של מאפיינים, שמחשב גם את הערכים של המאפיין שרוצים להנפיש. הוא כולל את כל הפונקציונליות הבסיסית שמחשבת ערכי אנימציה, ומכיל את פרטי התזמון של כל אנימציה, מידע על חזרה של אנימציה, מאזינים שמקבלים אירועי עדכון ואפשרות להגדיר סוגים מותאמים אישית להערכה. יש שני חלקים בהנפשת מאפיינים: חישוב הערכים המונפשים והגדרת הערכים האלה באובייקט ובמאפיין שמנפישים. ValueAnimator לא מבצע את החלק השני, ולכן צריך להאזין לעדכונים של ערכים שמחושבים על ידי ValueAnimator ולשנות את האובייקטים שרוצים להנפיש באמצעות לוגיקה משלכם. מידע נוסף זמין בקטע בנושא הנפשה באמצעות ValueAnimator. |
ObjectAnimator |
מחלקת משנה של ValueAnimator שמאפשרת להגדיר אובייקט יעד ומאפיין אובייקט להנפשה. המחלקות האלה מעדכנות את המאפיין בהתאם כשמחושב ערך חדש לאנימציה. מומלץ להשתמש ב-ObjectAnimator ברוב המקרים, כי הוא מקל מאוד על תהליך האנימציה של ערכים באובייקטים של היעד. עם זאת,
לפעמים כדאי להשתמש ישירות ב-ValueAnimator כי יש ל-ObjectAnimator כמה הגבלות נוספות, כמו דרישה לשימוש בשיטות גישה ספציפיות באובייקט היעד. |
AnimatorSet |
מספק מנגנון לקיבוץ אנימציות כך שהן יפעלו ביחס זו לזו. אתם יכולים להגדיר שאנימציות יפעלו יחד, ברצף או אחרי השהיה שצוינה. מידע נוסף זמין בקטע בנושא כוריאוגרפיה של כמה אנימציות באמצעות ערכות Animator. |
הפונקציות Evaluator אומרות למערכת האנימציה של המאפיינים איך לחשב ערכים של מאפיין נתון. הם מקבלים את נתוני התזמון שמסופקים על ידי מחלקה Animator, את ערך ההתחלה והסיום של האנימציה, ומחשבים את הערכים המונפשים של המאפיין על סמך הנתונים האלה. מערכת האנימציה של הנכס מספקת את המעריכים הבאים:
טבלה 2. מעריכים
| Class/Interface | תיאור |
|---|---|
IntEvaluator |
הערך שמוגדר כברירת מחדל לחישוב ערכים של מאפייני int. |
FloatEvaluator |
הערך שמוגדר כברירת מחדל לחישוב ערכים למאפיינים של float. |
ArgbEvaluator |
הפונקציה שמוגדרת כברירת מחדל לחישוב ערכים של מאפייני צבע שמיוצגים כערכים הקסדצימליים. |
TypeEvaluator |
ממשק שמאפשר לכם ליצור בודק משלכם. אם מנפישים מאפיין של אובייקט שהוא לא int, float או צבע, צריך להטמיע את הממשק TypeEvaluator כדי לציין איך לחשב את הערכים המונפשים של מאפיין האובייקט. אפשר גם לציין ערך מותאם אישית של TypeEvaluator לint, לfloat ולצבע, אם רוצים לעבד את הסוגים האלה באופן שונה מההתנהגות שמוגדרת כברירת מחדל.
מידע נוסף על כתיבת כלי הערכה מותאם אישית זמין בקטע שימוש ב-TypeEvaluator. |
אינטרפולטור זמן מגדיר איך ערכים ספציפיים באנימציה מחושבים כפונקציה של הזמן. לדוגמה, אפשר לציין שאנימציות יפעלו באופן לינארי לאורך כל האנימציה, כלומר האנימציה תנוע באופן שווה לאורך כל הזמן, או לציין שאנימציות ישתמשו בזמן לא לינארי, למשל, האצה בהתחלה והאטה בסוף האנימציה. בטבלה 3 מתוארים האובייקטים מסוג Interpolator שנכללים ב-android.view.animation. אם אף אחד מהאינטרפולטורים שסופקו לא מתאים לצרכים שלכם, אתם יכולים להטמיע את הממשק TimeInterpolator וליצור אינטרפולטור משלכם. מידע נוסף על כתיבת אינטרפולטור מותאם אישית זמין במאמר שימוש באינטרפולטורים.
טבלה 3. אינטרפולטורים
| Class/Interface | תיאור |
|---|---|
AccelerateDecelerateInterpolator |
אינטרפולטור ששיעור השינוי שלו מתחיל ומסתיים לאט, אבל מואץ באמצע. |
AccelerateInterpolator |
אינטרפולטור ששיעור השינוי שלו מתחיל לאט ואז מואץ. |
AnticipateInterpolator |
אינטרפולטור שהשינוי שלו מתחיל לאחור ואז מתקדם קדימה. |
AnticipateOvershootInterpolator |
פונקציית אינטרפולציה שהשינוי שלה מתחיל לאחור, מתקדם קדימה ועובר את ערך היעד, ואז חוזר לערך הסופי. |
BounceInterpolator |
אינטרפולטור שהשינוי שלו קופץ בסוף. |
CycleInterpolator |
אמצעי אינטרפולציה שהאנימציה שלו חוזרת על עצמה מספר מסוים של מחזורים. |
DecelerateInterpolator |
אינטרפולטור שקצב השינוי שלו מתחיל מהר ואז מואט. |
LinearInterpolator |
אינטרפולטור שקצב השינוי שלו קבוע. |
OvershootInterpolator |
אינטרפולטור שהשינוי שלו מתקדם קדימה וחוצה את הערך האחרון, ואז חוזר. |
TimeInterpolator |
ממשק שמאפשר לכם להטמיע אינטרפולטור משלכם. |
יצירת אנימציה באמצעות ValueAnimator
המחלקות ValueAnimator מאפשרות להוסיף אנימציה לערכים מסוג מסוים למשך האנימציה, על ידי ציון קבוצה של ערכי int, float או צבע שיוצגו באנימציה. כדי לקבל ValueAnimator, קוראים לאחת משיטות היצירה שלו: ofInt(), ofFloat() או ofObject(). לדוגמה:
Kotlin
ValueAnimator.ofFloat(0f, 100f).apply { duration = 1000 start() }
Java
ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f); animation.setDuration(1000); animation.start();
בדוגמה הזו, הפונקציה ValueAnimator מתחילה לחשב את ערכי האנימציה start(), בין 0 ל-100, למשך 1,000 אלפיות השנייה, כשהמתודה start() מופעלת.
אפשר גם לציין סוג מותאם אישית של אנימציה באופן הבא:
Kotlin
ValueAnimator.ofObject(MyTypeEvaluator(), startPropertyValue, endPropertyValue).apply { duration = 1000 start() }
Java
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue); animation.setDuration(1000); animation.start();
בקוד הזה, הפונקציה ValueAnimator מתחילה לחשב את ערכי האנימציה, בין startPropertyValue ל-endPropertyValue באמצעות הלוגיקה שסופקה על ידי MyTypeEvaluator למשך 1,000 אלפיות השנייה, כשהמתודה start() פועלת.
אפשר להשתמש בערכים של האנימציה על ידי הוספת AnimatorUpdateListener
לאובייקט ValueAnimator, כמו שמוצג בקוד הבא:
Kotlin
ValueAnimator.ofObject(...).apply { ... addUpdateListener { updatedAnimation -> // You can use the animated value in a property that uses the // same type as the animation. In this case, you can use the // float value in the translationX property. textView.translationX = updatedAnimation.animatedValue as Float } ... }
Java
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator updatedAnimation) { // You can use the animated value in a property that uses the // same type as the animation. In this case, you can use the // float value in the translationX property. float animatedValue = (float)updatedAnimation.getAnimatedValue(); textView.setTranslationX(animatedValue); } });
בשיטה onAnimationUpdate()
אפשר לגשת לערך המעודכן של האנימציה ולהשתמש בו במאפיין של
אחת מהתצוגות המפורטות. מידע נוסף על מאזינים זמין בקטע בנושא מאזינים של אנימציות.
יצירת אנימציה באמצעות ObjectAnimator
ObjectAnimator הוא מחלקת משנה של ValueAnimator (שמוסברת בקטע הקודם), והוא משלב את מנוע התזמון ואת חישוב הערך של ValueAnimator עם היכולת להנפיש מאפיין בעל שם של אובייקט יעד. כך קל יותר להנפיש אובייקטים, כי כבר לא צריך להטמיע את ValueAnimator.AnimatorUpdateListener, כי המאפיין המונפש מתעדכן באופן אוטומטי.
יצירת מופע של ObjectAnimator דומה ליצירת מופע של ValueAnimator, אבל צריך גם לציין את האובייקט ואת שם המאפיין של האובייקט (כמחרוזת) יחד עם הערכים שרוצים ליצור ביניהם אנימציה:
Kotlin
ObjectAnimator.ofFloat(textView, "translationX", 100f).apply { duration = 1000 start() }
Java
ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f); animation.setDuration(1000); animation.start();
כדי שהמערכת ObjectAnimatorתעדכן את הנכסים
כמו שצריך, צריך לבצע את הפעולות הבאות:
- למאפיין האובייקט שיוצרים לו אנימציה צריכה להיות פונקציית setter (ב-camel case) בצורה הבאה:
set<PropertyName>(). השיטה הזו מאפשרת גישה לנכס כיObjectAnimatorמעדכן את הנכס באופן אוטומטי במהלך האנימציה. לדוגמה, אם שם הנכס הואfoo, צריך להגדיר את השיטהsetFoo(). אם שיטת ההגדרה הזו לא קיימת, יש שלוש אפשרויות:- אם יש לכם הרשאה לעשות זאת, מוסיפים את שיטת ה-setter למחלקה.
- משתמשים במחלקת wrapper שיש לכם הרשאות לשנות, ומגדירים שה-wrapper יקבל את הערך באמצעות שיטת setter תקינה ויעביר אותו לאובייקט המקורי.
- במקומה, צריך להשתמש ב-method
ValueAnimator.
- אם מציינים רק ערך אחד לפרמטר
values...באחת משיטות היצירהObjectAnimator, המערכת מניחה שזהו ערך הסיום של האנימציה. לכן, למאפיין האובייקט שמנפישים צריך להיות פונקציית getter שמשמשת להשגת ערך ההתחלה של האנימציה. פונקציית ה-getter צריכה להיות בפורמטget<PropertyName>(). לדוגמה, אם שם הנכס הואfoo, צריך להגדיר את השיטהgetFoo(). - שיטות ה-getter (אם צריך) וה-setter של המאפיין שאתם מנפישים חייבות לפעול על אותו סוג כמו ערכי ההתחלה והסיום שאתם מציינים ל-
ObjectAnimator. לדוגמה, אם יוצרים אתObjectAnimatorהבא, צריך להגדיר אתtargetObject.setPropName(float)ואתtargetObject.getPropName():ObjectAnimator.ofFloat(targetObject, "propName", 1f)
- בהתאם למאפיין או לאובייקט שאתם מנפישים, יכול להיות שתצטרכו לקרוא לשיטה
invalidate()ב-View כדי לאלץ את המסך לצייר מחדש את עצמו עם הערכים המונפשים המעודכנים. הפעולה הזו מתבצעת ב-callback שלonAnimationUpdate(). לדוגמה, אם מפעילים אנימציה של מאפיין הצבע של אובייקט Drawable, המסך מתעדכן רק כשהאובייקט מצייר את עצמו מחדש. כל הפונקציות להגדרת מאפיינים בתצוגה המפורטת, כמוsetAlpha()ו-setTranslationX(), מבטלות את התוקף של התצוגה המפורטת בצורה תקינה, כך שלא צריך לבטל את התוקף של התצוגה המפורטת כשמפעילים את הפונקציות האלה עם ערכים חדשים. מידע נוסף על מאזינים זמין בקטע בנושא מאזינים של אנימציות.
כוריאוגרפיה של כמה אנימציות באמצעות AnimatorSet
במקרים רבים, רוצים להפעיל אנימציה שתלויה במועד שבו אנימציה אחרת מתחילה או מסתיימת. מערכת Android מאפשרת לכם לאגד אנימציות יחד לתוך AnimatorSet, כדי שתוכלו לציין אם האנימציות יתחילו בו-זמנית, ברצף או אחרי השהיה מוגדרת. אפשר גם להוסיף AnimatorSet אובייקטים אחד בתוך השני.
בקטע הקוד הבא מוצגים אובייקטים של Animator
שמופעלים באופן הבא:
- הפעלת
bounceAnim. - הפעלת
squashAnim1,squashAnim2,stretchAnim1ו-stretchAnim2בו-זמנית. - הפעלת
bounceBackAnim. - הפעלת
fadeAnim.
Kotlin
val bouncer = AnimatorSet().apply { play(bounceAnim).before(squashAnim1) play(squashAnim1).with(squashAnim2) play(squashAnim1).with(stretchAnim1) play(squashAnim1).with(stretchAnim2) play(bounceBackAnim).after(stretchAnim2) } val fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply { duration = 250 } AnimatorSet().apply { play(bouncer).before(fadeAnim) start() }
Java
AnimatorSet bouncer = new AnimatorSet(); bouncer.play(bounceAnim).before(squashAnim1); bouncer.play(squashAnim1).with(squashAnim2); bouncer.play(squashAnim1).with(stretchAnim1); bouncer.play(squashAnim1).with(stretchAnim2); bouncer.play(bounceBackAnim).after(stretchAnim2); ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f); fadeAnim.setDuration(250); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.play(bouncer).before(fadeAnim); animatorSet.start();
מאזינים לאנימציות
אפשר להאזין לאירועים חשובים במהלך האנימציה באמצעות מאזינים שמתוארים בהמשך.
Animator.AnimatorListeneronAnimationStart()– מופעל כשמתחילה האנימציה.onAnimationEnd()- מופעל כשהאנימציה מסתיימת.-
onAnimationRepeat()– מופעלת כשהאנימציה חוזרת על עצמה. -
onAnimationCancel()– מופעלת כשהאנימציה מבוטלת. אנימציה שבוטלה תפעיל גם אתonAnimationEnd(), לא משנה איך היא הסתיימה.
ValueAnimator.AnimatorUpdateListener-
onAnimationUpdate()– מופעל בכל פריים של האנימציה. כדאי להאזין לאירוע הזה כדי להשתמש בערכים המחושבים שנוצרו על ידיValueAnimatorבמהלך אנימציה. כדי להשתמש בערך, שולחים שאילתה לאובייקטValueAnimatorשעבר לאירוע כדי לקבל את הערך המונפש הנוכחי באמצעות השיטהgetAnimatedValue(). חובה להטמיע את מאזין האירועים הזה אם משתמשים ב-ValueAnimator.בהתאם לנכס או לאובייקט שמוסיפים לו אנימציה, יכול להיות שתצטרכו להפעיל את הפונקציה
invalidate()ב-View כדי לאלץ את האזור הזה במסך לצייר את עצמו מחדש עם ערכי האנימציה החדשים. לדוגמה, אם מפעילים אנימציה של מאפיין הצבע של אובייקט Drawable, המסך מתעדכן רק כשהאובייקט מצייר את עצמו מחדש. כל הפונקציות להגדרת מאפיינים בתצוגה המפורטת, כמוsetAlpha()ו-setTranslationX(), מבטלות את התוקף של התצוגה המפורטת בצורה תקינה, כך שלא צריך לבטל את התוקף של התצוגה המפורטת כשקוראים לפונקציות האלה עם ערכים חדשים.
-
אם אתם לא רוצים להטמיע את כל השיטות של הממשק Animator.AnimatorListener, אתם יכולים להרחיב את המחלקה AnimatorListenerAdapter במקום להטמיע את הממשק Animator.AnimatorListener. המחלקות AnimatorListenerAdapter מספקות הטמעות ריקות של השיטות שאפשר לבחור להחליף.
לדוגמה, קטע הקוד הבא יוצר AnimatorListenerAdapter
רק עבור הקריאה החוזרת onAnimationEnd():
Kotlin
ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply { duration = 250 addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { balls.remove((animation as ObjectAnimator).target) } }) }
Java
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f); fadeAnim.setDuration(250); fadeAnim.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { balls.remove(((ObjectAnimator)animation).getTarget()); }
הנפשת שינויים בפריסה של אובייקטים מסוג ViewGroup
מערכת האנימציה של המאפיינים מאפשרת להנפיש שינויים באובייקטים של ViewGroup, וגם מספקת דרך קלה להנפיש אובייקטים של View.
אפשר להנפיש שינויים בפריסה בתוך ViewGroup באמצעות המחלקה LayoutTransition. כשמוסיפים תצוגות ל-ViewGroup או מסירים אותן ממנו, או כשקוראים לשיטה setVisibility() של View עם VISIBLE, INVISIBLE או GONE, התצוגות בתוך ה-ViewGroup עוברות אנימציה של הופעה והיעלמות. גם שאר התצוגות ב-ViewGroup יכולות להנפיש את המעבר למיקומים החדשים שלהן כשמוסיפים או מסירים תצוגות. אפשר להגדיר את האנימציות הבאות באובייקט LayoutTransition באמצעות קריאה ל-setAnimator() והעברה של אובייקט Animator עם אחד מהקבועים הבאים של LayoutTransition:
-
APPEARING– דגל שמציין את האנימציה שמופעלת על פריטים שמופיעים במאגר התגים. -
CHANGE_APPEARING– דגל שמציין את האנימציה שמופעלת על פריטים שמשתנים בגלל פריט חדש שמופיע במאגר. -
DISAPPEARING– דגל שמציין את האנימציה שמופעלת על פריטים שנעלמים מהקונטיינר. -
CHANGE_DISAPPEARING– דגל שמציין את האנימציה שמופעלת על פריטים שמשתנים בגלל שפריט נעלם מהקונטיינר.
אתם יכולים להגדיר אנימציות מותאמות אישית משלכם ל-4 סוגי האירועים האלה כדי להתאים אישית את המראה של מעברי הפריסה, או פשוט להגדיר למערכת האנימציה להשתמש באנימציות ברירת המחדל.
כדי להגדיר את מאפיין android:animateLayoutchanges לערך true עבור ViewGroup:
<LinearLayout android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="match_parent" android:id="@+id/verticalContainer" android:animateLayoutChanges="true" />
הגדרת הערך של המאפיין הזה כ-true תגרום להנפשה אוטומטית של רכיבי View שנוספו ל-ViewGroup או הוסרו ממנו, וגם של רכיבי View שנותרו ב-ViewGroup.
הנפשת שינויים במצב התצוגה באמצעות StateListAnimator
המחלקות StateListAnimator מאפשרות להגדיר אנימציות שמופעלות כשמצב התצוגה משתנה. האובייקט הזה מתנהג כעטיפה לאובייקט Animator, ומפעיל את האנימציה הזו בכל פעם שמשתנה מצב התצוגה שצוין (למשל, 'לחוץ' או 'במיקוד').
אפשר להגדיר את StateListAnimator במשאב XML עם רכיב בסיס <selector> ואלמנטים צאצאים <item> שכל אחד מהם מציין מצב תצוגה שונה שמוגדר על ידי המחלקה StateListAnimator. כל רכיב <item> מכיל את ההגדרה של קבוצת אנימציות של מאפיינים.
לדוגמה, הקובץ הבא יוצר אנימטור של רשימת מצבים שמשנה את קנה המידה של ציר ה-x וציר ה-y של התצוגה כשלוחצים עליה:
res/xml/animate_scale.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- the pressed state; increase x and y size to 150% --> <item android:state_pressed="true"> <set> <objectAnimator android:propertyName="scaleX" android:duration="@android:integer/config_shortAnimTime" android:valueTo="1.5" android:valueType="floatType"/> <objectAnimator android:propertyName="scaleY" android:duration="@android:integer/config_shortAnimTime" android:valueTo="1.5" android:valueType="floatType"/> </set> </item> <!-- the default, non-pressed state; set x and y size to 100% --> <item android:state_pressed="false"> <set> <objectAnimator android:propertyName="scaleX" android:duration="@android:integer/config_shortAnimTime" android:valueTo="1" android:valueType="floatType"/> <objectAnimator android:propertyName="scaleY" android:duration="@android:integer/config_shortAnimTime" android:valueTo="1" android:valueType="floatType"/> </set> </item> </selector>
כדי לצרף את האנימטור של רשימת המצבים לתצוגה, מוסיפים את המאפיין
android:stateListAnimator באופן הבא:
<Button android:stateListAnimator="@xml/animate_scale" ... />
עכשיו נעשה שימוש באנימציות שמוגדרות ב-animate_scale.xml כשמצב הלחצן הזה משתנה.
לחלופין, כדי להקצות אנימטור של רשימת מצבים לתצוגה בקוד, משתמשים בשיטה AnimatorInflater.loadStateListAnimator() ומקצים את האנימטור לתצוגה באמצעות השיטה View.setStateListAnimator().
לחלופין, במקום להנפיש את מאפייני התצוגה, אפשר להפעיל אנימציה של רכיב drawable בין שינויי מצב באמצעות AnimatedStateListDrawable.
חלק מהווידג'טים של המערכת ב-Android 5.0 משתמשים באנימציות האלה כברירת מחדל. בדוגמה הבאה אפשר לראות איך מגדירים AnimatedStateListDrawable כמשאב XML:
<!-- res/drawable/myanimstatedrawable.xml --> <animated-selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- provide a different drawable for each state--> <item android:id="@+id/pressed" android:drawable="@drawable/drawableP" android:state_pressed="true"/> <item android:id="@+id/focused" android:drawable="@drawable/drawableF" android:state_focused="true"/> <item android:id="@id/default" android:drawable="@drawable/drawableD"/> <!-- specify a transition --> <transition android:fromId="@+id/default" android:toId="@+id/pressed"> <animation-list> <item android:duration="15" android:drawable="@drawable/dt1"/> <item android:duration="15" android:drawable="@drawable/dt2"/> ... </animation-list> </transition> ... </animated-selector>
שימוש ב-TypeEvaluator
אם רוצים להנפיש סוג שלא מוכר למערכת Android, אפשר ליצור מעריך משלכם על ידי הטמעה של הממשק TypeEvaluator. הסוגים שמערכת Android מכירה הם int, float או צבע, שנתמכים על ידי מעריכי הסוגים IntEvaluator, FloatEvaluator ו-ArgbEvaluator.
יש רק שיטה אחת להטמעה בממשק TypeEvaluator
– השיטה evaluate(). כך האנימטור שבו אתם משתמשים יכול להחזיר ערך מתאים לנכס האנימציה בנקודה הנוכחית של האנימציה. בדוגמה הבאה אפשר לראות איך עושים את זה באמצעות המחלקה FloatEvaluator:
Kotlin
private class FloatEvaluator : TypeEvaluator<Any> { override fun evaluate(fraction: Float, startValue: Any, endValue: Any): Any { return (startValue as Number).toFloat().let { startFloat -> startFloat + fraction * ((endValue as Number).toFloat() - startFloat) } } }
Java
public class FloatEvaluator implements TypeEvaluator { public Object evaluate(float fraction, Object startValue, Object endValue) { float startFloat = ((Number) startValue).floatValue(); return startFloat + fraction * (((Number) endValue).floatValue() - startFloat); } }
הערה: כשמפעילים את ValueAnimator (או את ObjectAnimator), המערכת מחשבת את החלק הנוכחי של האנימציה שעבר (ערך בין 0 ל-1), ואז מחשבת גרסה משוערת של החלק הזה בהתאם לאינטרפולטור שבו משתמשים. השבר שחושב באינטרפולציה הוא מה ש-TypeEvaluator מקבל דרך הפרמטר fraction, כך שלא צריך להתחשב בפונקציית האינטרפולציה כשמחשבים ערכים מונפשים.
שימוש באינטרפולטורים
אינטרפולטור מגדיר איך ערכים ספציפיים באנימציה מחושבים כפונקציה של זמן. לדוגמה, אפשר לציין שאנימציות יפעלו באופן לינארי לאורך כל האנימציה, כלומר האנימציה תנוע באופן שווה לאורך כל הזמן, או לציין שאנימציות יפעלו באופן לא לינארי, למשל באמצעות האצה או האטה בתחילת האנימציה או בסופה.
אינטרפולטורים במערכת האנימציה מקבלים מספר בין 0 ל-1 מ-Animators שמייצג את הזמן שחלף מאז תחילת האנימציה. האינטרפולטורים משנים את השבר הזה כך שיתאים לסוג האנימציה שהם אמורים לספק. מערכת Android מספקת קבוצה של פונקציות אינטרפולציה נפוצות ב-android.view.animation package. אם אף אחד מהם לא מתאים לצרכים שלכם, אתם יכולים להטמיע את הממשק TimeInterpolator וליצור משלכם.
לדוגמה, בהמשך מוצגות השוואות בין האופן שבו מחשבים את השברים המחושבים של ברירת המחדל של פונקציית האינטרפולציה AccelerateDecelerateInterpolator לבין האופן שבו מחשבים אותם באמצעות LinearInterpolator.
הערך LinearInterpolator לא משפיע על השבר שחלף. הערך AccelerateDecelerateInterpolator מאיץ לתוך האנימציה ומאט כשהוא יוצא ממנה. השיטות הבאות מגדירות את הלוגיקה של הפונקציות האלה:
AccelerateDecelerateInterpolator
Kotlin
override fun getInterpolation(input: Float): Float = (Math.cos((input + 1) * Math.PI) / 2.0f).toFloat() + 0.5f
Java
@Override public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; }
LinearInterpolator
Kotlin
override fun getInterpolation(input: Float): Float = input
Java
@Override public float getInterpolation(float input) { return input; }
בטבלה הבאה מוצגים הערכים המשוערים שמחושבים על ידי פונקציות האינטרפולציה האלה לאנימציה שנמשכת 1,000 אלפיות השנייה:
| הזמן שחלף (באלפיות השנייה) | השבר שחלף/השבר המשוער (ליניארי) | שבר משוער (האצה/האטה) |
|---|---|---|
| 0 | 0 | 0 |
| 200 | .2 | .1 |
| 400 | 4. | .345 |
| 600 | 6. | .654 |
| 800 | .8 | 9. |
| 1000 | 1 | 1 |
כפי שאפשר לראות בטבלה, LinearInterpolator משנה את הערכים באותה מהירות, 0.2 לכל 200 אלפיות השנייה שעוברות. השינוי בערכים של AccelerateDecelerateInterpolator מהיר יותר מאשר בערכים של LinearInterpolator בין 200 אלפיות השנייה ל-600 אלפיות השנייה, ואיטי יותר בין 600 אלפיות השנייה ל-1,000 אלפיות השנייה.
ציון מסגרות מפתח
אובייקט Keyframe מורכב מצמד של זמן/ערך שמאפשר להגדיר
מצב ספציפי בזמן ספציפי באנימציה. לכל תמונת מפתח יכול להיות גם אינטרפולטור משלה כדי לשלוט בהתנהגות של האנימציה במרווח שבין הזמן של תמונת המפתח הקודמת לבין הזמן של תמונת המפתח הנוכחית.
כדי ליצור מופע של אובייקט Keyframe, צריך להשתמש באחת משיטות הפקטורי, ofInt(), ofFloat() או ofObject(), כדי לקבל את הסוג המתאים של Keyframe. אחר כך קוראים ל-factory method ofKeyframe() כדי לקבל אובייקט PropertyValuesHolder. אחרי שיש לכם את האובייקט, אתם יכולים להשיג אנימטור על ידי העברת האובייקט PropertyValuesHolder והאובייקט שרוצים להנפיש. בקטע הקוד הבא אפשר לראות איך עושים את זה:
Kotlin
val kf0 = Keyframe.ofFloat(0f, 0f) val kf1 = Keyframe.ofFloat(.5f, 360f) val kf2 = Keyframe.ofFloat(1f, 0f) val pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2) ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation).apply { duration = 5000 }
Java
Keyframe kf0 = Keyframe.ofFloat(0f, 0f); Keyframe kf1 = Keyframe.ofFloat(.5f, 360f); Keyframe kf2 = Keyframe.ofFloat(1f, 0f); PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2); ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation); rotationAnim.setDuration(5000);
הנפשת תצוגות
מערכת האנימציה של הנכס מאפשרת אנימציה יעילה של אובייקטים מסוג View ומציעה כמה יתרונות על פני מערכת האנימציה של התצוגה. מערכת האנימציה של התצוגה שינתה את אובייקטי התצוגה על ידי שינוי האופן שבו הם צויירו. הטיפול הזה בוצע במאגר התגים של כל תצוגה מפורטת, כי לתצוגה המפורטת עצמה לא היו מאפיינים שאפשר לשנות. התוצאה הייתה שהתצוגה הונפשה, אבל לא חל שינוי באובייקט התצוגה עצמו. הדבר הוביל להתנהגויות כמו אובייקט שעדיין קיים במיקום המקורי שלו, למרות שהוא צויר במיקום אחר במסך. ב-Android 3.0, נוספו מאפיינים חדשים ושיטות ה-getter וה-setter התואמות כדי לפתור את הבעיה הזו.
מערכת האנימציה של המאפיינים יכולה להנפיש תצוגות במסך על ידי שינוי המאפיינים בפועל באובייקטים של התצוגה. בנוסף, Views גם קורא באופן אוטומטי לשיטה invalidate() כדי לרענן את המסך בכל פעם שהמאפיינים שלו משתנים. המאפיינים החדשים במחלקה View שמקלים על יצירת אנימציות של מאפיינים הם:
-
translationXו-translationY: המאפיינים האלה קובעים את המיקום של התצוגה כדלתא מהקואורדינטות השמאליות והעליונות שלה, שמוגדרות על ידי מאגר הפריסה שלה. -
rotation,rotationXו-rotationY: המאפיינים האלה שולטים בסיבוב בדו-ממד (המאפייןrotation) ובתלת-ממד סביב נקודת הציר. -
scaleXו-scaleY: המאפיינים האלה שולטים בשינוי הגודל הדו-ממדי של תצוגה סביב נקודת הציר שלה. -
pivotXו-pivotY: המאפיינים האלה קובעים את המיקום של נקודת הציר, שסביבה מתבצעים שינויי הסיבוב והקנה מידה. כברירת מחדל, נקודת הציר ממוקמת במרכז האובייקט. -
xו-y: אלה מאפייני כלי עזר פשוטים שמתארים את המיקום הסופי של התצוגה ברכיב המכיל שלה, כסכום של הערכים left ו-top ושל הערכים translationX ו-translationY. -
alpha: מייצג את שקיפות האלפא בתצוגה. ערך ברירת המחדל הוא 1 (אטום), והערך 0 מייצג שקיפות מלאה (לא נראה).
כדי להנפיש מאפיין של אובייקט View, כמו הצבע או ערך הסיבוב שלו, צריך רק ליצור אנימטור מאפיינים ולציין את מאפיין ה-View שרוצים להנפיש. לדוגמה:
Kotlin
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f)
Java
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
מידע נוסף על יצירת אנימטורים זמין בקטעים על אנימציה באמצעות ValueAnimator ו-ObjectAnimator.
יצירת אנימציה באמצעות ViewPropertyAnimator
ה-ViewPropertyAnimator מספק דרך פשוטה להנפשה של כמה מאפיינים של View במקביל, באמצעות אובייקט Animator בסיסי יחיד. היא מתנהגת בדומה ל-ObjectAnimator, כי היא משנה את הערכים בפועל של מאפייני התצוגה, אבל היא יעילה יותר כשמנפישים הרבה מאפיינים בו-זמנית. בנוסף, הקוד לשימוש ב-ViewPropertyAnimator הרבה יותר תמציתי וקל לקריאה. קטעי הקוד הבאים מראים את ההבדלים בשימוש בכמה אובייקטים של ObjectAnimator, באובייקט ObjectAnimator יחיד וב-ViewPropertyAnimator כשמנפישים בו-זמנית את המאפיינים x ו-y של תצוגה.
כמה אובייקטים של ObjectAnimator
Kotlin
val animX = ObjectAnimator.ofFloat(myView, "x", 50f) val animY = ObjectAnimator.ofFloat(myView, "y", 100f) AnimatorSet().apply { playTogether(animX, animY) start() }
Java
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f); ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f); AnimatorSet animSetXY = new AnimatorSet(); animSetXY.playTogether(animX, animY); animSetXY.start();
One ObjectAnimator
Kotlin
val pvhX = PropertyValuesHolder.ofFloat("x", 50f) val pvhY = PropertyValuesHolder.ofFloat("y", 100f) ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start()
Java
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f); PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f); ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start();
ViewPropertyAnimator
Kotlin
myView.animate().x(50f).y(100f)
Java
myView.animate().x(50f).y(100f);
מידע מפורט יותר על ViewPropertyAnimator זמין בפוסט בבלוג של Android Developers.
הצהרה על אנימציות ב-XML
מערכת האנימציה של המאפיינים מאפשרת להצהיר על אנימציות של מאפיינים באמצעות XML במקום לעשות זאת באופן פרוגרמטי. הגדרת האנימציות ב-XML מאפשרת לעשות בהן שימוש חוזר בקלות בכמה פעילויות, ולערוך את רצף האנימציה בקלות רבה יותר.
כדי להבדיל בין קובצי אנימציה שמשתמשים בממשקי ה-API החדשים של אנימציית מאפיינים לבין קובצי אנימציה שמשתמשים במסגרת אנימציית התצוגה מדור קודם, החל מ-Android 3.1, צריך לשמור את קובצי ה-XML של אנימציות המאפיינים בספרייה res/animator/.
מחלקות האנימציה הבאות של מאפיינים תומכות בהצהרת XML עם תגי ה-XML הבאים:
ValueAnimator-<animator>ObjectAnimator-<objectAnimator>AnimatorSet-<set>
כדי למצוא את המאפיינים שאפשר להשתמש בהם בהצהרת ה-XML, אפשר לעיין במאמר בנושא משאבי אנימציה. בדוגמה הבאה, שתי קבוצות האנימציות של האובייקטים מופעלות ברצף, והקבוצה המקוננת הראשונה מפעילה שתי אנימציות של אובייקטים יחד:
<set android:ordering="sequentially"> <set> <objectAnimator android:propertyName="x" android:duration="500" android:valueTo="400" android:valueType="intType"/> <objectAnimator android:propertyName="y" android:duration="500" android:valueTo="300" android:valueType="intType"/> </set> <objectAnimator android:propertyName="alpha" android:duration="500" android:valueTo="1f"/> </set>
כדי להפעיל את האנימציה הזו, צריך להרחיב את משאבי ה-XML בקוד לאובייקט AnimatorSet, ואז להגדיר את אובייקטי היעד לכל האנימציות לפני שמפעילים את קבוצת האנימציות. הקריאה ל-setTarget() מגדירה אובייקט יעד יחיד לכל הצאצאים של AnimatorSet, כדי להקל על השימוש. בדוגמה הבאה אפשר לראות איך עושים את זה:
Kotlin
(AnimatorInflater.loadAnimator(myContext, R.animator.property_animator) as AnimatorSet).apply { setTarget(myObject) start() }
Java
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext, R.animator.property_animator); set.setTarget(myObject); set.start();
אפשר גם להצהיר על ValueAnimator ב-XML, כמו בדוגמה הבאה:
<animator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:valueType="floatType" android:valueFrom="0f" android:valueTo="-100f" />
כדי להשתמש ב-ValueAnimator הקודם בקוד, צריך להרחיב את האובייקט, להוסיף AnimatorUpdateListener, לקבל את ערך האנימציה המעודכן ולהשתמש בו במאפיין של אחד מהתצוגות, כמו שמוצג בקוד הבא:
Kotlin
(AnimatorInflater.loadAnimator(this, R.animator.animator) as ValueAnimator).apply { addUpdateListener { updatedAnimation -> textView.translationX = updatedAnimation.animatedValue as Float } start() }
Java
ValueAnimator xmlAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.animator); xmlAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator updatedAnimation) { float animatedValue = (float)updatedAnimation.getAnimatedValue(); textView.setTranslationX(animatedValue); } }); xmlAnimator.start();
מידע על תחביר ה-XML להגדרת אנימציות של מאפיינים זמין במאמר משאבי אנימציה .
השפעות אפשריות על ביצועי ממשק המשתמש
אנימציות שמעדכנות את ממשק המשתמש גורמות לעבודת רינדור נוספת לכל פריים שבו האנימציה פועלת. לכן, שימוש באנימציות שדורשות הרבה משאבים עלול לפגוע בביצועים של האפליקציה.
העבודה שנדרשת כדי להנפיש את ממשק המשתמש מתווספת לשלב האנימציה של צינור העיבוד. כדי לבדוק אם האנימציות משפיעות על הביצועים של האפליקציה, אפשר להפעיל את האפשרות עיבוד פרופיל ב-GPU ולעקוב אחרי שלב האנימציה. מידע נוסף זמין במאמר הסבר על עיבוד פרופיל ב-GPU.