إدارة الصور المتحركة والتطبيقات المصغّرة باستخدام 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، أضِف التبعية التالية:

    Groovy

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

    Kotlin

    dependencies {
        implementation("androidx.constraintlayout:constraintlayout:2.2.0")
        // To use constraintlayout in compose
        implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0")
    }
    
  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. يجب أن يتطابقا مع نمط معيّن. على سبيل المثال، يُسمح باستخدام backgroundColor لأنّ طريقة العرض تتضمّن getBackgroundColor() وsetBackgroundColor().
  • تستند السمة الأخرى التي يجب تقديمها إلى نوع القيمة. اختَر من بين الأنواع المتوافقة التالية:
    • motion:customColorValue للألوان
    • motion:customIntegerValue للأرقام الصحيحة
    • motion:customFloatValue للعناصر العائمة
    • motion:customStringValue للسلاسل
    • motion:customDimension للسمات
    • motion:customBoolean للقيم المنطقية

عند تحديد سمة مخصّصة، حدِّد قيم نقاط النهاية في كلّ من عنصرَي start و end <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، يمكنك الاطّلاع على المراجع التالية: