اندروید پیشرفته در Kotlin 03.2: انیمیشن با MotionLayout

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.

  1. در 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 تبدیل کنید.

برای اینکه چیدمان شما از یک صحنه حرکتی استفاده کند، باید به آن اشاره کند.

  1. برای انجام این کار، سطح طراحی را باز کنید. در Android Studio 4.0، هنگام مشاهده یک فایل XML طرح‌بندی، با استفاده از نماد تقسیم یا طراحی در بالا سمت راست، سطح طراحی را باز می‌کنید.

a2beea710c2decb7.png

  1. هنگامی که سطح طراحی را باز کردید، روی پیش نمایش کلیک راست کرده و Convert to MotionLayout را انتخاب کنید.

4fa936a98a8393b9.png

این تگ 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 را نمایش می دهد

66d0e80d5ab4daf8.png

سه عنصر رابط کاربری جدید در Motion Editor وجود دارد:

  1. نمای کلی - این یک انتخاب مودال است که به شما امکان می دهد قسمت های مختلف انیمیشن را انتخاب کنید. در این تصویر start ConstraintSet انتخاب شده است. همچنین می توانید با کلیک بر روی فلش بین آنها، انتقال بین start و end را انتخاب کنید.
  2. بخش - در زیر نمای کلی یک پنجره بخش است که بر اساس آیتم نمای کلی انتخاب شده فعلی تغییر می کند. در این تصویر اطلاعات start ConstraintSet در پنجره انتخاب نمایش داده می شود.
  3. ویژگی – پانل مشخصه را نشان می دهد و به شما امکان می دهد ویژگی های مورد انتخابی فعلی را از پنجره نمای کلی یا انتخاب ویرایش کنید. در این تصویر، ویژگی های start ConstraintSet را نشان می دهد.

مرحله 3: محدودیت های شروع و پایان را تعریف کنید

همه انیمیشن ها را می توان بر حسب شروع و پایان تعریف کرد. شروع توضیح می‌دهد که صفحه قبل از انیمیشن چگونه به نظر می‌رسد، و پایان توضیح می‌دهد که صفحه بعد از تکمیل انیمیشن چگونه به نظر می‌رسد. MotionLayout مسئول تشخیص نحوه متحرک سازی بین حالت شروع و پایان (در طول زمان) است.

MotionScene از تگ ConstraintSet برای تعریف حالت شروع و پایان استفاده می کند. یک ConstraintSet همان چیزی است که به نظر می رسد، مجموعه ای از محدودیت ها که می تواند روی نماها اعمال شود. این شامل محدودیت های عرض، ارتفاع و ConstraintLayout است. همچنین شامل برخی از ویژگی ها مانند alpha است. این شامل خود دیدگاه‌ها نیست، فقط محدودیت‌های موجود در آن دیدگاه‌ها را شامل می‌شود.

هر گونه محدودیت مشخص شده در یک ConstraintSet ، محدودیت های مشخص شده در فایل طرح بندی را لغو می کند. اگر محدودیت‌هایی را هم در طرح‌بندی و هم MotionScene تعریف کنید، فقط محدودیت‌های MotionScene اعمال می‌شوند.

در این مرحله، نمای ستاره را محدود می‌کنید تا از ابتدای بالای صفحه شروع شود و در انتهای پایین صفحه به پایان برسد.

می توانید این مرحله را با استفاده از Motion Editor یا با ویرایش مستقیم متن activity_step1_scene.xml تکمیل کنید.

  1. start ConstraintSet را در پانل نمای کلی انتخاب کنید

6e57661ed358b860.png

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

f9564c574b86ea8.gif

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

2fce076cd7b04bd.png

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

  1. end ConstraintSet را در پانل نمای کلی انتخاب کنید.

