מוסיפים אנימציה של תנועה באמצעות פיזיקה של אביב

רוצה לנסות את שיטת הכתיבה?
'Jetpack פיתוח נייטיב' היא ערכת הכלים המומלצת לממשק המשתמש ל-Android. איך משתמשים באנימציות במצב 'כתיבה'

מבוססים על פיזיקה תנועה שמונעת מכוח. כוח האביב הוא אחד מהכוח שמנחה אינטראקטיביות ותנועה. לכוח קפיץ יש את המאפיינים הבאים: הרגישות והנוקשות. באנימציה של קפיץ, הערך המהירות מחושבת על סמך כוח הקפיץ שמופעל על כל אחד מסגרת.

אם אתם רוצים שהאנימציות באפליקציה יאטו בכיוון אחד בלבד, מומלץ להשתמש בשיטת חיכוך אנימציה מהבהבת במקום זאת.

מחזור החיים של אנימציה באביב

באנימציה של אביב, SpringForce מאפשר לך להתאים אישית את רמת הקפיציות, את יחס הריכוך המיקום הסופי. ברגע שהאנימציה מתחילה, כוח האביב מתעדכן ערך האנימציה והמהירות של כל פריים. האנימציה ממשיכה עד שכוח הקפיץ מגיע לשיווי משקל.

לדוגמה, אם גוררים סמל אפליקציה לאורך המסך ומשחררים אותו מאוחר יותר על ידי הרמת האצבע מהסמל, הסמל נמשך בחזרה למקור במקום בלתי נראה אבל מוכר.

איור 1 מדגים אפקט אביבי דומה. סימן הפלוס (+) באמצע העיגול מציין את הכוח שמופעל באמצעות תנועת מגע.

גרסת אביב
איור 1. אפקט לאביב

יצירת אנימציה לאביב

השלבים הכלליים ליצירת אנימציה של האביב לאפליקציה שלך הם: ככה:

בקטעים הבאים נדון בשלבים הכלליים של בניית מעיין את האנימציה בפירוט.

הוספה של ספריית התמיכה

כדי להשתמש בספריית התמיכה המבוססת על פיזיקה, צריך להוסיף את ספריית התמיכה לפרויקט ככה:

  1. פותחים את הקובץ build.gradle של מודול האפליקציה.
  2. צריך להוסיף את ספריית התמיכה לקטע dependencies.

    מגניב

            dependencies {
                def dynamicanimation_version = '1.0.0'
                implementation "androidx.dynamicanimation:dynamicanimation:$dynamicanimation_version"
            }
            

    Kotlin

            dependencies {
                val dynamicanimation_version = "1.0.0"
                implementation("androidx.dynamicanimation:dynamicanimation:$dynamicanimation_version")
            }
            

    כדי להציג את הגרסאות הנוכחיות של הספרייה הזו, אפשר לעיין במידע על דינמיות בדף גרסאות.

יצירת אנימציה לאביב

הכיתה SpringAnimation מאפשרת ליצור אנימציה של קפיץ לאובייקט. כדי ליצור אנימציה לאביב, צריך יוצרים מופע של SpringAnimation ולספק אובייקט, נכס של אובייקט שרוצים להוסיף לו אנימציה, מיקום קפיצי אופציונלי שבו רוצים לאפשר מנוחה של האנימציה.

הערה: בזמן היצירה של האנימציה באביב, מיקום הקפיץ הוא אופציונלי. עם זאת, חובה להגדיר לפני שאתם מתחילים את האנימציה.

Kotlin

val springAnim = findViewById<View>(R.id.imageView).let { img ->
    // Setting up a spring animation to animate the view’s translationY property with the final
    // spring position at 0.
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y, 0f)
}

Java

final View img = findViewById(R.id.imageView);
// Setting up a spring animation to animate the view’s translationY property with the final
// spring position at 0.
final SpringAnimation springAnim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y, 0);

האנימציה שמבוססת על האביב יכולה להנפיש את הצפיות במסך על ידי שינוי בפועל באובייקטים של התצוגה. התצוגות הבאות זמינות ב המערכת:

  • ALPHA: מייצג את השקיפות של גרסת האלפא בתצוגה. הערך הוא 1 (אטום) ב- ברירת מחדל, כאשר ערך 0 מייצג שקיפות מלאה (לא גלוי).
  • TRANSLATION_X, TRANSLATION_Y, וגם TRANSLATION_Z: אלה מאפיינים קובעים את מיקום התצוגה כדלתא משמאל קואורדינטות, קואורדינטה עליונה וגובה, שנקבעים לפי הפריסה מאגר תגים.
    • TRANSLATION_X מתאר את הקואורדינטה השמאלית.
    • TRANSLATION_Y מתארת את הקואורדינטה העליונה.
    • TRANSLATION_Z מתאר את עומק התצוגה ביחס לגובה שלה.
  • ROTATION, ROTATION_X, וגם ROTATION_Y: אלה הנכסים שולטים בסבב בדו-ממד (נכס rotation) תלת-ממד סביב נקודת הציר.
  • SCROLL_X והקבוצה SCROLL_Y: אלה מאפיינים מציינים את היסט הגלילה של המקור משמאל והקצה העליון בפיקסלים. הוא גם מציין את המיקום במונחים של עלות הדף גוללים.
  • SCALE_X והקבוצה SCALE_Y: אלה מאפיינים קובעים את קנה המידה הדו-ממדי של תצוגה לפי נקודת הציר שלה.
  • X, Y, וגם Z: אלה בסיסיים שמתארת את המיקום הסופי של התצוגה לפי מאגר תגים.

רישום מאזינים

המחלקה DynamicAnimation מספקת מאזינים: OnAnimationUpdateListener ו-OnAnimationEndListener. המאזינים האלה מאזינים לעדכונים באנימציה, למשל כשיש משתנה בערך האנימציה וכשהאנימציה מסתיימת.

OnAnimationUpdateListener

אם רוצים ליצור אנימציה של כמה תצוגות כדי ליצור אנימציה משורשרת, צריך יכול להגדיר מכשיר OnAnimationUpdateListener כדי לקבל התקשרות חזרה בכל פעם שיש שינוי בתצוגה הנוכחית לנכס. הקריאה החוזרת מיידעת את הצופים האחרים על עדכון מיקום האביב על סמך השינוי שבוצע בנכס של התצוגה המפורטת הנוכחית. כדי לרשום את ל-listener, מבצעים את השלבים הבאים:

  1. התקשרות אל addUpdateListener() ולצרף את ה-listener לאנימציה.

    הערה: צריך לרשום את העדכון שמאזין לפני תחילת האנימציה. עם זאת, אוזן העדכון צריך להיות רשום רק אם צריך עדכון לכל פריים בערך האנימציה שינויים. אוזן עדכון מונע מהאנימציה פועל בשרשור נפרד.

  2. שינוי של onAnimationUpdate() שמודיע למתקשר על השינוי באובייקט הנוכחי. הקוד לדוגמה הבא ממחיש את השימוש הכולל OnAnimationUpdateListener

Kotlin

// Setting up a spring animation to animate the view1 and view2 translationX and translationY properties
val (anim1X, anim1Y) = findViewById<View>(R.id.view1).let { view1 ->
    SpringAnimation(view1, DynamicAnimation.TRANSLATION_X) to
            SpringAnimation(view1, DynamicAnimation.TRANSLATION_Y)
}
val (anim2X, anim2Y) = findViewById<View>(R.id.view2).let { view2 ->
    SpringAnimation(view2, DynamicAnimation.TRANSLATION_X) to
            SpringAnimation(view2, DynamicAnimation.TRANSLATION_Y)
}

// Registering the update listener
anim1X.addUpdateListener { _, value, _ ->
    // Overriding the method to notify view2 about the change in the view1’s property.
    anim2X.animateToFinalPosition(value)
}

anim1Y.addUpdateListener { _, value, _ -> anim2Y.animateToFinalPosition(value) }

Java

// Creating two views to demonstrate the registration of the update listener.
final View view1 = findViewById(R.id.view1);
final View view2 = findViewById(R.id.view2);

// Setting up a spring animation to animate the view1 and view2 translationX and translationY properties
final SpringAnimation anim1X = new SpringAnimation(view1,
        DynamicAnimation.TRANSLATION_X);
final SpringAnimation anim1Y = new SpringAnimation(view1,
    DynamicAnimation.TRANSLATION_Y);
final SpringAnimation anim2X = new SpringAnimation(view2,
        DynamicAnimation.TRANSLATION_X);
final SpringAnimation anim2Y = new SpringAnimation(view2,
        DynamicAnimation.TRANSLATION_Y);

// Registering the update listener
anim1X.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {

// Overriding the method to notify view2 about the change in the view1’s property.
    @Override
    public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value,
                                  float velocity) {
        anim2X.animateToFinalPosition(value);
    }
});

anim1Y.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {

  @Override
    public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value,
                                  float velocity) {
        anim2Y.animateToFinalPosition(value);
    }
});

OnAnimationEndListener

OnAnimationEndListener מודיע על סיום אנימציה. יש לך אפשרות להגדיר את המאזינים לקבל הקריאה החוזרת (callback) בכל פעם שהאנימציה מגיעה לשיווי משקל או שהיא מתבטלת. שפת תרגום לרשום את ה-listener, מבצעים את השלבים הבאים:

  1. התקשרות אל addEndListener() ולצרף את ה-listener לאנימציה.
  2. שינוי של onAnimationEnd() שיטה לקבלת התראה בכל פעם שאנימציה מגיעה לשיווי משקל או שהוא מבוטל.

הסרת מאזינים

כדי להפסיק לקבל קריאות חוזרות (callbacks) ואנימציה של סיום הקריאה החוזרת, שיחה אל removeUpdateListener() ו-removeEndListener() שיטות, בהתאמה.

הגדרת הערך להתחלת האנימציה

כדי להגדיר את ערך ההתחלה של האנימציה, קוראים לפונקציה setStartValue() ומעבירים את ערך ההתחלה של האנימציה. אם לא מגדירים start, האנימציה משתמשת בערך הנוכחי של מאפיין האובייקט כערך ההתחלה.

הגדרת טווח של ערכי אנימציה

תוכלו להגדיר את ערכי האנימציה המינימליים והמקסימליים כשרוצים: להגביל את ערך המאפיין לטווח מסוים. היא גם עוזרת לשלוט טווח אם אתם מוסיפים אנימציה לנכסים שיש להם טווח פנימי, כמו alpha (מ-0 עד 1).

  • כדי להגדיר את הערך המינימלי, קוראים לפונקציה setMinValue() ומעביר את הערך המינימלי של הנכס.
  • כדי להגדיר את הערך המקסימלי, צריך להפעיל את setMaxValue() ומעביר את הערך המקסימלי של הנכס.

שתי השיטות מחזירות את האנימציה שעבורה הערך מוגדר.

הערה: אם הגדרתם את ערך ההתחלה ויש לכם הגדרתם טווח של ערכי אנימציה, ודאו שערך ההתחלה נמצא בתוך את טווח הערכים המינימלי ואת טווח הערכים המקסימלי.

הגדרה של מהירות ההתחלה

מהירות ההתחלה מגדירה את המהירות שבה מאפיין האנימציה משתנה בתחילת האנימציה. מהירות ההתחלה שמוגדרת כברירת מחדל היא אפס פיקסלים לשנייה. אפשר להגדיר את המהירות גם לפי מהירות המגע או על ידי שימוש בערך קבוע כמהירות ההתחלה. אם בוחרים מספק ערך קבוע, מומלץ להגדיר את הערך ב-dp לשנייה לאחר מכן ממירים אותו לפיקסלים לשנייה. הגדרת הערך ב-dp לשנייה מאפשר למהירות להיות בלתי תלויה בצפיפות ובגורמי צורה. לקבלת מידע נוסף על המרת ערך לפיקסלים לשנייה, המרת dp לשנייה לפיקסלים לשנייה .

כדי להגדיר את המהירות, setStartVelocity() ומעבירים את המהירות בפיקסלים לשנייה. ה-method מחזירה את הערך אובייקט כוח האביב שעליו מוגדרת המהירות.

הערה: השתמשו ב- GestureDetector.OnGestureListener או VelocityTracker שיטות כיתה לאחזור ולחישוב המהירות של תנועות מגע.

Kotlin

findViewById<View>(R.id.imageView).also { img ->
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
        
        // Compute velocity in the unit pixel/second
        vt.computeCurrentVelocity(1000)
        val velocity = vt.yVelocity
        setStartVelocity(velocity)
    }
}

Java

final View img = findViewById(R.id.imageView);
final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);

// Compute velocity in the unit pixel/second
vt.computeCurrentVelocity(1000);
float velocity = vt.getYVelocity();
anim.setStartVelocity(velocity);

ממירה את dp לשנייה לפיקסלים לשנייה

מהירות הקפיץ חייבת להיות בפיקסלים לשנייה. אם תבחר לספק ערך קבוע כהתחלת המהירות, מספקים את הערך ב-dp לשנייה ולאחר מכן ממירים אותו לפיקסלים לשנייה. כדי ליצור המרה, משתמשים applyDimension() מהמחלקה TypedValue. עיינו ב הקוד לדוגמה הבא:

Kotlin

val pixelPerSecond: Float =
    TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, resources.displayMetrics)

Java

float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, getResources().getDisplayMetrics());

הגדרת נכסי אביב

המחלקה SpringForce מגדירה את המקבל ואת שיטות המגדירות לכל אחד ממאפייני הקפיץ, כגון ריכוך יחס ונוקשות. כדי להגדיר את מאפייני Spring, חשוב מאחזרים את חפץ כוח הקפיץ או יוצרים כוח קפיץ מותאם אישית שעליו אפשר להגדיר את המאפיינים. למידע נוסף על יצירת התאמה אישית של את כוח האביב, יצירת כוחות אביב בהתאמה אישית .

טיפ: כשאתם משתמשים ב-methoder, אתם יכולים יוצרים שרשרת שיטות, שבה כל שיטות הממיר מחזירה את כוח הקפיץ לאובייקט.

יחס ריפוי

יחס הדחיסה מתאר הפחתה הדרגתית בתנודה של האביב. על ידי באמצעות יחס הדחיסה, אפשר להגדיר באיזו מהירות התנודה של התנודות מחזרה אחת לשנייה. יש ארבע דרכים שונות לצמצום אביב:

  • הפחתת יתר מתרחשת כשיחס הקטנת הנפח גדול מ-1. הוא מאפשר העצם חוזר בעדינות למצב המנוחה.
  • ריכוך קריטי מתרחש כאשר יחס הדחיסה שווה ל-1. הוא מאפשר האובייקט יחזור למיקום השאר בתוך פרק הזמן הקצר ביותר.
  • מתחת לפגיעוּת עם הזום מתרחשים כשיחס הדחיסה נמוך מ-1. הוא מאפשר האובייקט חלף מספר פעמים על ידי העברת תנוחת המנוחה, ואז מגיע בהדרגה למצב מנוחה.
  • ללא דחיסה מתרחש כאשר יחס הדחיסה שווה לאפס. היא מאפשרת לתנודה לתמיד.

כדי להוסיף את יחס הדחיסה לקפיץ, מבצעים את השלבים הבאים:

  1. התקשרות אל getSpring() שמאחזרת את הקפיץ כדי להוסיף את יחס הדחיסה.
  2. התקשרות אל setDampingRatio() ומעבירים את יחס הדחיסה שרוצים להוסיף לאביב. מחזירה את אובייקט כוח הקפיץ שעליו מוגדר יחס הדחיסה.

    הערה: יחס דחיסה חייב להיות מספר לא שלילי. אם מגדירים את יחס הדחיסה ל-0, הקפיץ אף פעם לא מגיעים למצב של מנוחה. במילים אחרות, הוא זז לנצח.

הקבועים הבאים של יחס ריכוך זמינים במערכת:

איור 2: החזרה גבוהה

איור 3: החזרה בינונית

איור 4: החזרה נמוכה

איור 5: ללא החזרה

יחס הדחיסה שמוגדר כברירת מחדל הוא DAMPING_RATIO_MEDIUM_BOUNCY.

Kotlin

findViewById<View>(R.id.imageView).also { img ->
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
        
        // Setting the damping ratio to create a low bouncing effect.
        spring.dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY
        
    }
}

Java

final View img = findViewById(R.id.imageView);
final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);

// Setting the damping ratio to create a low bouncing effect.
anim.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY);

נוקשות

הנוקשות מגדירה את קבוע הקפיץ, המודד את חוזק אביב. קפיץ קשיח מפעיל יותר כוח על העצם שמחובר כשהקפיץ לא בתנוחת מנוחה. כדי להוסיף את הנוקשות לקפיץ, מבצעים את השלבים הבאים:

  1. התקשרות אל getSpring() כדי לאחזר את הקצוות כדי להוסיף את הנוקשות.
  2. התקשרות אל setStiffness() ומעבירים את ערך הקשיחות שרוצים להוסיף לאביב. מחזירה את אובייקט כוח הקפיץ שעליו מוגדרת הנוקשות.

    הערה: האיכות צריכה להיות מספר חיובי.

הקבועים הבאים של קשיחות זמינים במערכת:

איור 6: קשיחות גבוהה

איור 7: קשיחות בינונית

איור 8: רמת קושי נמוכה

איור 9: רמת קושי נמוכה מאוד

רמת החומרה שמוגדרת כברירת מחדל היא STIFFNESS_MEDIUM.

Kotlin

findViewById<View>(R.id.imageView).also { img ->
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
        
        // Setting the spring with a low stiffness.
        spring.stiffness = SpringForce.STIFFNESS_LOW
        
    }
}

Java

final View img = findViewById(R.id.imageView);
final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);

// Setting the spring with a low stiffness.
anim.getSpring().setStiffness(SpringForce.STIFFNESS_LOW);

יצירת כוח אביב בהתאמה אישית

אפשר ליצור כוח קפיץ בהתאמה אישית כחלופה לשימוש בברירת המחדל של אביב. כוח האביב בהתאמה אישית מאפשר לך לחלוק את אותו כוח קפיץ בכמה אנימציות של האביב. לאחר יצירת האביב כוח, ניתן להגדיר מאפיינים כמו יחס ריכוך ונוקשות.

  1. יוצרים אובייקט SpringForce.

    SpringForce force = new SpringForce();

  2. כדי להקצות את המאפיינים, שולחים קריאה ל-methods המתאימות. אפשר גם יוצרים שרשרת method.

    force.setDampingRatio(DAMPING_RATIO_LOW_BOUNCY).setStiffness(STIFFNESS_LOW);

  3. התקשרות אל setSpring() כדי להגדיר קפיץ לאנימציה.

    setSpring(force);

התחלת האנימציה

יש שתי דרכים להתחיל אנימציית אביב: start() או על ידי שליחת קריאה animateToFinalPosition() . צריך לקרוא לשתי השיטות ב-thread הראשי.

animateToFinalPosition() מבצעת שתי משימות:

  • מגדיר את המיקום הסופי של הקפיץ.
  • מפעיל את האנימציה, אם היא עדיין לא התחילה.

מכיוון שהשיטה מעדכנת את המיקום הסופי של האביב ומתחילה אנימציה במקרה הצורך, אפשר לקרוא לשיטה הזו בכל שלב כדי לשנות את הקורס של אנימציה. לדוגמה, באנימציית אביב משורשרת, האנימציה של תצוגה אחת תלויה בתצוגה אחרת. באנימציה כזו, הרגשת שנוח להשתמש animateToFinalPosition() . אם משתמשים בשיטה הזו באנימציית אביב משורשרת, אין צורך אין מה לדאוג, אם האנימציה שרוצים לעדכן פועלת כרגע.

איור 10 ממחיש אנימציית אביב משורשרת, שבה אנימציה של תלויה בתצוגה אחרת.

הדגמה לאביב עם שרשרת
איור 10. הדגמה לאביב בשרשרת

כדי להשתמש בanimateToFinalPosition() מפעילים את הפונקציה animateToFinalPosition() ומעבירים את שארית האביב. אפשר גם להגדיר את השאר של מיקום האביב באמצעות קריאה setFinalPosition() .

השיטה start() לא להגדיר את ערך הנכס לערך ההתחלה באופן מיידי. הנכס הערך משתנה בכל דופק של האנימציה, שמתרחש לפני אישור הציור. כתוצאה מכך, השינויים ישתקפו בפריים הבא, כאילו הערכים מוגדרים באופן מיידי.

Kotlin

findViewById<View>(R.id.imageView).also { img ->
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
        
        // Starting the animation
        start()
        
    }
}

Java

final View img = findViewById(R.id.imageView);
final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);

// Starting the animation
anim.start();

ביטול האנימציה

אפשר לבטל את האנימציה או לדלג אליה בסוף האנימציה. מצב אידיאלי שבו צריך לבטל את הקישור או לדלג לסוף הקישור, כשהמשתמש מחייבת את האנימציה להפסיק מיידית. הדבר בעיקר כשמשתמש יוצא מאפליקציה בפתאומיות או בתצוגה הופכת לבלתי נראית.

יש שתי שיטות שבהן אפשר להשתמש כדי לסיים את האנימציה. השיטה cancel() מסיים את האנימציה בערך שבו היא נמצאת. אמצעי תשלום אחד (skipToEnd()) מדלג על האנימציה לערך הסופי ואז מסיים אותה.

לפני שאפשר לסיים את האנימציה, חשוב לבדוק של האביב. אם המצב לא ריק, האנימציה אף פעם לא יכולה להגיע אל ושאר המיקום. כדי לבדוק את מצב האביב, אמצעי תשלום אחד (canSkipToEnd()). אם המיקום הקפיץ נחלש, השיטה מחזירה true, אחרת false.

אחרי שיודעים את מצב האביב, אפשר לסיים את האנימציה עד באמצעות אמצעי תשלום אחד (skipToEnd()) או cancel(). אמצעי תשלום אחד (cancel()) חובה לבצע קריאה רק בשרשור הראשי.

הערה: באופן כללי, skipToEnd() סיבות לשיטה קפיצה חזותית.