1. قبل از شروع
این کد لبه بخشی از دوره Advanced Android in Kotlin است. اگر از طریق کدها به ترتیب کار کنید، بیشترین ارزش را از این دوره خواهید گرفت، اما اجباری نیست. همه کدهای دوره در صفحه فرود Android Advanced in Kotlin Codelabs فهرست شده اند.
MotionLayout کتابخانه ای است که به شما امکان می دهد حرکت غنی را به برنامه اندروید خود اضافه کنید. این بر اساس ConstraintLayout, است و به شما امکان می دهد هر چیزی را که می توانید با استفاده از ConstraintLayout بسازید متحرک کنید.
می توانید از MotionLayout برای متحرک سازی مکان، اندازه، دید، آلفا، رنگ، ارتفاع، چرخش و سایر ویژگی های چندین نما به طور همزمان استفاده کنید. با استفاده از XML اعلانی میتوانید انیمیشنهای هماهنگ، شامل چندین نما، ایجاد کنید که دستیابی به آنها در کد دشوار است.
انیمیشن ها راهی عالی برای بهبود تجربه اپلیکیشن هستند. می توانید از انیمیشن ها برای موارد زیر استفاده کنید:
- نمایش تغییرات — متحرک سازی بین حالت ها به کاربر اجازه می دهد به طور طبیعی تغییرات در رابط کاربری شما را ردیابی کند.
- جلب توجه - از انیمیشن ها برای جلب توجه به عناصر مهم رابط کاربری استفاده کنید.
- طرحهای زیبا بسازید —حرکت مؤثر در طراحی باعث میشود برنامهها شیک به نظر برسند.
پیش نیازها
این کد لبه برای توسعه دهندگان با تجربه توسعه اندروید طراحی شده است. قبل از تلاش برای تکمیل این کد لبه، باید:
- بدانید که چگونه یک برنامه با یک فعالیت، یک طرح اولیه ایجاد کنید و آن را با استفاده از Android Studio روی دستگاه یا شبیه ساز اجرا کنید. با
ConstraintLayoutآشنا باشید. برای کسب اطلاعات بیشتر در موردConstraintLayoutلابراتوار کدهای Constraint Layout را مطالعه کنید.
کاری که خواهی کرد
- با
ConstraintSetsوMotionLayoutیک انیمیشن تعریف کنید - متحرک سازی بر اساس رویدادهای کشیدن
- انیمیشن را با
KeyPositionتغییر دهید - ویژگی ها را با
KeyAttributeتغییر دهید - انیمیشن ها را با کد اجرا کنید
- هدرهای جمع شونده را با
MotionLayoutمتحرک کنید
آنچه شما نیاز دارید
- Android Studio 4.0 (ویرایشگر
MotionLayoutفقط با این نسخه از Android Studio کار می کند.)
2. شروع به کار
برای دانلود نمونه برنامه می توانید یکی از موارد زیر را انجام دهید:
... یا با استفاده از دستور زیر مخزن GitHub را از خط فرمان کلون کنید:
$ git clone https://github.com/googlecodelabs/motionlayout.git
3. ایجاد انیمیشن با MotionLayout
ابتدا یک انیمیشن می سازید که در پاسخ به کلیک های کاربر، نما را از ابتدای بالای صفحه به انتهای پایین حرکت می دهد.
برای ایجاد یک انیمیشن از کد شروع، به قطعات اصلی زیر نیاز دارید:
- یک
MotionLayout,که زیر کلاسConstraintLayoutاست. شما تمام نماها را برای متحرک شدن در تگMotionLayoutمشخص می کنید. - یک
MotionScene,که یک فایل XML است که یک انیمیشن را برایMotionLayout. - یک
Transition,که بخشی ازMotionSceneاست که مدت زمان انیمیشن، ماشه و نحوه جابجایی نماها را مشخص می کند. - یک
ConstraintSetکه هر دو محدودیت شروع و پایان انتقال را مشخص می کند.
بیایید به نوبه خود نگاهی به هر یک از این موارد بیندازیم، که از 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 Studio به شما هشداری در مورد عدم وجود محدودیت می دهد.
مرحله 2: تبدیل به Motion Layout
برای متحرک سازی با استفاده از MotionLayout, باید ConstraintLayout به MotionLayout تبدیل کنید.
برای اینکه چیدمان شما از یک صحنه حرکتی استفاده کند، باید به آن اشاره کند.
- برای انجام این کار، سطح طراحی را باز کنید. در Android Studio 4.0، هنگام مشاهده یک فایل XML طرحبندی، با استفاده از نماد تقسیم یا طراحی در بالا سمت راست، سطح طراحی را باز میکنید.