346e1248639b6f1e.png

  1. همان مراحلی را که قبلا انجام دادید دنبال کنید تا یک Constraint برای red_star در ConstraintSet end اضافه کنید.
  2. برای استفاده از Motion Editor برای تکمیل این مرحله، یک محدودیت به bottom اضافه کنید و با کلیک بر روی دکمه های آبی + end .

fd33c779ff83c80a.png

  1. کد در 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 شروع و پایان را برای انتقال مشخص کند. یک انتقال همچنین می‌تواند نحوه تغییر انیمیشن را به روش‌های دیگر، مانند مدت زمان اجرای انیمیشن یا نحوه متحرک کردن با کشیدن نماها، مشخص کند.

  1. 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

dff9ecdc1f4a0740.gif

انیمیشن: ویدیوی پخش پیش نمایش انتقال در Motion Editor

  1. Motion Editor را باز کرده و با کلیک بر روی فلش بین start و end در پانل نمای کلی، انتقال را انتخاب کنید.

1dc541ae8c43b250.png

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

a0fd2593384dfb36.png

مرحله 6: یک کنترل کننده کلیک اضافه کنید

برای شروع انیمیشن به راهی نیاز دارید. یکی از راه‌های انجام این کار این است که MotionLayout به رویدادهای کلیک در @id/red_star پاسخ دهد.

  1. ویرایشگر حرکت را باز کنید و با کلیک بر روی پیکان بین شروع و پایان در پانل نمای کلی، انتقال را انتخاب کنید.

b6f94b344ce65290.png

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

ccf92d06335105fe.png

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

b0d3f0c970604f01.png

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

cec3913e67fb4105.png

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

9af6fc60673d093d.png

  1. برای مشاهده کدی که 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 نمایی برای تماشای کلیک است.
  • clickAction toggle بین حالت شروع و پایان با کلیک تغییر می کند. می‌توانید گزینه‌های دیگری را برای clickAction در مستندات مشاهده کنید.
  1. کد خود را اجرا کنید، مرحله 1 را کلیک کنید، سپس ستاره قرمز را کلیک کنید و انیمیشن را ببینید!

مرحله 5: انیمیشن در عمل

برنامه را اجرا کنید! هنگامی که روی ستاره کلیک می کنید، باید انیمیشن خود را اجرا کنید.

7ba88af963fdfe10.gif

فایل صحنه حرکت تکمیل شده یک 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: کد اولیه را بررسی کنید

  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 را تعریف کنید

  1. صحنه حرکت xml/step2.xml را برای تعریف انیمیشن باز کنید.
  2. محدودیت های 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 را تعریف کنید

  1. محدودیت انتهایی را برای استفاده از یک زنجیره برای قرار دادن هر سه ستاره در زیر @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 اضافه کنید.

  1. 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 از انگشت خود دور نگه می دارد.

آن را امتحان کنید

  1. دوباره برنامه را اجرا کنید و صفحه مرحله 2 را باز کنید. انیمیشن را خواهید دید.
  2. سعی کنید "پرتاب" کنید یا انگشت خود را تا نیمه از انیمیشن رها کنید تا ببینید MotionLayout چگونه انیمیشن های مبتنی بر فیزیک سیال را نمایش می دهد!

fefcd690a0dcaec.gif

MotionLayout می تواند بین طرح های بسیار متفاوت با استفاده از ویژگی های ConstraintLayout برای ایجاد جلوه های غنی متحرک شود.

در این انیمیشن هر سه نما نسبت به والد خود در پایین صفحه برای شروع قرار می گیرند. در پایان، سه نما نسبت به @id/credits در یک زنجیره قرار می گیرند.

با وجود این طرح‌بندی‌های بسیار متفاوت، MotionLayout یک انیمیشن روان بین شروع و پایان ایجاد می‌کند.

5. اصلاح یک مسیر

در این مرحله شما یک انیمیشن می سازید که مسیر پیچیده ای را در طول انیمیشن دنبال می کند و تیتراژها را در طول حرکت متحرک می کند. MotionLayout می تواند مسیری را که یک view بین شروع و پایان طی می کند با استفاده از یک KeyPosition تغییر دهد.

مرحله 1: کد موجود را کاوش کنید

  1. layout/activity_step3.xml و xml/step3.xml را باز کنید تا طرح بندی و صحنه حرکت موجود را ببینید. ImageView و TextView متن ماه و اعتبار را نمایش می دهند.
  2. فایل صحنه حرکت ( xml/step3.xml ) را باز کنید. مشاهده می کنید که یک Transition از @id/start به @id/end تعریف شده است. انیمیشن تصویر ماه را از سمت چپ پایین صفحه به سمت راست پایین صفحه با استفاده از دو ConstraintSets منتقل می کند. با حرکت ماه، متن اعتبار از alpha="0.0" به alpha="1.0" محو می شود.
  3. اکنون برنامه را اجرا کنید و مرحله 3 را انتخاب کنید. وقتی روی ماه کلیک می کنید، خواهید دید که ماه یک مسیر خطی (یا یک خط مستقیم) را از ابتدا تا انتها دنبال می کند.

مرحله 2: اشکال زدایی مسیر را فعال کنید

قبل از اینکه یک قوس به حرکت ماه اضافه کنید، فعال کردن اشکال زدایی مسیر در MotionLayout مفید است.

برای کمک به توسعه انیمیشن های پیچیده با MotionLayout ، می توانید مسیر انیمیشن هر نما را ترسیم کنید. این زمانی که می خواهید انیمیشن خود را تجسم کنید و برای تنظیم دقیق جزئیات کوچک حرکت مفید است.

  1. برای فعال کردن مسیرهای اشکال زدایی، 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" >

پس از فعال کردن اشکال زدایی مسیر، وقتی دوباره برنامه را اجرا کردید، مسیرهای همه نماها را با یک خط نقطه چین مشاهده خواهید کرد.

23bbb604f456f65c.png

  • دایره ها موقعیت شروع یا پایان یک نما را نشان می دهند.
  • خطوط نشان دهنده مسیر یک نما هستند.
  • الماس نشان دهنده یک KeyPosition است که مسیر را تغییر می دهد.

برای مثال در این انیمیشن دایره وسط موقعیت متن تیتراژ است.

مرحله 3: یک مسیر را تغییر دهید

تمام انیمیشن‌ها در MotionLayout با یک شروع و یک ConstraintSet پایان تعریف می‌شوند که مشخص می‌کند صفحه قبل از شروع انیمیشن و بعد از اتمام انیمیشن چگونه به نظر می‌رسد. به طور پیش فرض، MotionLayout یک مسیر خطی (یک خط مستقیم) بین موقعیت شروع و پایان هر نما ترسیم می کند که موقعیت را تغییر می دهد.

برای ایجاد مسیرهای پیچیده مانند قوس ماه در این مثال، MotionLayout از یک KeyPosition برای تغییر مسیری که یک نما بین شروع و پایان طی می کند استفاده می کند.

  1. xml/step3.xml را باز کنید و یک KeyPosition به صحنه اضافه کنید. تگ KeyPosition در داخل تگ Transition قرار می گیرد.

eae4dae9a12d0410.png

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

می توانید به این شکل فکر کنید: "در framePosition مسیر motionTarget را با جابجایی آن بر حسب percentX یا percentY بر اساس مختصات تعیین شده توسط keyPositionType تغییر دهید ."

به طور پیش فرض MotionLayout هر گوشه ای را که با تغییر مسیر معرفی می شود گرد می کند. اگر به انیمیشنی که به تازگی ایجاد کرده‌اید نگاه کنید، می‌بینید که ماه یک مسیر منحنی را در پیچ دنبال می‌کند. برای اکثر انیمیشن ها، این همان چیزی است که می خواهید، و اگر نه، می توانید ویژگی curveFit را برای سفارشی کردن آن مشخص کنید.

آن را امتحان کنید

اگر دوباره برنامه را اجرا کنید، انیمیشن این مرحله را خواهید دید.

46b179c01801f19e.gif

ماه از یک کمان پیروی می کند زیرا از یک 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 می گذرد در حالی که بین شروع و پایان حرکت می کند.

اگر به دقت نگاه کنید، متن تیتراژ توسط موقعیت ماه محدود شده است. چرا به صورت عمودی هم حرکت نمی کند؟

1c7cf779931e45cc.gif

<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 بروید.

اگر همه اینها کمی شبیه کلاس جبر به نظر می رسد، تصاویر زیر را بررسی کنید!

مختصات نسبی والدین

a7b7568d46d9dec7.png

keyPositionType parentRelative از همان سیستم مختصات صفحه استفاده می کند. (0, 0) را در بالا سمت چپ کل MotionLayout ، و (1, 1) را در سمت راست پایین تعریف می کند.

می‌توانید هر زمان که می‌خواهید انیمیشنی بسازید که در کل MotionLayout حرکت می‌کند، از parentRelative استفاده کنید - مانند قوس ماه در این مثال.

با این حال، اگر می خواهید مسیری را نسبت به حرکت تغییر دهید، مثلاً آن را کمی منحنی کنید، دو سیستم مختصات دیگر انتخاب بهتری هستند.

دلتا مختصات نسبی

5680bf553627416c.png

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

محور X همیشه روی صفحه افقی است و محور Y همیشه عمودی روی صفحه است. در مقایسه با parentRelative ، تفاوت اصلی این است که مختصات فقط بخشی از صفحه را توصیف می کند که نما در آن حرکت می کند.

deltaRelative یک سیستم مختصات عالی برای کنترل حرکت افقی یا عمودی به صورت مجزا است. برای مثال، می‌توانید انیمیشنی بسازید که فقط حرکت عمودی (Y) خود را با 50% کامل کند و به انیمیشن افقی (X) ادامه دهد.

p ath مختصات نسبی

f3aaadaac8b4a93f.png

آخرین سیستم مختصات در MotionLayout pathRelative است. این کاملاً با دو مورد دیگر متفاوت است زیرا محور X مسیر حرکت را از ابتدا تا انتها دنبال می کند. بنابراین (0,0) موقعیت شروع و (1,0) موقعیت پایان است.

چرا این را می خواهید؟ در نگاه اول کاملاً تعجب آور است، به خصوص که این سیستم مختصات حتی با سیستم مختصات صفحه نمایش هم تراز نیست.

معلوم است pathRelative برای چند چیز واقعا مفید است.

  • افزایش سرعت، کاهش سرعت، یا توقف نمایش در طول بخشی از انیمیشن. از آنجایی که بعد X همیشه دقیقاً با مسیری که view طی می کند مطابقت دارد، می توانید از یک pathRelative KeyPosition برای تغییر framePosition به یک نقطه خاص در آن مسیر استفاده کنید. بنابراین یک KeyPosition در framePosition="50" با percentX="0.1" باعث می شود که انیمیشن 50٪ از زمان را برای طی کردن 10٪ اول حرکت صرف کند.
  • افزودن یک قوس ظریف به یک مسیر. از آنجایی که بعد Y همیشه بر حرکت عمود است، تغییر Y مسیر منحنی را نسبت به حرکت کلی تغییر می دهد.
  • افزودن بعد دوم زمانی که deltaRelative کار نمی کند. برای حرکت کاملا افقی و عمودی، deltaRelative تنها یک بعد مفید ایجاد می کند. با این حال، pathRelative همیشه مختصات X و Y قابل استفاده ایجاد می کند.

در مرحله بعدی شما یاد می گیرید که چگونه می توانید با استفاده از بیش از یک KeyPosition ، مسیرهای پیچیده تری بسازید.

7. ساخت مسیرهای پیچیده

با نگاهی به انیمیشن شما در آخرین مرحله ، یک منحنی صاف ایجاد می کند ، اما شکل می تواند "ماه مانند" باشد.

مسیری را با چندین عنصر صفحه کلید اصلاح کنید

MotionLayout می تواند با تعریف بسیاری از KeyPosition در صورت لزوم برای دستیابی به هرگونه حرکت ، یک مسیر را تغییر دهد. برای این انیمیشن یک قوس خواهید ساخت ، اما اگر می خواهید ماه را در وسط صفحه قرار دهید.

  1. xml/step4.xml را باز کنید. می بینید که دارای همان نماها و KeyFrame است که در آخرین مرحله اضافه شده است.
  2. برای دور کردن قسمت بالای منحنی ، دو KeyPositions دیگر را به مسیر @id/moon اضافه کنید ، یکی درست قبل از رسیدن به بالا ، و یکی بعد.

500B5AC2DB48EF87.PNG

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 عبور می کند.

آن را امتحان کنید

  1. دوباره برنامه را اجرا کنید. برای دیدن انیمیشن در عمل به مرحله 4 بروید. هنگامی که روی ماه کلیک می کنید ، مسیر را از ابتدا تا انتها دنبال می کند ، و هر KeyPosition که در KeyFrameSet مشخص شده بود ، طی می کنید.

به تنهایی کاوش کنید

قبل از حرکت به انواع دیگر KeyFrame ، سعی کنید برخی از KeyPositions دیگر را به KeyFrameSet اضافه کنید تا ببینید چه نوع جلوه هایی را می توانید فقط با استفاده از KeyPosition ایجاد کنید.

در اینجا یک مثال آورده شده است که چگونه می توان یک مسیر پیچیده را ساخت که در طول انیمیشن به جلو و عقب حرکت می کند.

cd9faaffde3dfef.png

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

  1. xml/step5.xml را باز کنید که حاوی همان انیمیشن شما در آخرین مرحله است. برای تنوع ، این صفحه از یک تصویر فضایی متفاوت به عنوان پس زمینه استفاده می کند.
  2. برای گسترش ماه در اندازه و چرخش ، دو برچسب KeyAttribute را در KeyFrameSet در keyFrame="50" و keyFrame="100" اضافه کنید.

BBAE524A2898569.png

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: ظاهر اعتبار را به تأخیر بیندازید

یکی از اهداف این مرحله ، به روزرسانی انیمیشن است تا متن اعتبار تا زمانی که انیمیشن کامل نشود ، ظاهر نشود.

  1. برای تأخیر در ظاهر اعتبار ، یک 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 برای 85 ٪ اول انیمیشن در 0.0 نگه می دارد. از آنجا که از آلفا 0 شروع می شود ، این بدان معنی است که برای 85 ٪ اول انیمیشن نامرئی خواهد بود.

تأثیر نهایی این KeyAttribute این است که اعتبارات به سمت پایان انیمیشن ظاهر می شوند. این امر باعث می شود که آنها با هماهنگی ماه در گوشه سمت راست صفحه هماهنگ شوند.

با تأخیر در انیمیشن ها در یک نمای در حالی که نمای دیگری مانند این حرکت می کند ، می توانید انیمیشن های چشمگیر بسازید که برای کاربر پویا احساس می کنند.

آن را امتحان کنید

  1. دوباره برنامه را اجرا کنید و برای دیدن انیمیشن در عمل به مرحله 5 بروید. هنگامی که روی ماه کلیک می کنید ، مسیر را از ابتدا تا انتها دنبال می کند ، از طریق هر KeyAttribute که در KeyFrameSet مشخص شده است ، می روید.

2f4bfdd681c1fa98.gif

از آنجا که شما ماه را دو دایره کامل می چرخانید ، اکنون یک تلنگر دوتایی انجام می دهد ، و اعتبارات ظاهر آنها را به تأخیر می اندازد تا زمانی که انیمیشن تقریباً انجام شود.

به تنهایی کاوش کنید

قبل از اینکه به نوع نهایی 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 در ماه برای ساخت انیمیشن نشان داده شده در زیر استفاده خواهید کرد.

5fb6792126a09fda.gif

ویژگی های سفارشی را تعریف کنید

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

214699D5FDD956DA.PNG

مرحله 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 می تواند هر چیزی را که یک تنظیم کننده در هر نمای فراهم می کند ، تحریک کند.

آن را امتحان کنید

  1. دوباره برنامه را اجرا کنید و برای دیدن انیمیشن در عمل به مرحله 6 بروید. هنگامی که روی ماه کلیک می کنید ، مسیر را از ابتدا تا انتها دنبال می کند ، از طریق هر KeyAttribute که در KeyFrameSet مشخص شده است ، می روید.

5fb6792126a09fda.gif

هنگامی که KeyFrames بیشتری را اضافه می کنید ، MotionLayout مسیر ماه را از یک خط مستقیم به یک منحنی پیچیده تغییر می دهد ، و یک پشتی مضاعف ، تغییر اندازه و تغییر رنگ در وسط انیمیشن اضافه می کند.

در انیمیشن های واقعی ، شما اغلب چندین نمایش را به طور همزمان کنترل می کنید که حرکت آنها را در طول مسیرها و سرعت های مختلف کنترل می کنید. با مشخص کردن یک KeyFrame متفاوت برای هر نمای ، می توان انیمیشن های غنی را رقص کرد که چندین نمایش را با MotionLayout تحریک می کنند.

10. وقایع و مسیرهای پیچیده را بکشید

در این مرحله با استفاده از OnSwipe با مسیرهای پیچیده کاوش خواهید کرد. تاکنون انیمیشن ماه توسط یک شنونده OnClick ایجاد شده و برای مدت زمان ثابت اجرا می شود.

کنترل انیمیشن هایی که مسیرهای پیچیده ای با استفاده از OnSwipe دارند ، مانند انیمیشن ماه که در چند مرحله آخر ساخته اید ، نیاز به درک نحوه کار OnSwipe دارد.

مرحله 1: رفتار Onswipe را کاوش کنید

  1. xml/step7.xml را باز کنید و اعلامیه موجود OnSwipe را پیدا کنید.

مرحله 7.xml

<!-- Fix OnSwipe by changing touchAnchorSide →

<OnSwipe
       motion:touchAnchorId="@id/moon"
       motion:touchAnchorSide="bottom"
/>
  1. برنامه را روی دستگاه خود اجرا کنید و به مرحله 7 بروید. ببینید آیا می توانید با کشیدن ماه در مسیر قوس ، یک انیمیشن صاف تولید کنید.

وقتی این انیمیشن را اجرا می کنید ، خیلی خوب به نظر نمی رسد. بعد از رسیدن ماه به بالای قوس ، شروع به پریدن به اطراف می کند.

ED96E3674854A548.GIF

برای درک اشکال ، در نظر بگیرید که وقتی کاربر دقیقاً زیر بالای قوس لمس می کند ، چه اتفاقی می افتد. از آنجا که برچسب OnSwipe دارای motion:touchAnchorSide="bottom" MotionLayout سعی خواهد کرد فاصله بین انگشت و پایین نمای را در طول انیمیشن ثابت کند.

اما ، از آنجا که کف ماه همیشه در همان جهت پیش نمی رود ، بالا می رود و به پایین می رود ، MotionLayout نمی داند وقتی کاربر به تازگی بالای قوس را پشت سر گذاشته است چه کاری انجام دهد. برای در نظر گرفتن این موضوع ، از آنجا که در حال ردیابی قسمت پایین ماه هستید ، وقتی کاربر در اینجا لمس می کند ، کجا باید قرار گیرد؟

56CD575C5C77eddd.png

مرحله 2: از سمت راست استفاده کنید

برای جلوگیری از اشکالات مانند این ، مهم است که همیشه یک touchAnchorId و touchAnchorSide را انتخاب کنید که همیشه در طول مدت کل انیمیشن در یک جهت پیشرفت می کند.

در این انیمیشن ، هم سمت right و هم سمت left ماه در یک جهت در صفحه نمایش پیشرفت می کند.

با این حال ، هم bottom و هم top جهت معکوس خواهد بود. هنگامی که OnSwipe تلاش می کند تا آنها را ردیابی کند ، با تغییر جهت آنها گیج می شود.

  1. برای ساختن این انیمیشن از رویدادهای لمسی ، 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 به درستی تحریک می شود.

  1. OnSwipe را به روز کنید تا حرکت ماه را به درستی ردیابی کنید.

مرحله 7.xml

<!-- Using dragDirection to control the direction of drag tracking →

<OnSwipe
       motion:touchAnchorId="@id/moon"
       motion:touchAnchorSide="bottom"
       motion:dragDirection="dragRight"
/>

آن را امتحان کنید

  1. دوباره برنامه را اجرا کنید و سعی کنید ماه را در کل مسیر بکشید. حتی اگر از یک قوس پیچیده پیروی کند ، MotionLayout قادر خواهد بود در پاسخ به وقایع کشویی ، انیمیشن را پیشرفت کند.

5458DFF382261427.GIF

11. حرکت با کد

MotionLayout می توان برای ساخت انیمیشن های غنی هنگام استفاده از CoordinatorLayout استفاده کرد. در این مرحله ، با استفاده از MotionLayout یک هدر قابل جمع شدن ایجاد خواهید کرد.

مرحله 1: کد موجود را کاوش کنید

  1. برای شروع ، layout/activity_step8.xml باز کنید.
  2. در 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 کار کند.

  1. برنامه را اجرا کنید و به مرحله 8 بروید. می بینید که وقتی متن را پیمایش می کنید ، ماه حرکت نمی کند.

مرحله 2: حرکت MotionLayout را بسازید

  1. برای ساخت حرکت MotionLayout به محض پیمایش کتیبه های NestedScrollView ، motion:minHeight and motion: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"  >
  1. دوباره برنامه را اجرا کنید و به مرحله 8 بروید. می بینید که هنگام حرکت به سمت حرکت ، MotionLayout سقوط می کند. با این حال ، انیمیشن هنوز بر اساس رفتار پیمایش پیشرفت نمی کند.

مرحله 3: حرکت را با کد حرکت دهید

  1. 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 و درصدی ، بر اساس دامنه کل پیمایش تقسیم کنید.

آن را امتحان کنید

  1. دوباره برنامه را مستقر کنید و انیمیشن مرحله 8 را اجرا کنید. می بینید که MotionLayout بر اساس موقعیت پیمایش ، انیمیشن را پیشرفت خواهد کرد.

ee5ce4d9e33a59ca.gif

ساخت انیمیشن های نوار ابزار فروپاشی پویا با استفاده از MotionLayout امکان پذیر است. با استفاده از دنباله ای از KeyFrames می توانید به جلوه های بسیار جسورانه برسید.

12. تبریک می گویم

این CodeLab API اصلی MotionLayout را پوشش می داد.

برای دیدن نمونه های بیشتر از MotionLayout در عمل ، نمونه رسمی را بررسی کنید. و حتماً مستندات را بررسی کنید!

بیشتر بدانید

MotionLayout از ویژگی های بیشتری پشتیبانی می کند که در این CodeLab پوشانده نشده است ، مانند KeyCycle, که به شما امکان می دهد مسیرها یا ویژگی های خود را با چرخه های تکرار شونده کنترل کنید و KeyTimeCycle, که به شما امکان می دهد بر اساس زمان ساعت تحریک شوید. برای نمونه های هر یک از نمونه ها را بررسی کنید.

برای پیوندها به سایر CodeLabs در این دوره ، به صفحه Advanced Android در Kotlin Codelabs Landing مراجعه کنید.