לפעמים צריך לשנות את המיקום של אובייקטים במסך בגלל אינטראקציה של המשתמש או בגלל עיבוד מאחורי הקלעים. במקום לעדכן מיד את המיקום של האובייקט, מה שגורם לו להבהב ממיקום אחד למיקום אחר, אפשר להשתמש באנימציה כדי להעביר אותו ממיקום ההתחלה למיקום הסיום.
אחת הדרכים שבהן Android מאפשרת לשנות את המיקום של אובייקטים בתצוגה במסך היא באמצעות ObjectAnimator. אתם מציינים את נקודת הסיום שבה אתם רוצים שהאובייקט יתמקם, וגם את משך ההנפשה. אפשר גם להשתמש בפונקציות אינטרפולציה של זמן כדי לשלוט בהאצה או בהאטה של האנימציה.
שינוי מיקום התצוגה באמצעות ObjectAnimator
ObjectAnimator API מאפשר לשנות את המאפיינים של תצוגה למשך זמן מוגדר.
הוא מכיל שיטות סטטיות ליצירת מופעים של ObjectAnimator בהתאם לסוג המאפיין שיוצרים לו אנימציה. כשמשנים את המיקום של התצוגות במסך, משתמשים במאפיינים translationX ו-translationY.
הנה דוגמה ל-ObjectAnimator שמעביר את התצוגה למיקום שנמצא 100 פיקסלים משמאל למסך תוך 2 שניות:
Kotlin
ObjectAnimator.ofFloat(view, "translationX", 100f).apply { duration = 2000 start() }
Java
ObjectAnimator animation = ObjectAnimator.ofFloat(view, "translationX", 100f); animation.setDuration(2000); animation.start();
בדוגמה הזו נעשה שימוש בשיטה ObjectAnimator.ofFloat(), כי ערכי התרגום צריכים להיות מספרים עם נקודה עשרונית. הפרמטר הראשון הוא התצוגה שרוצים להוסיף לה אנימציה. הפרמטר השני הוא הנכס שיוצרים לו אנימציה. מכיוון שהתצוגה צריכה לנוע אופקית, נעשה שימוש במאפיין translationX. הפרמטר האחרון הוא ערך הסיום של האנימציה. בדוגמה הזו, הערך 100 מציין מיקום שרחוק ב-100 פיקסלים מהצד השמאלי של המסך.
בשיטה הבאה מציינים את משך האנימציה, באלפיות השנייה. בדוגמה הזו, האנימציה פועלת במשך 2 שניות (2,000 אלפיות השנייה).
השיטה האחרונה גורמת להפעלת האנימציה, שמעדכנת את מיקום התצוגה במסך.
מידע נוסף על השימוש ב-ObjectAnimator זמין במאמר יצירת אנימציה באמצעות ObjectAnimator.
הוספת תנועה מעוקלת
השימוש בObjectAnimator נוח, אבל כברירת מחדל הוא ממקם מחדש את התצוגה לאורך קו ישר בין נקודת ההתחלה לנקודת הסיום. Material Design מסתמך על עקומות כדי להגדיר את התנועה המרחבית של אובייקטים במסך ואת התזמון של אנימציה. שימוש בתנועה מעוקלת מעניק לאפליקציה מראה יותר מוחשי,
ובמקביל הופך את האנימציות למעניינות יותר.
הגדרת נתיב משלכם
למחלקת ObjectAnimator יש בנאים שמאפשרים להנפיש קואורדינטות
באמצעות שתי מאפיינים או יותר בו-זמנית לאורך נתיב. לדוגמה, האנימטור הבא משתמש באובייקט Path כדי להנפיש את המאפיינים X ו-Y של תצוגה:
Kotlin
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val path = Path().apply { arcTo(0f, 0f, 1000f, 1000f, 270f, -180f, true) } val animator = ObjectAnimator.ofFloat(view, View.X, View.Y, path).apply { duration = 2000 start() } } else { // Create animator without using curved path }
Java
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Path path = new Path(); path.arcTo(0f, 0f, 1000f, 1000f, 270f, -180f, true); ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.X, View.Y, path); animator.setDuration(2000); animator.start(); } else { // Create animator without using curved path }
כך נראית האנימציה של הקשת:
איור 1. אנימציה של נתיב מעוקל.
Interpolator
היא הטמעה של עקומת האצה. מידע נוסף על המושג של עקומות האצה מופיע במאמרי העזרה בנושא Material Design. פונקציית Interpolator
מגדירה איך ערכים ספציפיים באנימציה מחושבים כפונקציה של
הזמן. המערכת מספקת משאבי XML לשלושת העיקולים הבסיסיים במפרט של Material Design:
@interpolator/fast_out_linear_in.xml@interpolator/fast_out_slow_in.xml@interpolator/linear_out_slow_in.xml
שימוש ב-PathInterpolator
המחלקות PathInterpolator הן מחלקות של אינטרפולציה שהוצגו ב-Android 5.0 (API 21). היא מבוססת על עקומת בזייה או על אובייקט Path. בדוגמאות ל-Android בתיעוד של Material Design בנושא easing נעשה שימוש ב-PathInterpolator.
PathInterpolator כולל בנאים שמבוססים על סוגים שונים של עקומות בזייה.
לכל עקומות בזייה יש נקודות התחלה וסיום קבועות ב-(0,0) וב-(1,1), בהתאמה. שאר הארגומנטים של הבנאי תלויים בסוג של עקומת בזייה שנוצרת.
לדוגמה, כדי ליצור עקומת בזייה ריבועית צריך רק את קואורדינטות ה-X וה-Y של נקודת בקרה אחת:
Kotlin
val myInterpolator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { PathInterpolator(0.67f, 0.33f) } else { LinearInterpolator() }
Java
Interpolator myInterpolator = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { myInterpolator = new PathInterpolator(0.67f, 0.33f); } else { myInterpolator = new LinearInterpolator(); }
כך נוצרת עקומת האצה שמתחילה במהירות ומאטה ככל שהיא מתקרבת לסוף.
באופן דומה, לבונה של עקומת בזייה ממעלה שלישית יש נקודות התחלה וסיום קבועות, אבל הוא דורש שתי נקודות בקרה:
Kotlin
val myInterpolator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { PathInterpolator(0.5f, 0.7f, 0.1f, 1.0f) } else { LinearInterpolator() }
Java
Interpolator myInterpolator = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { myInterpolator = new PathInterpolator(0.5f, 0.7f, 0.1f, 1.0f); } else { myInterpolator = new LinearInterpolator(); }
זוהי הטמעה של עקומת ה-easing emphasized decelerate של Material Design.
כדי לשלוט טוב יותר בעקומת ההפחתה, אפשר להשתמש בערך שרירותי Path כדי להגדיר אותה:
Kotlin
val myInterpolator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val path = Path().apply { moveTo(0.0f, 0.0f) cubicTo(0.5f, 0.7f, 0.1f, 1.0f, 1.0f, 1.0f) } PathInterpolator(path) } else { LinearInterpolator() }
Java
Interpolator myInterpolator = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Path path = new Path(); path.moveTo(0.0f, 0.0f); path.cubicTo(0.5f, 0.7f, 0.1f, 1.0f, 1.0f, 1.0f); myInterpolator = new PathInterpolator(path); } else { myInterpolator = new LinearInterpolator(); }
התוצאה היא אותה עקומת שיכוך כמו בדוגמה של עקומת בזייה ממעלה שלישית, אבל במקום זאת נעשה שימוש ב-Path.
אפשר גם להגדיר משתנה אינטרפולציה של נתיב כמשאב XML:
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:controlX1="0.5"
android:controlY1="0.7"
android:controlX2="0.1f"
android:controlY2="1.0f"/>
אחרי שיוצרים אובייקט PathInterpolator, אפשר להעביר אותו לשיטה Animator.setInterpolator(). ה-Animator משתמש במתאם כדי לקבוע את התזמון או את עקומת הנתיב כשהוא מופעל.
Kotlin
val animation = ObjectAnimator.ofFloat(view, "translationX", 100f).apply { interpolator = myInterpolator start() }
Java
ObjectAnimator animation = ObjectAnimator.ofFloat(view, "translationX", 100f); animation.setInterpolator(myInterpolator); animation.start();