1. قبل البدء
هذا الدرس التطبيقي حول الترميز هو جزء من الدورة التدريبية المتقدّمة لنظام التشغيل Android باستخدام لغة Kotlin. وستحصل على أقصى قيمة من هذه الدورة التدريبية إذا كنت تعمل من خلال الدروس التطبيقية حول الترميز بالتسلسل، ولكنها ليست إلزامية. يتم إدراج جميع الدروس التطبيقية حول ترميز الدورة التدريبية في الصفحة المقصودة للدروس التطبيقية حول الترميز باستخدام تكنولوجيا Android المتقدمة.
MotionLayout
هي مكتبة تتيح لك إضافة حركة غنية إلى تطبيق Android. تستند هذه الميزة إلى ConstraintLayout,
، وتتيح لك إضافة تأثيرات حركية إلى أي شيء يمكنك إنشاؤه باستخدام ConstraintLayout
.
يمكنك استخدام MotionLayout
لتحريك الموقع والحجم ومستوى الرؤية وألفا واللون والمسقط الرأسي والتدوير وغيرها من سمات طرق العرض المتعددة في الوقت نفسه. باستخدام ملف XML التعريفي، يمكنك إنشاء صور متحركة منسّقة تتضمن عدة طرق عرض يصعب الحصول عليها من خلال الرموز البرمجية.
تشكّل الصور المتحركة طريقة رائعة لتحسين تجربة استخدام التطبيق. يمكنك استخدام الرسوم المتحركة من أجل:
- عرض التغييرات: تتيح الحركة بين الحالات للمستخدم تتبُّع التغييرات في واجهة المستخدم بشكل طبيعي.
- لفت الانتباه: استخدِم الصور المتحركة لجذب الانتباه إلى عناصر واجهة المستخدم المهمة.
- إنشاء تصميمات جميلة: الحركة الفعالة في التصميم تجعل التطبيقات تبدو راقية.
المتطلبات الأساسية
تم تصميم هذا الدرس التطبيقي حول الترميز للمطورين الذين لديهم بعض الخبرة في تطوير تطبيقات Android. قبل محاولة إكمال هذا الدرس التطبيقي حول الترميز، عليك تنفيذ ما يلي:
- يمكنك التعرّف على كيفية إنشاء تطبيق باستخدام نشاط وتصميم أساسي وتشغيله على جهاز أو محاكي باستخدام "استوديو Android". مزيد من المعلومات عن "
ConstraintLayout
" يمكنك الاطّلاع على الدرس التطبيقي حول ترميز تنسيق القيود لمعرفة مزيد من المعلومات حولConstraintLayout
.
الأنشطة
- تحديد صورة متحركة باستخدام
ConstraintSets
وMotionLayout
- التحريك بناءً على أحداث السحب
- تغيير الصورة المتحركة باستخدام "
KeyPosition
" - تغيير السمات باستخدام
KeyAttribute
- تشغيل الصور المتحركة باستخدام رمز
- تحريك العناوين القابلة للتصغير باستخدام "
MotionLayout
"
المتطلبات
- الإصدار 4.0 من "استوديو Android" (لا يعمل محرِّر
MotionLayout
إلا مع هذا الإصدار من "استوديو Android").
2. البدء
لتنزيل نموذج التطبيق، يمكنك تنفيذ أحد الإجراءَين التاليَين:
... أو استنساخ مستودع GitHub من سطر الأوامر باستخدام الأمر التالي:
$ git clone https://github.com/googlecodelabs/motionlayout.git
3- إنشاء صور متحركة باستخدام MotionLayout
أولاً، ستنشئ رسمًا متحركًا ينقل طريقة العرض من أعلى الشاشة إلى النهاية السفلية استجابةً لنقرات المستخدم.
لإنشاء رسم متحرك من رمز البداية، ستحتاج إلى القطع الرئيسية التالية:
MotionLayout,
التي هي فئة فرعية منConstraintLayout
. أنت تحدّد جميع طرق العرض التي سيتم تحريكها داخل العلامةMotionLayout
.MotionScene,
، وهو ملف XML يصف صورة متحركة لـ "MotionLayout.
".- تمثّل هذه السمة
Transition,
جزءًا منMotionScene
وتحدّد مدة الحركة وطريقة تشغيلها وكيفية نقل طرق العرض. ConstraintSet
: يحدد كل من قيد start وend الانتقال.
لنلقِ نظرة على كل من هذه الخيارات بالترتيب، بدءًا من MotionLayout
.
الخطوة 1: استكشاف الرمز الحالي
MotionLayout
هي فئة فرعية من ConstraintLayout
، لذا فهي تتيح كل الميزات نفسها عند إضافة الحركة. لاستخدام MotionLayout
، عليك إضافة ملف شخصي على MotionLayout
حيث يمكنك استخدام ConstraintLayout.
.
- في
res/layout
، افتحactivity_step1.xml.
هنا، لديكConstraintLayout
معImageView
نجمة واحدة، مع تطبيق تلوين خفيف بداخله.
activity_step1.xml
<!-- initial code -->
<androidx.constraintlayout.widget.ConstraintLayout
...
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="@+id/red_star"
...
/>
</androidx.constraintlayout.motion.widget.MotionLayout>
لا يتم فرض أي قيود على ConstraintLayout
هذا، لذا إذا أردت تشغيل التطبيق الآن، ستظهر لك شاشة النجمة بدون قيود، ما يعني أنّه سيتم وضعها في موقع غير معروف. سيُرسل لك "استوديو Android" تحذيرًا بشأن عدم توفُّر قيود.
الخطوة 2: التحويل إلى "تخطيط الحركة"
لإجراء رسم متحرك باستخدام MotionLayout,
، يجب تحويل ConstraintLayout
إلى MotionLayout
.
لكي يستخدم التخطيط مشهدًا متحركًا، يجب أن يشير إليه.
- للقيام بذلك، افتح سطح التصميم. في Android Studio 4.0، يمكنك فتح مساحة التصميم باستخدام رمز التقسيم أو التصميم في أعلى يسار الصفحة عند الاطّلاع على ملف XML للتنسيق.
- بعد فتح سطح التصميم، انقر بزر الماوس الأيمن على المعاينة واختَر التحويل إلى MotionLayout.
يؤدي ذلك إلى استبدال العلامة ConstraintLayout
بعلامة MotionLayout
وإضافة motion:layoutDescription
إلى العلامة MotionLayout
التي تشير إلى @xml/activity_step1_scene.
.
activity_step1**.xml**
<!-- explore motion:layoutDescription="@xml/activity_step1_scene" -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:layoutDescription="@xml/activity_step1_scene">
مشهد الحركة هو ملف XML واحد يصف الحركة في صورة MotionLayout
.
بعد التحويل إلى MotionLayout
، سيعرض سطح التصميم "أداة تعديل الصور المتحركة"
هناك ثلاثة عناصر جديدة في واجهة المستخدم في "محرِّر الحركة":
- نظرة عامة: تحديد شكلي يتيح لك اختيار أجزاء مختلفة من الصورة المتحركة. في هذه الصورة، تم اختيار
ConstraintSet
start
. يمكنك أيضًا اختيار الانتقال بينstart
وend
من خلال النقر على السهم بينهما. - القسم: أسفل النظرة العامة، تظهر نافذة قسم تتغيّر استنادًا إلى عنصر النظرة العامة المحدَّد حاليًا. في هذه الصورة، يتم عرض معلومات
start
ConstraintSet
في نافذة الاختيار. - السمة – تعرض لوحة السمات وتتيح لك تعديل سمات العنصر الحالي المحدّد من خلال نافذة النظرة العامة أو نافذة الاختيار. وتعرض في هذه الصورة سمات
start
ConstraintSet
.
الخطوة 3: تحديد قيود البدء والانتهاء
يمكن تحديد جميع الرسوم المتحركة من حيث البداية والنهاية. تصف البداية الشكل الذي تبدو عليه الشاشة قبل الرسوم المتحركة، وتصف النهاية كيف تبدو الشاشة بعد اكتمال الرسم المتحرك. إنّ "MotionLayout
" مسؤول عن معرفة كيفية إنشاء الصور المتحركة بين حالتَي البداية والنهاية (مع مرور الوقت).
تستخدم السمة MotionScene
العلامة ConstraintSet
لتحديد حالتَي البدء والانتهاء. ConstraintSet
هي عبارة عن مجموعة من القيود التي يمكن تطبيقها على طرق العرض. ويشمل ذلك العرض والارتفاع وقيود ConstraintLayout
. وتشتمل أيضًا على بعض السمات مثل alpha
. ولا تتضمن المشاهدات، بل القيود المفروضة على هذه المشاهدات فقط.
أي قيود محدّدة في ConstraintSet
ستلغي القيود المحددة في ملف التنسيق. في حال تحديد قيود في كل من التنسيق وMotionScene
، سيتم تطبيق القيود في MotionScene
فقط.
في هذه الخطوة، ستقوم بتقييد عرض النجوم كي يبدأ من بداية الشاشة العلوية وينتهي في نهاية الجزء السفلي من الشاشة.
يمكنك إكمال هذه الخطوة باستخدام "أداة تعديل الصور المتحركة" أو عن طريق تعديل نص "activity_step1_scene.xml
" مباشرةً.
- اختَر ConstraintSet
start
في لوحة النظرة العامة.
- في لوحة الاختيار، اختَر
red_star
. وتعرض حاليًا مصدرlayout
، أي أنّه غير مقيَّد فيConstraintSet
. استخدم رمز القلم الرصاص في أعلى اليسار من أجل إنشاء قيد
- تأكَّد من أنّ
red_star
يعرض مصدرstart
عند اختيارstart
ConstraintSet
في لوحة النظرة العامة. - في لوحة "السمات"، مع اختيار
red_star
فيConstraintSet
start
، أضِف قيدًا في الأعلى وابدأ بالنقر على الأزرار الزرقاء +.
- افتح
xml/activity_step1_scene.xml
للاطّلاع على الرمز الذي أنشأه Motion Editor لهذا القيد.
activity_step1_scene.xml
<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
تضم ConstraintSet
id
من @id/start
، وتحدد جميع القيود المراد تطبيقها على كل طرق العرض في MotionLayout
. وبما أنّ MotionLayout
هذا له ملف شخصي واحد فقط، فهو يحتاج إلى ملف Constraint
واحد فقط.
يحدّد Constraint
داخل ConstraintSet
معرّف طريقة العرض التي تفرض قيودًا، وهو @id/red_star
المحدّد في activity_step1.xml
. من المهم ملاحظة أنّ علامات Constraint
تحدّد فقط القيود ومعلومات التنسيق. لا تعرف العلامة Constraint
أنّه يتم تطبيقها على ImageView
.
يحدّد هذا القيد الارتفاع والعرض والقيدان الآخران اللازمان لفرض قيود على طريقة العرض red_star
بالبداية العلوية من العنصر الرئيسي.
- اختَر ConstraintSet
end
في لوحة النظرة العامة.
- اتّبِع الخطوات نفسها التي اتخذتها في السابق لإضافة
Constraint
لـred_star
فيConstraintSet
end
. - لاستخدام "أداة تعديل الصور المتحركة" لإكمال هذه الخطوة، أضِف قيدًا إلى
bottom
وend
من خلال النقر على زرَّي + الأزرقَين.
- يظهر الرمز في ملف XML على النحو التالي:
activitiy_step1_scene.xml
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
تمامًا مثل "@id/start
"، سيتضمّن ConstraintSet
هذا "Constraint
" مرة واحدة في @id/red_star
. هذه المرة يقصره على النهاية السفلية من الشاشة.
لستَ مضطرًا إلى تسميةهما باسم @id/start
و@id/end
، ولكن من السهل إجراء ذلك.
الخطوة 4: تحديد عملية نقل
يجب أن يتضمن كل MotionScene
أيضًا انتقالاً واحدًا على الأقل. يحدد الانتقال كل جزء من الحركة، من البداية إلى النهاية.
يجب أن يحدد الانتقال ConstraintSet
بداية ونهاية للانتقال. يمكن أن يحدد الانتقال أيضًا كيفية تعديل الحركة بطرق أخرى، مثل مدة تشغيل الحركة أو كيفية التحريك عن طريق سحب طرق العرض.
- أنشأ Motion Editor عنصر انتقالي بشكل تلقائي عند إنشاء ملف MotionScene. افتح
activity_step1_scene.xml
لعرض النقل الذي تم إنشاؤه.
activity_step1_scene.xml
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@id/start"
motion:duration="1000">
<KeyFrameSet>
</KeyFrameSet>
</Transition>
إليك كل ما يحتاج إليه تطبيق "MotionLayout
" لإنشاء صورة متحركة. عند النظر إلى كل سمة:
- سيتم تطبيق
constraintSetStart
على طرق العرض عند بدء الصورة المتحركة. - سيتم تطبيق
constraintSetEnd
على طرق العرض في نهاية الصورة المتحركة. duration
: تحدِّد هذه السمة المدة التي يجب أن يستغرقها الحركة بالملي ثانية.
سيحدّد MotionLayout
بعد ذلك مسارًا بين قيدَي البداية والنهاية ويحرّكه لمدّة محدّدة.
الخطوة 5: معاينة الصورة المتحركة في "محرِّر الحركة"
صورة متحركة: فيديو لتشغيل معاينة انتقال في Motion Editor
- افتح Motion Editor واختَر العنصر الانتقالي من خلال النقر على السهم بين
start
وend
في لوحة النظرة العامة.
- تعرض لوحة التحديد عناصر التحكم في التشغيل وشريط التقديم والترجيع عند تحديد تأثير انتقالي. انقر على "تشغيل" أو اسحب الموضع الحالي لمعاينة الرسم المتحرك.
الخطوة 6: إضافة معالج عند النقر
أنت بحاجة إلى طريقة لبدء الصورة المتحركة. ويتم ذلك من خلال استجابة MotionLayout
إلى أحداث النقر في @id/red_star
.
- افتح محرِّر الحركة واختَر العنصر الانتقالي من خلال النقر على السهم بين البداية والنهاية في لوحة النظرة العامة.
- انقر على إنشاء معالج نقرات أو التمرير السريع في شريط الأدوات في لوحة النظرة العامة . يؤدي هذا إلى إضافة معالج سيبدأ عملية النقل.
- اختَر معالِج النقرات من النافذة المنبثقة.
- غيِّر عرض النقر إلى
red_star
.
- انقر على إضافة، ويتم تمثيل معالِج النقر بنقطة صغيرة في "النقل" في "أداة تعديل الصور المتحركة".
- بعد اختيار عملية النقل في لوحة النظرة العامة، أضِف السمة
clickAction
الخاصة بـtoggle
إلى معالج OnClick الذي أضفته للتو في لوحة السمات.
- افتح
activity_step1_scene.xml
لعرض الرمز الذي أنشأه تطبيق Motion Editor.
activity_step1_scene.xml
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000">
<!-- MotionLayout will handle clicks on @id/red_star to "toggle" the animation between the start and end -->
<OnClick
motion:targetId="@id/red_star"
motion:clickAction="toggle" />
</Transition>
تطلب السياسة Transition
من MotionLayout
تشغيل الصورة المتحركة استجابةً لأحداث النقر باستخدام العلامة <OnClick>
. عند النظر إلى كل سمة:
- "
targetId
" هو العرض الذي يمكنك مشاهدته عند تلقّي نقرات. - سيتم تبديل
clickAction
من أصلtoggle
بين حالتَي البدء والانتهاء عند النقر. يمكنك الاطّلاع على خيارات أخرى بشأنclickAction
في المستندات.
- شغِّل الرمز، وانقر على الخطوة 1، ثم انقر على النجمة الحمراء وشاهد الحركة!
الخطوة 5: استخدام الصور المتحركة
شغِّل التطبيق. من المفترض أن يتم تشغيل الرسم المتحرك عند النقر على النجمة.
ويحدِّد ملف المشهد المتحرك المكتمل عنصر Transition
واحدًا يشير إلى بداية ونهاية ConstraintSet
.
في بداية الصورة المتحركة (@id/start
)، يتم تثبيت رمز النجمة في أعلى بداية الشاشة. في نهاية الصورة المتحركة (@id/end
)، يظهر رمز النجمة في الجزء السفلي من الشاشة.
<?xml version="1.0" encoding="utf-8"?>
<!-- Describe the animation for activity_step1.xml -->
<MotionScene xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000">
<!-- MotionLayout will handle clicks on @id/star to "toggle" the animation between the start and end -->
<OnClick
motion:targetId="@id/red_star"
motion:clickAction="toggle" />
</Transition>
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
</MotionScene>
4. الصور المتحركة استنادًا إلى أحداث السحب
في هذه الخطوة، ستنشئ رسمًا متحركًا يستجيب لحدث سحب مستخدم (عندما يمرر المستخدم الشاشة) لتشغيل الرسم المتحرك. يتيح تطبيق "MotionLayout
" تتبُّع أحداث اللمس لتحريك طرق العرض، بالإضافة إلى الإيماءات المستندة إلى الفيزياء لجعل الحركة انسيابية.
الخطوة 1: فحص الرمز الأولي
- للبدء، افتح ملف التنسيق
activity_step2.xml
، الذي يحتوي على محتوىMotionLayout
حالي. ألقِ نظرة على الرمز.
activity_step2.xml
<!-- initial code -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:layoutDescription="@xml/step2" >
<ImageView
android:id="@+id/left_star"
...
/>
<ImageView
android:id="@+id/right_star"
...
/>
<ImageView
android:id="@+id/red_star"
...
/>
<TextView
android:id="@+id/credits"
...
motion:layout_constraintTop_toTopOf="parent"
motion:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.motion.widget.MotionLayout>
يحدد هذا التنسيق جميع طرق عرض الرسوم المتحركة. لم يتم تقييد أيقونات النجوم الثلاث في التخطيط لأنها ستكون متحركة في مشهد الحركة.
هناك قيود مطبَّقة على الأرصدة TextView
لأنّها تبقى في المكان نفسه طوال مدة الحركة بأكملها ولا تعدِّل أي سمات.
الخطوة 2: تحريك المشهد
وتمامًا كما في آخر صورة متحركة، يتم تحديد الحركة من خلال علامة ConstraintSet,
بادئة ونهاية وTransition
.
حدِّد بداية ConstraintSet
- افتح مشهد الحركة
xml/step2.xml
لتحديد الحركة. - أضِف القيود لقيد البدء
start
. في البداية، تكون النجوم الثلاث جميعها وسط الجزء السفلي من الشاشة. النجوم اليمنى واليسرى لهاalpha
القيمة0.0
، مما يعني أنها شفافة ومخفية تمامًا.
step2.xml
<!-- TODO apply starting constraints -->
<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
<Constraint
android:id="@+id/left_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.0"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
<Constraint
android:id="@+id/right_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.0"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
في ConstraintSet
، يمكنك تحديد Constraint
واحدة لكل نجمة. سيتم تطبيق كل قيد من قِبل "MotionLayout
" في بداية الصورة المتحركة.
يتم توسيط كل عرض نجوم في أسفل الشاشة باستخدام قيود البداية والنهاية والأسفل. النجمتان @id/left_star
و@id/right_star
لهما قيمة ألفا إضافية تجعلهما غير مرئيين وسيتم تطبيقه في بداية الصورة المتحركة.
تحدّد مجموعتا القيود start
وend
بداية الحركة ونهايتها. هناك قيد على البداية، مثل motion:layout_constraintStart_toStartOf
، سيؤدي إلى تقييد بداية طريقة العرض إلى بداية عرض آخر. قد يكون هذا محيرًا في البداية، لأنّ اسم start
مستخدَم لكلٍّ منهما وكلاهما مُستخدَم في سياق القيود. للمساعدة في توضيح التمييز، تشير السمة start
في السمة layout_constraintStart
إلى كلمة "البداية". العرض، والذي هو اليسار بلغة من اليسار إلى اليمين واليمين بلغة من اليمين إلى اليسار. تشير مجموعة قيود start
إلى بداية الصورة المتحركة.
تحديد النهاية ConstraintSet
- حدِّد قيد النهاية لاستخدام سلسلة لوضع جميع النجوم الثلاث معًا أسفل
@id/credits
. بالإضافة إلى ذلك، سيتم ضبط قيمة نهايةalpha
للنجمتَين اليسرى واليمنى على1.0
.
step2.xml
<!-- TODO apply ending constraints -->
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/left_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1.0"
motion:layout_constraintHorizontal_chainStyle="packed"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toStartOf="@id/red_star"
motion:layout_constraintTop_toBottomOf="@id/credits" />
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toEndOf="@id/left_star"
motion:layout_constraintEnd_toStartOf="@id/right_star"
motion:layout_constraintTop_toBottomOf="@id/credits" />
<Constraint
android:id="@+id/right_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1.0"
motion:layout_constraintStart_toEndOf="@id/red_star"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintTop_toBottomOf="@id/credits" />
</ConstraintSet>
والنتيجة النهائية هي أن عدد مرات المشاهدة ستنتشر من وسط المدينة حيث تتحرك.
بالإضافة إلى ذلك، بما أنّه تم ضبط السمة alpha
على @id/right_start
و@id/left_star
في كليهما ConstraintSets
، ستتلاشى كلتا الطريقتين مع تقدّم الحركة.
الصور المتحركة بناءً على تمرير المستخدم
بإمكان "MotionLayout
" تتبُّع أحداث السحب أو التمرير السريع لدى المستخدم لإنشاء "قفز" يستند إلى قوانين الفيزياء. الرسوم المتحركة. هذا يعني أن مرات العرض ستستمر في حالة ما إذا كان المستخدم يضربها بسهولة وسوف تبطئ كما لو كان جسم مادي يتدحرج على سطح ما. يمكنك إضافة هذا النوع من الصور المتحركة باستخدام علامة OnSwipe
في Transition
.
- استبدِل قائمة المهام لإضافة علامة
OnSwipe
بـ<OnSwipe motion:touchAnchorId="@id/red_star" />
.
step2.xml
<!-- TODO add OnSwipe tag -->
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end">
<!-- MotionLayout will track swipes relative to this view -->
<OnSwipe motion:touchAnchorId="@id/red_star" />
</Transition>
يحتوي OnSwipe
على بعض السمات، أهمها touchAnchorId
.
touchAnchorId
هو العرض الذي يتم تتبُّعه ويتحرك استجابةً للمس. سيبقيMotionLayout
طريقة العرض هذه على مسافة واحدة من إصبع التمرير السريع.- تحدِّد
touchAnchorSide
جانب العرض الذي يجب تتبُّعه. وهذا مهم لطرق العرض التي تغير حجمها أو تتبع مسارات معقدة أو يتحرك جانب واحد بشكل أسرع من الآخر. - تحدِّد
dragDirection
الاتجاه المهم لهذه الصورة المتحركة (لأعلى أو أسفل أو يسار أو يمين).
عندما يرصد MotionLayout
أحداث السحب، سيتم تسجيل المستمع في طريقة العرض MotionLayout
وليس في طريقة العرض التي يحدّدها touchAnchorId
. عندما يبدأ المستخدم إيماءة في أي مكان على الشاشة، سيحافظ "MotionLayout
" على المسافة بين إصبعه وtouchAnchorSide
من قيمة عرض "touchAnchorId
" الثابتة. على سبيل المثال، إذا لمست جهة الارتساء بمقدار 100 بكسل مستقل الكثافة، سيبقي "MotionLayout
" ذلك الجانب بعيدًا عن إصبعه بنسبة 100 بكسل مستقل الكثافة مثلاً طوال مدة الحركة.
جرّبه الآن
- شغّل التطبيق مرة أخرى، وافتح شاشة الخطوة 2. ستظهر لك الحركة.
- جرِّب "قذف" أو ارفع إصبعك في منتصف الصورة المتحركة لاستكشاف الطريقة التي يعرض بها تطبيق "
MotionLayout
" صورًا متحركة تستند إلى قوانين السوائل.
بإمكان MotionLayout
إنشاء تأثيرات متحركة بين تصميمات مختلفة جدًا باستخدام ميزات ConstraintLayout
لإنشاء تأثيرات غنية بصريًا.
في هذه الصورة المتحركة، يتم وضع كل طرق العرض الثلاث نسبةً إلى العناصر الرئيسية في أسفل الشاشة للبدء. في النهاية، يتم وضع طرق العرض الثلاث نسبةً إلى @id/credits
في سلسلة.
على الرغم من هذه التنسيقات المختلفة جدًا، سينشئ MotionLayout
رسمًا متحركًا سلسًا بين البداية والنهاية.
5- تعديل مسار
ستنشئ في هذه الخطوة رسمًا متحركًا يتبع مسارًا معقدًا أثناء الرسم المتحرك ويحرّك الأرصدة أثناء الحركة. بإمكان MotionLayout
تعديل المسار الذي سيتخذه العرض بين بدايته ونهايته باستخدام KeyPosition
.
الخطوة 1: استكشاف الرمز الحالي
- افتح
layout/activity_step3.xml
وxml/step3.xml
للاطّلاع على التنسيق الحالي ومشهد الحركة. يعرض كل منImageView
وTextView
رمز القمر ونص المساهمة. - افتح ملف مشهد الحركة (
xml/step3.xml
). ستلاحظ أنه تم تحديدTransition
من@id/start
إلى@id/end
. تنقل الصورة المتحركة صورة القمر من أسفل يسار الشاشة إلى أسفل يمين الشاشة باستخدام رمزَيConstraintSets
. يتلاشى نص الأرصدة منalpha="0.0"
إلىalpha="1.0"
مع تحرك القمر. - شغِّل التطبيق الآن واختَر الخطوة 3. سترى أن القمر يتبع مسارًا خطيًا (أو خطًا مستقيمًا) من البداية إلى النهاية عندما تنقر على القمر.
الخطوة 2: تفعيل تصحيح أخطاء المسار
قبل إضافة قوس إلى حركة القمر، من المفيد تفعيل تصحيح أخطاء المسار في MotionLayout
.
للمساعدة في تطوير الصور المتحركة المعقّدة باستخدام MotionLayout
، يمكنك رسم مسار الحركة لكل طريقة عرض. يعد ذلك مفيدًا عندما تريد عرض الرسوم المتحركة، ولضبط التفاصيل الصغيرة للحركة.
- لتفعيل مسارات تصحيح الأخطاء، افتح
layout/activity_step3.xml
وأضِفmotion:motionDebug="SHOW_PATH"
إلى العلامةMotionLayout
.
activity_step3.xml
<!-- Add motion:motionDebug="SHOW_PATH" -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:motionDebug="SHOW_PATH" >
بعد تفعيل تصحيح أخطاء المسار، وعند تشغيل التطبيق مرة أخرى، ستظهر لك مسارات جميع طرق العرض بخط منقط.
- تمثل الدوائر موضع البداية أو النهاية لملف شخصي واحد.
- الخطوط تمثل مسار عرض واحد.
- يمثّل الماس
KeyPosition
الذي يعدِّل المسار.
على سبيل المثال، في هذه الصورة المتحركة، الدائرة الوسطى هي موضع نص "المساهمون".
الخطوة 3: تعديل مسار
يتم تحديد جميع الصور المتحركة في MotionLayout
من خلال نقطة بداية ونهاية ConstraintSet
التي تحدّد شكل الشاشة قبل بدء الصورة المتحركة وبعد انتهائها. بشكل تلقائي، يرسم MotionLayout
مسارًا خطيًا (خطًا مستقيمًا) بين موضع البداية والنهاية لكل طريقة عرض يغيّر موضعها.
لإنشاء مسارات معقدة مثل قوس القمر في هذا المثال، يستخدم MotionLayout
KeyPosition
لتعديل المسار الذي تتخذه طريقة العرض بين بدايته ونهايته.
- افتح
xml/step3.xml
وأضِفKeyPosition
إلى المشهد. يتم وضع العلامةKeyPosition
داخل العلامةTransition
.
step3.xml
<!-- TODO: Add KeyFrameSet and KeyPosition -->
<KeyFrameSet>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
</KeyFrameSet>
KeyFrameSet
هي عنصر ثانوي لـ Transition
، وهي مجموعة من كل KeyFrames
، مثل KeyPosition
، التي يجب تطبيقها أثناء عملية النقل.
بما أنّ MotionLayout
يحتسب مسار القمر بين بدايته ونهايته، سيتم تعديل المسار استنادًا إلى قيمة KeyPosition
المحدّدة في KeyFrameSet
. ويمكنك الاطلاع على كيفية تعديل المسار من خلال تشغيل التطبيق مرة أخرى.
تتضمّن السمة KeyPosition
عدة سمات توضّح كيفية تعديل المسار. وأهم هذه القيود هي:
framePosition
هو رقم بين 0 و100. وتحدّد هذه السياسة الحالات التي يجب فيها تطبيقKeyPosition
في الحركة، حيث تمثّل 1 %1 من الحركة المتحركة، و99% بقيمة 99% للحركة. لذا، إذا كانت القيمة 50، فإنك تطبقها في المنتصف.motionTarget
هو العرض الذي يعدِّلKeyPosition
فيه المسار.- تستخدم السمة
keyPositionType
طريقة تعديلKeyPosition
للمسار. ويمكن أن تكون إماparentRelative
أوpathRelative
أوdeltaRelative
(كما هو موضّح في الخطوة التالية). - تمثل
percentX | percentY
مقدار تعديل المسار فيframePosition
(القيم بين 0.0 و1.0، مع السماح بالقيم والقيم السالبة أكبر من 1).
يمكنك التفكير في الأمر على النحو التالي: "عند framePosition
تعديل مسار motionTarget
من خلال تحريكه من خلال percentX
أو percentY
وفقًا للإحداثيات التي تحددها keyPositionType
."
سيقرّب MotionLayout
تلقائيًا أي زوايا تظهر من خلال تعديل المسار. إذا نظرت إلى الصورة المتحركة التي أنشأتها للتو، سترى أن القمر يتبع مسارًا منحنيًا عند المنحنى. وهذا هو الخيار الذي تريده في معظم الصور المتحركة، وإذا لم يكن كذلك، يمكنك تحديد السمة curveFit
لتخصيصها.
جرّب بنفسك
إذا شغّلت التطبيق مرة أخرى، ستظهر لك الصورة المتحركة لهذه الخطوة.
يتبع القمر قوسًا لأنّه يمر عبر KeyPosition
المحدّدة في Transition
.
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
يمكنك قراءة هذا KeyPosition
على النحو التالي: "في framePosition 50
(في منتصف الصورة المتحركة) عدِّل مسار motionTarget
@id/moon
بتحريكه بمقدار 50% Y
(في منتصف الشاشة) وفقًا للإحداثيات التي تحدِّدها parentRelative
(MotionLayout
بالكامل).
لذلك، في منتصف عرض الصورة المتحركة، يجب أن يمر القمر عبر KeyPosition
بأسفل الشاشة بنسبة 50%. لا يغيّر KeyPosition
هذا الحركة X على الإطلاق، لذا سيظل القمر يتجه من البداية إلى النهاية أفقيًا. سيحدّد MotionLayout
مسارًا سلسًا يمر عبر KeyPosition
أثناء التنقّل بين البداية والنهاية.
إذا نظرت عن كثب، فإن نص أسماء المساهمين يقتصر على موضع القمر. لماذا لا يتحرك عموديًا أيضًا؟
<Constraint
android:id="@id/credits"
...
motion:layout_constraintBottom_toBottomOf="@id/moon"
motion:layout_constraintTop_toTopOf="@id/moon"
/>
لقد اتضح أنه على الرغم من أنك تقوم بتعديل المسار الذي يسلكه القمر، فإن موقعي بداية القمر ونهايته لا يتحركان رأسيًا على الإطلاق. لا تعدّل السمة KeyPosition
موضع البداية أو النهاية، لذلك يقتصر نص أسماء المساهمين على الموضع النهائي للقمر.
إذا أردت أن تنتقل نِسب المساهمة إلى القمر الصناعي، يمكنك إضافة KeyPosition
إلى الأرصدة أو تعديل قيود البدء في "@id/credits
".
في القسم التالي، سنتناول أنواع keyPositionType
المختلفة في MotionLayout
.
6- فهم keyPositionType
في الخطوة الأخيرة، استخدمت نوع keyPosition
من parentRelative
لتعويض المسار بنسبة% 50 من الشاشة. تحدِّد السمة keyPositionType
كيفية تعديل MotionLayout للمسار وفقًا percentX
أو percentY
.
<KeyFrameSet>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
</KeyFrameSet>
ثمة ثلاثة أنواع مختلفة من keyPosition
: parentRelative
وpathRelative
وdeltaRelative
. يؤدي تحديد نوع إلى تغيير نظام الإحداثيات الذي يتم من خلاله حساب percentX
وpercentY
.
ما هو نظام الإحداثيات؟
يتيح نظام الإحداثيات إمكانية تحديد نقطة في المساحة. وهي مفيدة أيضًا لوصف موضع على الشاشة.
أنظمة الإحداثيات MotionLayout
هي نظام إحداثيات كارتزي. هذا يعني أنها تحتوي على محور س ومحور ص محدد بخطين عموديين. الفرق الرئيسي بينهما هو مكان المحور س على الشاشة (المحور ص دائمًا عمودي على المحور س).
تستخدم جميع أنظمة الإحداثيات في MotionLayout
قيمًا بين 0.0
و1.0
على كل من المحورين "س" و"ص". وتسمح بالقيم السالبة والقيم الأكبر من 1.0
. على سبيل المثال، تعني قيمة percentX
لـ -2.0
، الاتجاه المعاكس للمحور س مرتين.
إذا كان كل ذلك يبدو إلى حد كبير مثل حصة الجبر، فراجع الصور أدناه!
الإحداثيات النسبية
تستخدم keyPositionType
لـ parentRelative
نظام الإحداثيات نفسه مثل الشاشة. ويحدّد (0, 0)
إلى أعلى يسار MotionLayout
بالكامل و(1, 1)
إلى أسفل اليمين.
يمكنك استخدام parentRelative
كلما أردت إنشاء صورة متحركة تتحرك في أنحاء MotionLayout
بالكامل، مثل قوس القمر في هذا المثال.
ومع ذلك، إذا كنت تريد تعديل مسار بالنسبة إلى الحركة، على سبيل المثال، جعله ينحني قليلاً، فإن نظامي الإحداثيات الآخرين هما خيار أفضل.
إحداثيات دلتا النسبية
دلتا هو مصطلح رياضي للتغير، لذلك فإن deltaRelative
هي طريقة لقول "تغيير نسبي". في deltaRelative
الإحداثيات(0,0)
هو موضع بداية العرض و(1,1)
هو موضع النهاية. تتم محاذاة محوري س وص مع الشاشة.
يكون المحور س دائمًا أفقيًا على الشاشة، ودائمًا ما يكون المحور ص عموديًا على الشاشة. مقارنةً بـ parentRelative
، يتمثل الاختلاف الرئيسي في أن الإحداثيات لا تصف سوى جزء الشاشة الذي سيتحرك فيه العرض.
deltaRelative
هو نظام إحداثي رائع للتحكم في الحركة الأفقية أو الرأسية بمعزل عن بعضها. على سبيل المثال، يمكنك إنشاء رسم متحرك يكمل حركته الرأسية (Y) فقط بنسبة 50٪، ويستمر في الحركة أفقيًا (X).
الإحداثيات النسبية
آخر نظام إحداثي في MotionLayout
هو pathRelative
. ويختلف تمامًا عن الاثنين الآخرين حيث يتبع المحور س مسار الحركة من البداية إلى النهاية. إذًا، يمثل العنصر (0,0)
موضع البداية، و(1,0)
هو موضع النهاية.
لماذا تريد هذا؟ وقد تفاجئني الوهلة الأولى، خاصةً وأن نظام الإحداثيات هذا غير متوافق حتى مع نظام إحداثيات الشاشة.
اتضح أن pathRelative
مفيد حقًا في بعض الأمور.
- تسريع أو إبطاء أو إيقاف عرض أثناء جزء من الصورة المتحركة: بما أنّ السمة X ستتطابق دائمًا مع المسار الذي يتخذه العرض تمامًا، يمكنك استخدام
KeyPosition
pathRelative
لتغييرframePosition
نقطة معيّنة في ذلك المسار. وبالتالي، فإنّKeyPosition
عندframePosition="50"
معpercentX="0.1"
سيؤدي إلى استغراق الرسم المتحرك 50% من الوقت لاجتياز أول 10% من الحركة. - إضافة قوس دقيق إلى المسار نظرًا لأن البعد Y يكون دائمًا عموديًا على الحركة، سيؤدي تغيير Y إلى تغيير المسار إلى الانحناء بالنسبة إلى الحركة الكلية.
- إضافة سمة ثانية عندما يتعذّر على
deltaRelative
إضافة. بالنسبة إلى الحركة الأفقية والرأسية بالكامل، سينشئdeltaRelative
بُعدًا واحدًا مفيدًا فقط. في المقابل، ستنشئ الدالةpathRelative
دائمًا إحداثيات X وY قابلة للاستخدام.
في الخطوة التالية، ستتعرَّف على كيفية إنشاء مسارات أكثر تعقيدًا باستخدام أكثر من سمة KeyPosition
واحدة.
7. إنشاء مسارات معقدة
بالنظر إلى الرسم المتحرك الذي أنشأته في الخطوة الأخيرة، يخلق منحنى سلسًا، لكن الشكل يمكن أن يكون "أشبه بالقمر".
تعديل مسار يتضمّن عدة عناصر KeyPosition
يمكن لـ MotionLayout
تعديل مسار بشكل أكبر من خلال تحديد أكبر عدد ممكن من KeyPosition
للحصول على أي حركة. في هذه الصورة المتحركة، ستنشئ قوسًا، ولكن يمكنك جعل القمر يقفز للأعلى وللأسفل في منتصف الشاشة، إذا أردت ذلك.
- فتح "
xml/step4.xml
" ستلاحظ أنّ الفيديو حصد المشاهدات نفسها وعددKeyFrame
الذي أضفته في الخطوة الأخيرة. - لتقريب الجزء العلوي من المنحنى، أضِف عنصرَين
KeyPositions
آخرَين إلى مسار@id/moon
، أحدهما قبل الوصول إلى الأعلى مباشرةً والآخر بعده.
step4.xml
<!-- TODO: Add two more KeyPositions to the KeyFrameSet here -->
<KeyPosition
motion:framePosition="25"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
/>
<KeyPosition
motion:framePosition="75"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
/>
سيتم تطبيق KeyPositions
هذه بنسبة 25% و 75% من طول الصورة المتحركة، وتؤدي إلى انتقال @id/moon
خلال مسار يبعد 60% من أعلى الشاشة. ويؤدي الدمج مع KeyPosition
الحالية عند نسبة 50% إلى إنشاء قوس سلِس يتبعه القمر.
في MotionLayout
، يمكنك إضافة أكبر عدد تريده من KeyPositions
للحصول على مسار الحركة الذي تريده. ستطبِّق MotionLayout
كل KeyPosition
عند قيمة framePosition
المحدّدة، وتحدّد كيفية إنشاء حركة سلسة تمر عبر KeyPositions
بالكامل.
جرّب بنفسك
- شغِّل التطبيق مرة أخرى. انتقِل إلى الخطوة 4 لرؤية الحركة أثناء تشغيلها. عند النقر على القمر، فإنّه يتتبّع المسار من البداية إلى النهاية ويمر عبر كل
KeyPosition
تم تحديده فيKeyFrameSet
.
الاستكشاف بمفردك
قبل الانتقال إلى أنواع أخرى من KeyFrame
، حاوِل إضافة المزيد من KeyPositions
إلى KeyFrameSet
لمعرفة نوع التأثيرات التي يمكنك إنشاؤها باستخدام KeyPosition
فقط.
إليك مثال يوضح كيفية إنشاء مسار معقد يتحرك ذهابًا وإيابًا أثناء الحركة.
step4.xml
<!-- Complex paths example: Dancing moon -->
<KeyFrameSet>
<KeyPosition
motion:framePosition="25"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
motion:percentX="0.1"
/>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
motion:percentX="0.3"
/>
<KeyPosition
motion:framePosition="75"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
motion:percentX="0.1"
/>
</KeyFrameSet>
عند الانتهاء من استكشاف "KeyPosition
"، ستنتقل في الخطوة التالية إلى الأنواع الأخرى من KeyFrames
.
8. تغيير السمات أثناء الحركة
غالبًا ما يعني إنشاء صور متحركة ديناميكية تغيير طرق العرض size
أو rotation
أو alpha
مع تقدّم الحركة. تتيح الدالة MotionLayout
تحريك العديد من السمات في أي ملف شخصي باستخدام KeyAttribute
.
في هذه الخطوة، سيتم استخدام KeyAttribute
لضبط تحجيم القمر وتدويره. وستستخدم أيضًا السمة KeyAttribute
لتأجيل ظهور النص إلى أن تكتمل رحلته القمر.
الخطوة 1: تغيير الحجم والتدوير باستخدام KeyAttribute
- افتح
xml/step5.xml
الذي يحتوي على الصورة المتحركة نفسها التي أنشأتها في الخطوة الأخيرة. لإضفاء التنوع، تستخدم هذه الشاشة صورة مساحة مختلفة كخلفية. - لتوسيع حجم القمر وتدويره، أضِف علامتَي
KeyAttribute
فيKeyFrameSet
علىkeyFrame="50"
وkeyFrame="100"
.
step5.xml
<!-- TODO: Add KeyAttributes to rotate and resize @id/moon -->
<KeyAttribute
motion:framePosition="50"
motion:motionTarget="@id/moon"
android:scaleY="2.0"
android:scaleX="2.0"
android:rotation="-360"
/>
<KeyAttribute
motion:framePosition="100"
motion:motionTarget="@id/moon"
android:rotation="-720"
/>
يتم تطبيق KeyAttributes
هذه بنسبة 50% و 100% من الحركة. سيحدث أول KeyAttribute
عند نسبة 50% أعلى القوس، ويؤدي إلى مضاعفة حجم العرض وتدوير -360 درجة (أو دائرة واحدة كاملة). سيُنهي KeyAttribute
الثاني التدوير الثاني إلى -720 درجة (دائرتان كاملتان) وسيقلص الحجم إلى الحجم العادي لأن قيمتي scaleX
وscaleY
الإعداد التلقائي هي 1.0.
وتمامًا مثل KeyPosition
، يستخدم KeyAttribute
framePosition
وmotionTarget
لتحديد وقت تطبيق KeyFrame
وطريقة العرض التي يجب تعديلها. سيتم دمج MotionLayout
بين KeyPositions
لإنشاء صور متحركة سلسة.
تتيح KeyAttributes
السمات التي يمكن تطبيقها على جميع الملفات الشخصية. وتتيح تغيير السمات الأساسية، مثل visibility
أو alpha
أو elevation
. يمكنك أيضًا تغيير التدوير كما تفعل هنا، أو التدوير في ثلاثة أبعاد باستخدام rotateX
وrotateY
، أو تغيير الحجم باستخدام scaleX
وscaleY
، أو ترجمة موضع العرض بتنسيق X أو Y أو Z.
الخطوة 2: تأخير ظهور الأرصدة
أحد أهداف هذه الخطوة هو تحديث الرسوم المتحركة بحيث لا يظهر نص "المساهمون" حتى يكتمل الرسم المتحرك تقريبًا.
- لتأجيل ظهور الأرصدة، حدِّد سمة
KeyAttribute
أخرى تضمن بقاء قيمةalpha
0 حتىkeyPosition="85"
. سيظلMotionLayout
ينتقل بسلاسة من 0 إلى 100 ألفا، ولكن سيتم إجراء ذلك خلال آخر 15% من الرسوم المتحركة.
step5.xml
<!-- TODO: Add KeyAttribute to delay the appearance of @id/credits -->
<KeyAttribute
motion:framePosition="85"
motion:motionTarget="@id/credits"
android:alpha="0.0"
/>
يؤدي هذا KeyAttribute
إلى إبقاء alpha
من @id/credits
عند 0.0 لأول 85% من الرسوم المتحركة. ولأنّه يبدأ في قيمة ألفا 0، يعني ذلك أنّ الصورة ستكون غير مرئية في أول 85% من الرسوم المتحركة.
يتمثّل التأثير النهائي لـ "KeyAttribute
" في ظهور أسماء المساهمين في نهاية الصورة المتحركة. تظهر المَشاهد متناسقة مع القمر وهو يستقر في الزاوية اليمنى من الشاشة.
من خلال تأخير الرسوم المتحركة في إحدى طرق العرض بينما تتحرك طريقة عرض أخرى على هذا النحو، يمكنك إنشاء رسوم متحركة مثيرة للإعجاب تبدو ديناميكية للمستخدم.
جرّب بنفسك
- شغِّل التطبيق مرة أخرى وانتقِل إلى الخطوة 5 لمشاهدة الصورة المتحركة أثناء عمل الصورة المتحركة. عند النقر على القمر، سيتبع المسار من البداية إلى النهاية، ويمر عبر كل
KeyAttribute
تم تحديده فيKeyFrameSet
.
بما أنك تقوم بتدوير القمر دائرتين كاملتين، سيؤدي هذا الأمر الآن إلى العودة مرة أخرى، وسوف تؤخر الأرصدة ظهورها حتى انتهاء الرسم المتحرك.
الاستكشاف بنفسك
قبل الانتقال إلى النوع النهائي من KeyFrame
، حاوِل تعديل السمات العادية الأخرى في KeyAttributes
. على سبيل المثال، جرِّب تغيير rotation
إلى rotationX
للاطّلاع على الحركة الناتجة.
وفي ما يلي قائمة بالسمات العادية التي يمكنك تجربتها:
android:visibility
android:alpha
android:elevation
android:rotation
android:rotationX
android:rotationY
android:scaleX
android:scaleY
android:translationX
android:translationY
android:translationZ
9. تغيير السمات المخصصة
تتضمن الرسوم المتحركة الغنية بصريًا تغيير اللون أو السمات الأخرى لطريقة العرض. على الرغم من أنّ السمة MotionLayout
يمكنها استخدام KeyAttribute
لتغيير أي من السمات العادية المدرَجة في المهمة السابقة، يمكنك استخدام CustomAttribute
لتحديد أي سمة أخرى.
يمكن استخدام CustomAttribute
لضبط أي قيمة لها قيمة setter. على سبيل المثال، يمكنك ضبط backgroundColor على طريقة عرض باستخدام CustomAttribute
. سيستخدم MotionLayout
الانعكاس للعثور على أداة الضبط، ثم يستخدمها بشكل متكرّر لتحريك العرض.
في هذه الخطوة، ستستخدم السمة CustomAttribute
لضبط السمة colorFilter
على القمر لإنشاء الصورة المتحرّكة أدناه.
تحديد السمات المخصّصة
- للبدء، افتح تطبيق
xml/step6.xml
الذي يحتوي على الصورة المتحركة نفسها التي أنشأتها في الخطوة الأخيرة. - لتغيير ألوان القمر، أضِف سمتَين
KeyAttribute
معCustomAttribute
فيKeyFrameSet
علىkeyFrame="0"
وkeyFrame="50"
وkeyFrame="100".
.
step6.xml
<!-- TODO: Add Custom attributes here -->
<KeyAttribute
motion:framePosition="0"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFFFFF"
/>
</KeyAttribute>
<KeyAttribute
motion:framePosition="50"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFB612"
/>
</KeyAttribute>
<KeyAttribute
motion:framePosition="100"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFFFFF"
/>
</KeyAttribute>
عليك إضافة CustomAttribute
داخل KeyAttribute
. سيتم تطبيق CustomAttribute
في قيمة framePosition
التي تحدّدها KeyAttribute
.
داخل CustomAttribute
، عليك تحديد attributeName
وقيمة واحدة لضبطها.
motion:attributeName
هو اسم أداة الضبط التي سيتم استدعاؤها بهذه السمة المخصّصة. في هذا المثال، سيتم استدعاءsetColorFilter
فيDrawable
.motion:custom*Value
هي قيمة مخصصة من النوع المذكور في الاسم. وفي هذا المثال، تكون القيمة المخصصة لونًا محددًا.
يمكن أن تحتوي القيم المخصّصة على أيٍّ من الأنواع التالية:
- اللون
- عدد صحيح
- عائم
- سلسلة
- السمة
- منطقي
باستخدام واجهة برمجة التطبيقات هذه، بإمكان MotionLayout
تحريك أي عنصر يوفّر أداة ضبط لأي طريقة عرض.
جرّب بنفسك
- شغِّل التطبيق مرة أخرى وانتقِل إلى الخطوة 6 لمشاهدة الصورة المتحركة أثناء عمل الصورة المتحركة. عند النقر على القمر، سيتبع المسار من البداية إلى النهاية، ويمر عبر كل
KeyAttribute
تم تحديده فيKeyFrameSet
.
عند إضافة المزيد من KeyFrames
، يغيّر MotionLayout
مسار القمر من خط مستقيم إلى منحنى معقد، ما يؤدي إلى إضافة قلب خلفي مزدوج وتغيير الحجم وتغيير اللون في منتصف الصورة المتحركة.
في الصور المتحركة الحقيقية، ستُحرّك غالبًا عدة طرق عرض في الوقت نفسه مع التحكّم في حركتها على طول مسارات وسرعات مختلفة. ومن خلال تحديد KeyFrame
مختلف لكل طريقة عرض، يمكن تصميم صور متحركة غنية بصريًا لإضفاء الحركة على طرق عرض متعددة باستخدام MotionLayout
.
10. سحب الأحداث والمسارات المعقّدة
سوف تستكشف في هذه الخطوة استخدام الدالة OnSwipe
مع المسارات المعقّدة. إلى الآن، استدعى أحد المستمعين إلى "OnClick
" الصورة المتحركة للقمر، ويتم تشغيلها لمدة محددة.
إنّ التحكّم في الصور المتحركة التي تحتوي على مسارات معقّدة باستخدام ميزة "OnSwipe
"، مثل الصورة المتحركة على سطح القمر التي أنشأتها في الخطوات القليلة الأخيرة، يتطلّب فهم آلية عمل "OnSwipe
".
الخطوة 1: استكشاف سلوك OnSالتمرير
- افتح
xml/step7.xml
وابحث عن بيانOnSwipe
الحالي.
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="bottom"
/>
- شغِّل التطبيق على جهازك وانتقِل إلى الخطوة 7. لنرَ ما إذا كان بإمكانك إنشاء صورة متحركة سلسة من خلال سحب القمر على طول مسار القوس.
عند تشغيل هذه الرسوم المتحركة، لا تبدو جيدة جدًا. بعد وصول القمر إلى قمة القوس، يبدأ في القفز.
ولفهم الخطأ، ضع في اعتبارك ما يحدث عندما يلمس المستخدم الجزء العلوي من القوس مباشرةً. ولأنّ العلامة OnSwipe
تحتوي على علامة motion:touchAnchorSide="bottom"
MotionLayout
ستحاول المسافة بين الإصبع والجزء السفلي من العرض الثابت خلال الصورة المتحركة.
وبما أنّ الجزء السفلي من القمر لا يسير في الاتجاه نفسه دائمًا، فإنّه يرتفع ثم يعود إلى أسفل، لا يعرف "MotionLayout
" ما يجب فعله عندما يتخطى المستخدم قمة القوس. لأخذ ذلك في الاعتبار، بما أنّك تتتبّع قاع القمر، أين يجب وضعه عندما يلمس المستخدم هنا؟
الخطوة 2: استخدام الجانب الأيمن من الصفحة
لتجنّب أخطاء كهذه، من المهم دائمًا اختيار touchAnchorId
وtouchAnchorSide
يتقدمان دائمًا في اتجاه واحد طوال مدة الحركة بأكملها.
في هذه الصورة المتحرّكة، سيتحرك كلّ من الجانبَين "right
" و"left
" من القمر على الشاشة في اتجاه واحد.
مع ذلك، سيعكس كلّ من bottom
وtop
اتجاهه. عندما يحاول "OnSwipe
" تتبُّعهما، سيتغيّر اتجاههما.
- لجعل هذه الصورة المتحركة تتبع أحداث اللمس، غيِّر
touchAnchorSide
إلىright
.
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="right"
/>
الخطوة 3: استخدام DragDirection
يمكنك أيضًا الجمع بين dragDirection
وtouchAnchorSide
لجعل المسار الجانبي مختلفًا عن الاتجاه المعتاد. لا يزال من المهم أن يتقدم touchAnchorSide
في اتجاه واحد فقط، ولكن يمكنك تحديد الاتجاه الذي يجب تتبعه لـ MotionLayout
. على سبيل المثال، يمكنك الاحتفاظ بسمة touchAnchorSide="bottom"
مع إضافة السمة dragDirection="dragRight"
. سيؤدي ذلك إلى تتبُّع MotionLayout
لموضع العرض السفلي، مع الأخذ في الاعتبار موقعه الجغرافي عند تحريكه لليمين فقط (يتجاهل الحركة الرأسية). لذلك، حتى إذا كان الجزء السفلي متجهًا للأعلى وللأسفل، سيظل الصورة المتحركة تتحرك بشكل صحيح باستخدام OnSwipe
.
- يجب تحديث "
OnSwipe
" لتتبُّع حركة القمر بشكل صحيح.
step7.xml
<!-- Using dragDirection to control the direction of drag tracking →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="bottom"
motion:dragDirection="dragRight"
/>
جرّب بنفسك
- قم بتشغيل التطبيق مرة أخرى وحاول سحب القمر خلال المسار بالكامل. وعلى الرغم من أنّه يتتبّع قوسًا معقدًا، سيتمكّن "
MotionLayout
" من تقديم الصورة المتحركة استجابةً لأحداث التمرير السريع.
11. حركة جري مع رمز
يمكن استخدام MotionLayout
لإنشاء صور متحركة غنية عند استخدامها مع CoordinatorLayout
. في هذه الخطوة، عليك إنشاء عنوان قابل للتصغير باستخدام "MotionLayout
".
الخطوة 1: استكشاف الرمز الحالي
- للبدء، افتح
layout/activity_step8.xml
. - في
layout/activity_step8.xml
، ستلاحظ أنّه سبق إنشاءCoordinatorLayout
وAppBarLayout
صالحَين.
activity_step8.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout
...>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="180dp">
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motion_layout"
... >
...
</androidx.constraintlayout.motion.widget.MotionLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
...
motion:layout_behavior="@string/appbar_scrolling_view_behavior" >
...
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
يستخدم هذا التنسيق CoordinatorLayout
لمشاركة معلومات الانتقال بين NestedScrollView
وAppBarLayout
. وبالتالي، عندما يتم الانتقال للأعلى من خلال NestedScrollView
، سيتم إعلام AppBarLayout
بالتغيير. وهذه هي طريقة تنفيذ شريط أدوات قابل للتصغير مثل هذا على Android، حيث يكون تمرير النص "منسقًا" مع العنوان القابل للتصغير.
مشهد الحركة الذي يشير إليه @id/motion_layout
مشابه لمشهد الحركة في الخطوة الأخيرة. ومع ذلك، تمت إزالة بيان OnSwipe
لتفعيل العمل مع CoordinatorLayout
.
- شغِّل التطبيق وانتقِل إلى الخطوة 8. وترى أنه عند تمرير النص، لا يتحرك القمر.
الخطوة 2: الانتقال للأسفل أو للأعلى في MotionLayout
- إذا أردت الانتقال للأسفل أو للأعلى في طريقة العرض
MotionLayout
فور انتقالNestedScrollView
، أضِفmotion:minHeight
وmotion:layout_scrollFlags
إلىMotionLayout
.
activity_step8.xml
<!-- Add minHeight and layout_scrollFlags to the MotionLayout -->
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motion_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
motion:layoutDescription="@xml/step8"
motion:motionDebug="SHOW_PATH"
android:minHeight="80dp"
motion:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed" >
- شغِّل التطبيق مرة أخرى وانتقِل إلى الخطوة 8. ستلاحظ أن
MotionLayout
يتم تصغيره أثناء الانتقال للأعلى. ومع ذلك، لا يتقدم الرسم المتحرك بناءً على سلوك التمرير حتى الآن.
الخطوة 3: تحريك الحركة باستخدام الرمز
- افتح
Step8Activity.kt
. عدِّل الدالةcoordinateMotion()
لإخبارMotionLayout
بالتغييرات في موضع التمرير.
Step8Activity.kt
// TODO: set progress of MotionLayout based on an AppBarLayout.OnOffsetChangedListener
private fun coordinateMotion() {
val appBarLayout: AppBarLayout = findViewById(R.id.appbar_layout)
val motionLayout: MotionLayout = findViewById(R.id.motion_layout)
val listener = AppBarLayout.OnOffsetChangedListener { unused, verticalOffset ->
val seekPosition = -verticalOffset / appBarLayout.totalScrollRange.toFloat()
motionLayout.progress = seekPosition
}
appBarLayout.addOnOffsetChangedListener(listener)
}
سيسجِّل هذا الرمز OnOffsetChangedListener
الذي سيتم طلبه في كل مرة يتنقل فيها المستخدم باستخدام إزاحة التمرير الحالية.
يتيح MotionLayout
طلب عملية النقل من خلال ضبط خاصية التقدّم. للتحويل بين verticalOffset
ومستوى التقدّم بالنسبة المئوية، اقسِم على إجمالي نطاق التمرير.
جرّب بنفسك
- انشر التطبيق مرة أخرى وشغِّل الصورة المتحركة من الخطوة 8. وستلاحظ أنّ
MotionLayout
سيقدم الحركة استنادًا إلى موضع الانتقال للأعلى أو للأسفل.
من الممكن إنشاء صور متحركة مخصّصة لشريط الأدوات القابل للتصغير الديناميكي باستخدام MotionLayout
. باستخدام سلسلة من KeyFrames
يمكنك الحصول على تأثيرات واضحة جدًا.
12. تهانينا
يتناول هذا الدرس التطبيقي حول الترميز واجهة برمجة التطبيقات الأساسية لـ MotionLayout
.
للاطّلاع على المزيد من الأمثلة حول "MotionLayout
" عمليًا، يمكنك الاطّلاع على النموذج الرسمي. وننصحك بمراجعة المستندات.
مزيد من المعلومات
يتيح MotionLayout
المزيد من الميزات التي لا يشملها هذا الدرس التطبيقي، مثل KeyCycle,
الذي يتيح لك التحكّم في المسارات أو السمات التي تتضمن دورات متكررة، وKeyTimeCycle,
التي تتيح لك إمكانية تعديل الصور استنادًا إلى وقت الساعة. اطلع على النماذج للحصول على أمثلة لكل منها.
للحصول على روابط إلى الدروس التطبيقية الأخرى حول الترميز في هذه الدورة التدريبية، اطّلِع على الصفحة المقصودة للدروس التطبيقية حول الترميز في لغة Kotlin المتقدمة.