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

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

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

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

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