- هنگامی که سطح طراحی را باز کردید، روی پیش نمایش کلیک راست کرده و Convert to 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 ، سطح طراحی Motion Editor را نمایش می دهد

سه عنصر رابط کاربری جدید در Motion Editor وجود دارد:
- نمای کلی - این یک انتخاب مودال است که به شما امکان می دهد قسمت های مختلف انیمیشن را انتخاب کنید. در این تصویر
startConstraintSetانتخاب شده است. همچنین می توانید با کلیک بر روی فلش بین آنها، انتقال بینstartوendرا انتخاب کنید. - بخش - در زیر نمای کلی یک پنجره بخش است که بر اساس آیتم نمای کلی انتخاب شده فعلی تغییر می کند. در این تصویر اطلاعات
startConstraintSetدر پنجره انتخاب نمایش داده می شود. - ویژگی – پانل مشخصه را نشان می دهد و به شما امکان می دهد ویژگی های مورد انتخابی فعلی را از پنجره نمای کلی یا انتخاب ویرایش کنید. در این تصویر، ویژگی های
startConstraintSetرا نشان می دهد.
مرحله 3: محدودیت های شروع و پایان را تعریف کنید
همه انیمیشن ها را می توان بر حسب شروع و پایان تعریف کرد. شروع توضیح میدهد که صفحه قبل از انیمیشن چگونه به نظر میرسد، و پایان توضیح میدهد که صفحه بعد از تکمیل انیمیشن چگونه به نظر میرسد. MotionLayout مسئول تشخیص نحوه متحرک سازی بین حالت شروع و پایان (در طول زمان) است.
MotionScene از تگ ConstraintSet برای تعریف حالت شروع و پایان استفاده می کند. یک ConstraintSet همان چیزی است که به نظر می رسد، مجموعه ای از محدودیت ها که می تواند روی نماها اعمال شود. این شامل محدودیت های عرض، ارتفاع و ConstraintLayout است. همچنین شامل برخی از ویژگی ها مانند alpha است. این شامل خود دیدگاهها نیست، فقط محدودیتهای موجود در آن دیدگاهها را شامل میشود.
هر گونه محدودیت مشخص شده در یک ConstraintSet ، محدودیت های مشخص شده در فایل طرح بندی را لغو می کند. اگر محدودیتهایی را هم در طرحبندی و هم MotionScene تعریف کنید، فقط محدودیتهای MotionScene اعمال میشوند.
در این مرحله، نمای ستاره را محدود میکنید تا از ابتدای بالای صفحه شروع شود و در انتهای پایین صفحه به پایان برسد.
می توانید این مرحله را با استفاده از Motion Editor یا با ویرایش مستقیم متن activity_step1_scene.xml تکمیل کنید.
-
startConstraintSet را در پانل نمای کلی انتخاب کنید

- در پانل انتخاب ،
red_starرا انتخاب کنید. در حال حاضر منبعlayoutرا نشان میدهد - این بدان معناست که در اینConstraintSetمحدود نیست. برای ایجاد محدودیت از نماد مداد در سمت راست بالا استفاده کنید

- زمانی که
startConstraintSetدر پانل نمای کلی انتخاب می شود، تأیید کنید کهred_starیک منبعstartرا نشان می دهد. - در پانل Attributes، با انتخاب
red_starدرstartConstraintSet، یک Constraint در بالا اضافه کنید و با کلیک بر روی دکمه های آبی + شروع کنید.

-
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 را در شروع بالای والد آن مشخص میکند.
-
endConstraintSet را در پانل نمای کلی انتخاب کنید.

- همان مراحلی را که قبلا انجام دادید دنبال کنید تا یک
Constraintبرایred_starدرConstraintSetendاضافه کنید. - برای استفاده از Motion Editor برای تکمیل این مرحله، یک محدودیت به
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
- Motion Editor را باز کرده و با کلیک بر روی فلش بین
startوendدر پانل نمای کلی، انتقال را انتخاب کنید.

