ניהול האנימציה של התנועה והווידג'ט בעזרת MotionLayout

אפשר לנסות את הדרך של כתיבת הודעה
‫Jetpack Compose היא ערכת הכלים המומלצת לבניית ממשק משתמש ב-Android. איך משתמשים באנימציות בכתיבה.

MotionLayout הוא סוג פריסה שעוזר לכם לנהל את התנועה ואת האנימציה של הווידג'טים באפליקציה. ‫MotionLayout הוא מחלקת משנה של ‫ConstraintLayout ומבוסס על יכולות הפריסה העשירות שלה. ‫MotionLayout זמין כספריית תמיכה כחלק מהספרייה ConstraintLayout.

MotionLayout מגשר בין מעברים בפריסה לבין טיפול בתנועה מורכבת, ומציע שילוב של תכונות בין מסגרת האנימציה של המאפיינים,‏ TransitionManager ו-CoordinatorLayout.

איור 1. תנועה בסיסית בשליטת מגע.

בנוסף לתיאור המעברים בין פריסות, MotionLayout מאפשרת להנפיש כל מאפיין פריסה. בנוסף, הוא תומך באופן מובנה במעברים שאפשר לדלג עליהם. כלומר, אתם יכולים להציג באופן מיידי כל נקודה במעבר על סמך תנאי מסוים, כמו קלט מגע. ‫MotionLayout תומך גם ב-keyframes, שמאפשרים ליצור מעברים בהתאמה אישית מלאה לפי הצרכים שלכם.

MotionLayout הוא דקלרטיבי לחלוטין, כלומר אפשר לתאר כל מעבר ב-XML, לא משנה כמה הוא מורכב.

שיקולים לגבי העיצוב

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

שנתחיל?

כדי להתחיל להשתמש ב-MotionLayout בפרויקט, פועלים לפי השלבים הבאים.

  1. מוסיפים את התלות ב-ConstraintLayout: כדי להשתמש ב-MotionLayout בפרויקט, מוסיפים את התלות ב-ConstraintLayout גרסה 2.0 לקובץ build.gradle של האפליקציה. אם אתם משתמשים ב-AndroidX, מוסיפים את יחסי התלות הבאים:

    מגניב

    dependencies {
        implementation "androidx.constraintlayout:constraintlayout:2.2.1"
        // To use constraintlayout in compose
        implementation "androidx.constraintlayout:constraintlayout-compose:1.1.1"
    }

    Kotlin

    dependencies {
        implementation("androidx.constraintlayout:constraintlayout:2.2.1")
        // To use constraintlayout in compose
        implementation("androidx.constraintlayout:constraintlayout-compose:1.1.1")
    }
  2. יוצרים קובץ MotionLayout: MotionLayout הוא מחלקת משנה של ConstraintLayout, כך שאפשר להמיר כל ConstraintLayout קיים ל-MotionLayout על ידי החלפת שם המחלקה בקובץ משאבי הפריסה, כמו בדוגמאות הבאות:

    AndroidX

    <!-- before: ConstraintLayout -->
    <androidx.constraintlayout.widget.ConstraintLayout .../>
    <!-- after: MotionLayout -->
    <androidx.constraintlayout.motion.widget.MotionLayout .../>
              

    ספריית תמיכה

    <!-- before: ConstraintLayout -->
    <android.support.constraint.ConstraintLayout .../>
    <!-- after: MotionLayout -->
    <android.support.constraint.motion.MotionLayout .../>
              

    דוגמה מלאה לקובץ MotionLayout, שבו מוגדר הפריסה שמוצגת באיור 1:

    AndroidX

    <?xml version="1.0" encoding="utf-8"?>
    <!-- activity_main.xml -->
    <androidx.constraintlayout.motion.widget.MotionLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/motionLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutDescription="@xml/scene_01"
        tools:showPaths="true">
    
        <View
            android:id="@+id/button"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:background="@color/colorAccent"
            android:text="Button" />
    
    </androidx.constraintlayout.motion.widget.MotionLayout>
            

    ספריית תמיכה

    <?xml version="1.0" encoding="utf-8"?>
    <!-- activity_main.xml -->
    <android.support.constraint.motion.MotionLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/motionLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutDescription="@xml/scene_01"
        tools:showPaths="true">
    
        <View
            android:id="@+id/button"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:background="@color/colorAccent"
            android:text="Button" />
    
    </android.support.constraint.motion.MotionLayout>
            
  3. יצירת MotionScene: בדוגמה הקודמת MotionLayout המאפיין app:layoutDescription מפנה אל סצנת תנועה. סצנת תנועה היא קובץ משאבים בפורמט XML. בתוך רכיב הבסיס <MotionScene>, סצנת תנועה מכילה את כל תיאורי התנועה של הפריסה המתאימה. כדי להפריד בין נתוני הפריסה לבין תיאורי התנועה, כל תג MotionLayout מפנה לסצנת תנועה נפרדת. ההגדרות בסצנת התנועה מקבלות עדיפות על פני הגדרות דומות ב-MotionLayout.

    דוגמה לקובץ של סצנת תנועה שמתארת את התנועה האופקית הבסיסית באיור 1:

    <?xml version="1.0" encoding="utf-8"?>
    <MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:motion="http://schemas.android.com/apk/res-auto">
    
        <Transition
            motion:constraintSetStart="@+id/start"
            motion:constraintSetEnd="@+id/end"
            motion:duration="1000">
            <OnSwipe
                motion:touchAnchorId="@+id/button"
                motion:touchAnchorSide="right"
                motion:dragDirection="dragRight" />
        </Transition>
    
        <ConstraintSet android:id="@+id/start">
            <Constraint
                android:id="@+id/button"
                android:layout_width="64dp"
                android:layout_height="64dp"
                android:layout_marginStart="8dp"
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintStart_toStartOf="parent"
                motion:layout_constraintTop_toTopOf="parent" />
        </ConstraintSet>
    
        <ConstraintSet android:id="@+id/end">
            <Constraint
                android:id="@+id/button"
                android:layout_width="64dp"
                android:layout_height="64dp"
                android:layout_marginEnd="8dp"
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintEnd_toEndOf="parent"
                motion:layout_constraintTop_toTopOf="parent" />
        </ConstraintSet>
    
    </MotionScene>
        

    שימו לב לנקודות הבאות:

    • <Transition> מכיל את ההגדרה הבסיסית של התנועה.

      • motion:constraintSetStart ו-motion:constraintSetEnd הם הפניות לנקודות הקצה של התנועה. נקודות הקצה האלה מוגדרות ברכיבי <ConstraintSet> בהמשך סצנת התנועה.

      • motion:duration מציין את מספר אלפיות השנייה שנדרשות להשלמת התנועה.

    • <OnSwipe> מאפשר ליצור שליטה באמצעות מגע לתנועה.

      • motion:touchAnchorId מתייחס לתצוגה שהמשתמש יכול להחליק ולגרור.

      • motion:touchAnchorSide אומר שהתצוגה נגררת מהצד הימני.

      • motion:dragDirection מתייחס להתקדמות כיוון הגרירה. לדוגמה, motion:dragDirection="dragRight" פירושו שההתקדמות גדלה ככל שגוררים את התצוגה ימינה.

    • <ConstraintSet> כאן מגדירים את האילוצים השונים שמתארים את התנועה. בדוגמה הזו, מוגדר <ConstraintSet> אחד לכל נקודת קצה של התנועה. נקודות הקצה האלה ממוקמות במרכז באופן אנכי באמצעות app:layout_constraintTop_toTopOf="parent" ו-app:layout_constraintBottom_toBottomOf="parent". בצורה אופקית, נקודות הקצה נמצאות בצד ימין ובצד שמאל של המסך.

    לעיון מפורט יותר ברכיבים השונים שסצנת תנועה תומכת בהם, אפשר לעיין בדוגמאות ל-MotionLayout.

