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

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

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

איור 1. תנועה בסיסית מבוקרת במגע.

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

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

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

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

שנתחיל?

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

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

    מגניב

    dependencies {
        implementation "androidx.constraintlayout:constraintlayout:2.2.0-beta01"
        // To use constraintlayout in compose
        implementation "androidx.constraintlayout:constraintlayout-compose:1.1.0-beta01"
    }
    

    Kotlin

    dependencies {
        implementation("androidx.constraintlayout:constraintlayout:2.2.0-beta01")
        // To use constraintlayout in compose
        implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-beta01")
    }
    
  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. Create a 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". אופקית, נקודות הקצה נמצאות בקצה השמאלי והימני של המסך.

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

מאפיינים שהומרו לפוליגונים

בתוך קובץ של סצנה של תנועה, רכיבי 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 למספרים שלמים
    • motion:customFloatValue לערכים צפים
    • motion:customStringValue למחרוזות
    • motion:customDimension למאפיינים
    • motion:customBoolean עבור ערכים בוליאניים

כשמציינים מאפיין מותאם אישית, צריך להגדיר את ערכי נקודות הקצה גם באלמנט <ConstraintSet> של ההתחלה וגם באלמנט <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" אפשר לציין במפורש את התקדמות המעבר. אפשר להשתמש בכל ערך של נקודה צפה (floating-point) מ-0 (התחלת המעבר) ל-1 (סיום המעבר).
  • app:currentState="reference" מאפשר לציין ConstraintSet ספציפי.
  • app:motionDebug מאפשרת להציג מידע נוסף על ניפוי הבאגים של התנועה. הערכים האפשריים הם "SHOW_PROGRESS",‏ "SHOW_PATH" או "SHOW_ALL".

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

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