- پانل انتخاب ، کنترلهای پخش و نوار اسکراب را هنگام انتخاب یک انتقال نشان میدهد. برای پیش نمایش انیمیشن روی پخش کلیک کنید یا موقعیت فعلی را بکشید.

مرحله 6: یک کنترل کننده کلیک اضافه کنید
برای شروع انیمیشن به راهی نیاز دارید. یکی از راههای انجام این کار این است که MotionLayout به رویدادهای کلیک در @id/red_star پاسخ دهد.
- ویرایشگر حرکت را باز کنید و با کلیک بر روی پیکان بین شروع و پایان در پانل نمای کلی، انتقال را انتخاب کنید.

- کلیک کنید
کنترل کننده کلیک یا کشیدن انگشت را در نوار ابزار پانل نمای کلی ایجاد کنید . این یک کنترل کننده اضافه می کند که یک انتقال را شروع می کند. - از پنجره بازشو گزینه Click Handler را انتخاب کنید

- View To Click را به
red_starتغییر دهید.

- روی Add کلیک کنید کنترل کننده کلیک با یک نقطه کوچک در Transition in Motion Editor نشان داده می شود.

- با انتخاب انتقال در پانل نمای کلی، یک ویژگی
clickActionازtoggleرا به کنترل کننده OnClick که به تازگی در پانل ویژگی ها اضافه کرده اید، اضافه کنید.

- برای مشاهده کدی که Motion Editor ایجاد کرده است،
activity_step1_scene.xmlرا باز کنید
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نمایی برای تماشای کلیک است. -
clickActiontoggleبین حالت شروع و پایان با کلیک تغییر می کند. میتوانید گزینههای دیگری را برای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محدودیت را اضافه کنید. در ابتدا، هر سه ستاره در مرکز پایین صفحه قرار دارند. ستاره های راست و چپ دارای مقدارalpha0.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 اضافه کنید.
- TODO را برای افزودن تگ
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 را ثابت نگه میدارد. برای مثال، اگر 100dp از سمت لنگر فاصله داشته باشد، MotionLayout آن سمت را برای کل انیمیشن 100dp از انگشت خود دور نگه می دارد.
آن را امتحان کنید
- دوباره برنامه را اجرا کنید و صفحه مرحله 2 را باز کنید. انیمیشن را خواهید دید.
- سعی کنید "پرتاب" کنید یا انگشت خود را تا نیمه از انیمیشن رها کنید تا ببینید
MotionLayoutچگونه انیمیشن های مبتنی بر فیزیک سیال را نمایش می دهد!

MotionLayout می تواند بین طرح های بسیار متفاوت با استفاده از ویژگی های ConstraintLayout برای ایجاد جلوه های غنی متحرک شود.
در این انیمیشن هر سه نما نسبت به والد خود در پایین صفحه برای شروع قرار می گیرند. در پایان، سه نما نسبت به @id/credits در یک زنجیره قرار می گیرند.
با وجود این طرحبندیهای بسیار متفاوت، MotionLayout یک انیمیشن روان بین شروع و پایان ایجاد میکند.
5. اصلاح یک مسیر
در این مرحله شما یک انیمیشن می سازید که مسیر پیچیده ای را در طول انیمیشن دنبال می کند و تیتراژها را در طول حرکت متحرک می کند. MotionLayout می تواند مسیری را که یک view بین شروع و پایان طی می کند با استفاده از یک 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 | percentYframePosition
می توانید به این شکل فکر کنید: "در 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 یک سیستم مختصات دکارتی هستند. این بدان معناست که آنها یک محور X و Y دارند که با دو خط عمود بر هم تعریف شده اند. تفاوت اصلی بین آنها در جایی است که محور X روی صفحه نمایش می رود (محور Y همیشه بر محور X عمود است).
همه سیستم های مختصات در MotionLayout از مقادیر بین 0.0 و 1.0 در هر دو محور X و Y استفاده می کنند. آنها مقادیر منفی و مقادیر بزرگتر از 1.0 را مجاز می کنند. به عنوان مثال، یک مقدار percentX -2.0 به این معنی است که دو بار در جهت مخالف محور X بروید.
اگر همه اینها کمی شبیه کلاس جبر به نظر می رسد، تصاویر زیر را بررسی کنید!
مختصات نسبی والدین

keyPositionType parentRelative از همان سیستم مختصات صفحه استفاده می کند. (0, 0) را در بالا سمت چپ کل MotionLayout ، و (1, 1) را در سمت راست پایین تعریف می کند.
میتوانید هر زمان که میخواهید انیمیشنی بسازید که در کل MotionLayout حرکت میکند، از parentRelative استفاده کنید - مانند قوس ماه در این مثال.
با این حال، اگر می خواهید مسیری را نسبت به حرکت تغییر دهید، مثلاً آن را کمی منحنی کنید، دو سیستم مختصات دیگر انتخاب بهتری هستند.
دلتا مختصات نسبی

دلتا یک اصطلاح ریاضی برای تغییر است، بنابراین deltaRelative راهی برای گفتن "تغییر نسبی" است. در مختصات deltaRelative (0,0) موقعیت شروع نمای و (1,1) موقعیت پایانی است. محورهای X و Y با صفحه نمایش هم تراز هستند.
محور X همیشه روی صفحه افقی است و محور Y همیشه عمودی روی صفحه است. در مقایسه با parentRelative ، تفاوت اصلی این است که مختصات فقط بخشی از صفحه را توصیف می کند که نما در آن حرکت می کند.
deltaRelative یک سیستم مختصات عالی برای کنترل حرکت افقی یا عمودی به صورت مجزا است. برای مثال، میتوانید انیمیشنی بسازید که فقط حرکت عمودی (Y) خود را با 50% کامل کند و به انیمیشن افقی (X) ادامه دهد.
p ath مختصات نسبی

آخرین سیستم مختصات در MotionLayout pathRelative است. این کاملاً با دو مورد دیگر متفاوت است زیرا محور X مسیر حرکت را از ابتدا تا انتها دنبال می کند. بنابراین (0,0) موقعیت شروع و (1,0) موقعیت پایان است.
چرا این را می خواهید؟ در نگاه اول کاملاً تعجب آور است، به خصوص که این سیستم مختصات حتی با سیستم مختصات صفحه نمایش هم تراز نیست.
معلوم است pathRelative برای چند چیز واقعا مفید است.
- افزایش سرعت، کاهش سرعت، یا توقف نمایش در طول بخشی از انیمیشن. از آنجایی که بعد X همیشه دقیقاً با مسیری که view طی می کند مطابقت دارد، می توانید از یک
pathRelativeKeyPositionبرای تغییرframePositionبه یک نقطه خاص در آن مسیر استفاده کنید. بنابراین یکKeyPositionدرframePosition="50"باpercentX="0.1"باعث می شود که انیمیشن 50٪ از زمان را برای طی کردن 10٪ اول حرکت صرف کند. - افزودن یک قوس ظریف به یک مسیر. از آنجایی که بعد Y همیشه بر حرکت عمود است، تغییر Y مسیر منحنی را نسبت به حرکت کلی تغییر می دهد.
- افزودن بعد دوم زمانی که
deltaRelativeکار نمی کند. برای حرکت کاملا افقی و عمودی،deltaRelativeتنها یک بعد مفید ایجاد می کند. با این حال،pathRelativeهمیشه مختصات X و Y قابل استفاده ایجاد می کند.
در مرحله بعدی شما یاد می گیرید که چگونه می توانید با استفاده از بیش از یک KeyPosition ، مسیرهای پیچیده تری بسازید.
7. ساخت مسیرهای پیچیده
با نگاهی به انیمیشن شما در آخرین مرحله ، یک منحنی صاف ایجاد می کند ، اما شکل می تواند "ماه مانند" باشد.
مسیری را با چندین عنصر صفحه کلید اصلاح کنید
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دیگر را تعریف کنید که تضمین می کند کهalpha0 تا زمان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 برای 85 ٪ اول انیمیشن در 0.0 نگه می دارد. از آنجا که از آلفا 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 استفاده کرد. به عنوان مثال ، می توانید با استفاده از یک CustomAttribute ، BackgroundColor را روی یک نمای تنظیم کنید. MotionLayout از بازتاب برای یافتن تنظیم کننده استفاده می کند ، سپس آن را بارها و بارها صدا می کند تا نمای را تحریک کند.
در این مرحله ، شما از یک CustomAttribute برای تنظیم ویژگی colorFilter در ماه برای ساخت انیمیشن نشان داده شده در زیر استفاده خواهید کرد.

ویژگی های سفارشی را تعریف کنید
- برای شروع کار
xml/step6.xmlکه حاوی همان انیمیشن شما در آخرین مرحله است. - برای ایجاد رنگ های تغییر ماه ، دو
KeyAttributeبا یکCustomAttributeدرKeyFrameSetدرkeyFrame="0"،keyFrame="50"وkeyFrame="100".

مرحله 6.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مقدار سفارشی از نوع ذکر شده در نام است ، در این مثال مقدار سفارشی رنگی مشخص شده است.
مقادیر سفارشی می توانند هر یک از انواع زیر را داشته باشند:
- رنگ
- عدد صحیح
- شناور
- رشته
- بعد
- بولی
با استفاده از این API ، MotionLayout می تواند هر چیزی را که یک تنظیم کننده در هر نمای فراهم می کند ، تحریک کند.
آن را امتحان کنید
- دوباره برنامه را اجرا کنید و برای دیدن انیمیشن در عمل به مرحله 6 بروید. هنگامی که روی ماه کلیک می کنید ، مسیر را از ابتدا تا انتها دنبال می کند ، از طریق هر
KeyAttributeکه درKeyFrameSetمشخص شده است ، می روید.

هنگامی که KeyFrames بیشتری را اضافه می کنید ، MotionLayout مسیر ماه را از یک خط مستقیم به یک منحنی پیچیده تغییر می دهد ، و یک پشتی مضاعف ، تغییر اندازه و تغییر رنگ در وسط انیمیشن اضافه می کند.
در انیمیشن های واقعی ، شما اغلب چندین نمایش را به طور همزمان کنترل می کنید که حرکت آنها را در طول مسیرها و سرعت های مختلف کنترل می کنید. با مشخص کردن یک KeyFrame متفاوت برای هر نمای ، می توان انیمیشن های غنی را رقص کرد که چندین نمایش را با MotionLayout تحریک می کنند.
10. وقایع و مسیرهای پیچیده را بکشید
در این مرحله با استفاده از OnSwipe با مسیرهای پیچیده کاوش خواهید کرد. تاکنون انیمیشن ماه توسط یک شنونده OnClick ایجاد شده و برای مدت زمان ثابت اجرا می شود.
کنترل انیمیشن هایی که مسیرهای پیچیده ای با استفاده از OnSwipe دارند ، مانند انیمیشن ماه که در چند مرحله آخر ساخته اید ، نیاز به درک نحوه کار OnSwipe دارد.
مرحله 1: رفتار Onswipe را کاوش کنید
-
xml/step7.xmlرا باز کنید و اعلامیه موجودOnSwipeرا پیدا کنید.
مرحله 7.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تغییر دهید.
مرحله 7.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را به روز کنید تا حرکت ماه را به درستی ردیابی کنید.
مرحله 7.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از قبل ساخته شده است.
action_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:minHeightandmotion:layout_scrollFlagsبهMotionLayout.
action_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. تبریک می گویم
این CodeLab API اصلی MotionLayout را پوشش می داد.
برای دیدن نمونه های بیشتر از MotionLayout در عمل ، نمونه رسمی را بررسی کنید. و حتماً مستندات را بررسی کنید!
بیشتر بدانید
MotionLayout از ویژگی های بیشتری پشتیبانی می کند که در این CodeLab پوشانده نشده است ، مانند KeyCycle, که به شما امکان می دهد مسیرها یا ویژگی های خود را با چرخه های تکرار شونده کنترل کنید و KeyTimeCycle, که به شما امکان می دهد بر اساس زمان ساعت تحریک شوید. برای نمونه های هر یک از نمونه ها را بررسی کنید.
برای پیوندها به سایر CodeLabs در این دوره ، به صفحه Advanced Android در Kotlin Codelabs Landing مراجعه کنید.