מאפיינים שעברו אינטרפולציה

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

  • alpha
  • visibility
  • elevation
  • rotation, rotationX, rotationY
  • translationX, translationY, translationZ
  • scaleX, scaleY

מאפיינים מותאמים אישית

בתוך רכיב <Constraint>, אפשר להשתמש ברכיב <CustomAttribute> כדי לציין מעבר למאפיינים שלא קשורים רק למיקום או למאפייני View.

<Constraint
    android:id="@+id/button" ...>
    <CustomAttribute
        motion:attributeName="backgroundColor"
        motion:customColorValue="#D81B60"/>
</Constraint>

ל-<CustomAttribute> יש שני מאפיינים משלו:

  • הפרמטר motion:attributeName הוא חובה והוא צריך להיות זהה לאובייקט עם שיטות getter ו-setter. הפונקציות getter ו-setter צריכות להתאים לתבנית ספציפית. לדוגמה, יש תמיכה ב-backgroundColor כי לתצוגה יש שיטות בסיסיות של getBackgroundColor() ו-setBackgroundColor().
  • המאפיין השני שחובה לספק תלוי בסוג הערך. בוחרים מבין הסוגים הנתמכים הבאים:
    • motion:customColorValue לצבעים
    • motion:customIntegerValue for integers
    • motion:customFloatValue למספרים עשרוניים
    • motion:customStringValue למחרוזות
    • motion:customDimension למאפיינים
    • motion:customBoolean לערכים בוליאניים

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

שינוי צבע הרקע

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

איור 2. צבע הרקע של התצוגה משתנה כשהיא זזה.

מוסיפים רכיב <CustomAttribute> לכל רכיב ConstraintSet, כמו שמוצג בקטע הקוד הבא:

<ConstraintSet android:id="@+id/start">
    <Constraint
        android:id="@+id/button"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginStart="8dp"
        motion:layout_constraintBottom_toBottomOf="parent"
        motion:layout_constraintStart_toStartOf="parent"
        motion:layout_constraintTop_toTopOf="parent">
        <CustomAttribute
            motion:attributeName="backgroundColor"
            motion:customColorValue="#D81B60" />
    </Constraint>
</ConstraintSet>

<ConstraintSet android:id="@+id/end">
    <Constraint
        android:id="@+id/button"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginEnd="8dp"
        motion:layout_constraintBottom_toBottomOf="parent"
        motion:layout_constraintEnd_toEndOf="parent"
        motion:layout_constraintTop_toTopOf="parent">
        <CustomAttribute
            motion:attributeName="backgroundColor"
            motion:customColorValue="#9999FF" />
    </Constraint>
</ConstraintSet>

מאפיינים נוספים של MotionLayout

בנוסף למאפיינים בדוגמה הקודמת, ל-MotionLayout יש מאפיינים אחרים שאולי תרצו לציין:

  • app:applyMotionScene="boolean" מציין אם להחיל את סצנת התנועה. ערך ברירת המחדל של המאפיין הזה הוא true.
  • app:showPaths="boolean" מציין אם להציג את נתיבי התנועה בזמן שהתנועה פועלת. ערך ברירת המחדל של המאפיין הזה הוא false.
  • app:progress="float" מאפשר לציין במפורש את התקדמות המעבר. אפשר להשתמש בכל ערך של נקודה צפה מ-0 (ההתחלה של המעבר) עד 1 (הסוף של המעבר).
  • אפשר להשתמש ב-app:currentState="reference" כדי לציין ConstraintSet ספציפי.
  • app:motionDebug מאפשר להציג מידע נוסף על ניפוי הבאגים של התנועה. הערכים האפשריים הם "SHOW_PROGRESS",‏ "SHOW_PATH" או "SHOW_ALL".

מקורות מידע נוספים

מידע נוסף על MotionLayout זמין במקורות המידע הבאים: