MotionLayout و هر آنچه باید در مورد ایجاد انیمیشن در اندروید بدانید

۱۷۴ بازدید
آخرین به‌روزرسانی: ۲۰ اردیبهشت ۱۴۰۲
زمان مطالعه: ۷ دقیقه
MotionLayout و هر آنچه باید در مورد ایجاد انیمیشن در اندروید بدانید

MotionLayout کلاس جدیدی است که در کتابخانه ConstraintLayout 2.0 معرفی شده تا توسعه‌دهندگان اندروید بتوانند حرکت‌ها و انیمیشن ویجت را در برنامه‌های خود مدیریت کنند.

گوگل در نیمه دوم کنفرانس I/O سال 2018 خود با موضوع «معرفی موارد جدید در ConstraintLayout و ابزارهای طراحی اندروید استودیو» مرور خوبی در مورد قابلیت‌های این کتابخانه داشت.

ویجت ConstraintLayout برای طراحی لی‌آوت برنامه‌های اندرویدی، به لطف تنوع بالا تبدیل به یک چاقوی همه‌کاره شده است. با این وجود افزودن انیمیشن‌های مختلف به آن، علی‌رغم امکانات خوبی که ایجاد کرده است، فرایندی زمان‌گیر محسوب می‌شد. گوگل با افزودن ویجت MotionLayout در کنفرانس I/O 2018 به رفع این مشکل کمک زیادی کرده است.

ویجت MotionLayout که اینک بخشی از کتابخانه support اندروید است در واقع ویجت ConstraintLayout را بسط می‌دهد. این یک ویجت منحصربه‌فرد است که امکان انیمیت محتوای خود را با اعلان‌های XML فراهم می‌سازد. به علاوه کنترل مناسبی بر روی همه اجزای انیمیشن ارائه می‌دهد.

در این راهنما شیوه افزودن آن به پروژه‌های اندروید استودیو و ایجاد چند انیمیشن مختلف را با هم مرور خواهیم کرد.

پیش‌نیازها

برای استفاده از این راهنما به موارد زیر نیاز خواهید داشت:

  • اندروید استودیو نسخه 3.1.3 یا بالاتر،
  • دستگاه یا شبیه‌سازی که اندروید با API سطح 21 یا بالاتر را اجرا کند،
  • داشتن درکی مقدماتی از ویجت ConstraintLayout نیز لازم است.

1. افزودن وابستگی‌ها (Dependencies)

برای این که بتوانید ویجت MotionLayout را به یک پروژه اندروید استودیو اضافه کنید باید آخرین نسخه از کتابخانه پشتیبانی onstraint Layout را به صورت وابستگی implementation داشته باشید. به علاوه برای جلوگیری از تعارض نسخه‌ها، اطمینان حاصل کنید که یک وابستگی برای آخرین نسخه پایدار از کتابخانه v7 appcompat support نیز ایجاد کرده‌اید.

کدهای زیر را نیز به فایلbuild.gradle ماژول app اضافه کنید:

implementation 'com.android.support:appcompat-v7:27.0.2'
implementation 'com.android.support.constraint:constraint-layout:2.0.0-alpha1

2. تعریف یک لی‌آوت

ویجت MotionLayout می‌تواند هر کاری که ویجت ConstraintLayout انجام می‌دهد را اجرا کند. از این رو می‌توانید به راحتی وهله‌ای از MotionLayout را جایگزین آن کنید. با این حال در حال حاضر پیشنهاد می‌کنیم که یک فایل XML جدید ایجاد کرده و ویجت MotionLayout را به عنصر root آن اضافه کنید.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.motion.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/motion_container">
<!-- More code here -->
</android.support.constraint.motion.MotionLayout>

در سراسر این راهنما سعی خواهیم کرد که یک ویجت ImageView را انیمیت کنیم. بنابراین آن را به عنوان نخستین فرزند لی‌آوت اضافه می‌کنیم.

<ImageView
android:id="@+id/actor"
app:srcCompat="@color/colorAccent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

شما آزاد هستید که از هر نوع drawable (اشیای قابل ترسیم در اندروید) به عنوان منبع ویجت ImageView استفاده کنید. در کد فوق از یک drawable رنگ استفاده شده است.

سپس دکمه‌ای را اضافه می‌کنیم که بتوانیم با آن یک انیمیشن را آغاز کنیم. کد زیر نحوه قرارگیری آن در مرکز لی‌آوت را نشان می‌دهد:

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Press Me"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:onClick="start"/>

علاوه بر آن برای نظارت بر پیشرفت انیمیشن یک ویجت SeekBar به لی‌آوت اضافه کرده و آن را زیر دکمه قرار می‌دهیم. روش این کار به صورت زیر است:

<SeekBar
android:layout_width="100dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="10dp"
android:id="@+id/seekbar"/>

در نهایت از آنجا که یک رویداد on-click در ارتباط با دکمه وجود دارد، باید مطمئن شویم که آن را در activity خود تعریف نموده‌ایم:

fun start(v: View) {
// More code here
}

3. ایجاد یک صحنه متحرک (Motion Scene)

ممکن است متوجه شده باشید که در زمان تعریف لی‌آوت هیچ قیدی به ویجت ImageView اضافه نکردیم. دلیل این امر آن است که به جای آن می‌خواهیم یک صحنه متحرک (Motion Scene) اضافه کنیم. صحنه متحرک یک فایل XML است که شامل جزییاتی در مورد انیمیشنی است که می‌خواهیم با ویجت MotionLayout بسازیم.

برای ایجاد یک صحنه متحرک جدید ابتدا یک فایل منابع XML می‌سازیم و آن را به عنصر MotionScene اضافه می‌کنیم.

?xml version="1.0" encoding="utf-8"?>
<MotionScene
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- More code here -->
</MotionScene>

هر صحنه متحرک شامل عناصر ConstraintSet است که قیودی که باید در ویجت به عنوان نقاط مختلف انیمیشن استفاده شود را مشخص می‌کند. فایل‌های صحنه متحرک معمولاً شامل دو مجموعه قیود هستند: یک دسته برای آغاز انیمیشن و دیگری برای پایان آن.

کد زیر به شما نشان می‌دهد چطور می‌توانید دو مجموعه قیود برای کمک به حرکت دادن ویجت ImageView از سوی ویجت MotionLayout از گوشه راست-پایین به گوشه بالا-چپ صفحه مورد استفاده قرار دهید:

<ConstraintSet android:id="@+id/starting_set">
<Constraint android:id="@+id/actor"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_width="60dp"
android:layout_height="60dp"
/>
</ConstraintSet>
<ConstraintSet android:id="@+id/ending_set">
<Constraint android:id="@+id/actor"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_width="60dp"
android:layout_height="60dp"
/>
</ConstraintSet>

توجه داشته باشید که هر عنصر ConstraintSet باید همواره هم موقعیت مطلوب و هم اندازه مطلوب را مشخص کند. این امر مهم است زیرا باعث بازنویسی هر گونه اطلاعات قبلی تعیین شده برای لی‌آوت می‌شود.

برای کمک به MotionLayout در جهت درک این که کدام مجموعه قیود باید استفاده شوند در ادامه یک عنصر Transition ایجاد می‌کنیم. با استفاده از خصوصیاتی با نام‌های constraintSetStart و constraintSetEnd می‌توانید تعیین کنید که کدام مجموعه باید ابتدا و کدام یک سپس مورد استفاده قرار گیرند. عنصر Transition همچنین امکان تعیین مدت انیمیشن را فراهم می‌کند.

<Transition
android:id="@+id/my_transition"
app:constraintSetStart="@+id/starting_set"
app:constraintSetEnd="@+id/ending_set"
app:duration="2000">
</Transition>

در این زمان صحنه متحرک کامل شده است. با این وجود ویجت MotionLayout همچنان از آن بی‌اطلاع است. بنابراین باید به فایل XML بازگردیم و یک خصوصیت layoutDescription به ویجت اضافه نماییم و مقدار آن را برابر نام فایل MotionScene تعیین کنیم.

اگر نام فایل MotionScene به صورت my_scene.xml باشد، ویجت MotionLayout باید ظاهری مانند زیر داشته باشد:

<android.support.constraint.motion.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/my_scene"
android:id="@+id/motion_container">
...
</android.support.constraint.motion.MotionLayout>

4. آغاز انیمیشن

زمانی که برنامه را اجرا می‌کنید ویجت MotionLayout به طور خودکار مجموعه قیود ذکر شده در خصوصیت constraintSetStart را در عنصر Transition به کار می‌گیرد. از این رو برای آغاز انیمیشن تنها کافی است متد ()transitionToEnd ویجت را فراخوانی کنید.

کد زیر که باید به Handler رویداد on-click که در گام قبلی ایجاد شد اضافه شود، روش کار را به شما نشان می‌دهد:

motion_container.transitionToEnd()

در این مرحله اگر برنامه اجرا شده و دکمه مربوطه فشرده شود می‌توانید ببینید که ویجت ImageView به نرمی در عرض صفحه حرکت می‌کند.

5. مدیریت رویدادهای انیمیشن

با اتصال شیء TransitionListener به ویجت MotionLayout می‌توانید روند انیمیشن را به طور دقیق‌تری پیگیری نمایید.

motion_container.setTransitionListener(
object: MotionLayout.TransitionListener {
// More code here
}
)

رابط TransitionListener دو متد تجریدی دارد و اندروید استودیو به طور خودکار روال‌هایی برای آن‌ها ایجاد می‌کند.

متد ()onTransitionCompleted زمانی که گذار از یک مجموعه قیود به مجموعه دیگر کامل شد فراخوانی می‌شود. در حال حاضر از آن برای ریست قیود ویجت ImageView با فراخوانی متد ()transitionToStart در داخلش استفاده می‌کنیم.

override fun onTransitionCompleted(motionLayout: MotionLayout?,
currentId: Int) {
if(currentId == R.id.ending_set) {
// Return to original constraint set
motion_container.transitionToStart()
}
}

متد ()onTransitionChange هر بار که روند پیشروی انیمیشن تغییری پیدا کند، فراخوانی می‌شود. در چنین وضعیتی روند پیشروی انیمیشن یک عدد اعشاری است که بین 0 و 1 قرار دارد. کد زیر نحوه به‌روزرسانی SeekBar بر اساس روند پیشروی انیمیشن را نشان می‌دهد:

override fun onTransitionChange(motionLayout: MotionLayout?,
startId: Int,
endId: Int,
progress: Float) {
seekbar.progress = ceil(progress * 100).toInt()
}

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

6. ایجاد فریم‌های کلیدی (key frames)

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

پیش از آن که شروع به ایجاد فریم‌های کلیدی بکنید باید عنصر KeyFrameSet را به عنصر Transition صحنه متحرک خود اضافه کنید. درون این عنصر می‌توانید هر تعداد که می‌خواهید فریم‌های کلیدی ایجاد کنید.

<KeyFrameSet android:id="@+id/my_keys">
<!-- More code here -->
</KeyFrameSet>

ویجت MotionLayout از انواع مختلفی از فریم‌های کلیدی پشتیبانی می‌کند. در این راهنما ما تنها با دو نوع از آن‌ها کار می‌کنیم: فریم‌های KeyPosition و فریم‌های KeyCycle .

فریم‌های KeyPosition انواعی از فریم‌ها هستند که شکل مسیر را تغییر می‌دهند. در زمان ایجاد این فریم‌ها باید مطمئن شوید که ID ویجت هدف، یک موقعیت در راستای تایملاین که هر عددی بین 0 تا 100 می‌تواند باشد و همچنین مختصات X و Y مطلوب که به صورت درصد بیان می‌شود را مشخص ساخته‌اید. مختصات می‌توانند به طور نسبی در رابطه با محورهای X و Y یا به طور نسبتی در رابطه با خود مسیر بیان شوند.

کد زیر شیوه ایجاد دو فریم را نشان می‌دهد که باعث می‌شوند ویجت ImageView مسیری را طی کند که از تصادم با دکمه و Seek Bar جلوگیری شود.

<KeyPosition
app:target="@+id/actor"
app:framePosition="30"
app:type="deltaRelative"
app:percentX="0.85" />
<KeyPosition
app:target="@+id/actor"
app:framePosition="60"
app:type="deltaRelative"
app:percentX="1" />

اگر برنامه را اجرا کنید می‌بینید که انیمیشن شبیه تصویر زیر خواهد بود:

البته شما می‌توانید فریم‌های کلیدی بیشتری را اضافه کنید. برای نمونه با افزودن فریم‌های کلیدی زیر به انتهای تایملاین می‌توانید باعث شوید که ویجت ImageView مسیری منحنی‌تر را طی کند.

<KeyPosition
app:target="@+id/actor"
app:framePosition="80"
app:type="deltaRelative"
app:percentX="0.5" />

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

کد زیر یک فریم KeyCycle ایجاد می‌کند که از نوسانگر با موج سینوسی با چرخش دوره‌ای ویجت ImageView به میزان 50 درجه استفاده می‌کند.

<KeyCycle
app:target="@+id/actor"
app:framePosition="30"
android:rotation="50"
app:waveShape="sin"
app:wavePeriod="1" />

با اجرای مجدد برنامه می‌توانید انیمیشنی مشابه زیر را ببینید:

7. تعامل‌پذیر ساختن ویجت های انیمیشنی

در این برنامه برای شروع انیمیشن یک دکمه فشرده می‌شود. با این حال چنین دکمه‌ای همواره الزامی نیست، چون ویجت MotionLayout اجازه می‌دهد که handler های رویداد لمس را به ویجتی که قرار است انیمیت شود متصل سازید. در حال حاضر این ویجت از رویدادهای on-click و on-swipe پشتیبانی می‌کند.

برای نمونه می‌توانید عنصر onClick زیر را با ویجت هدف ImageView درون عنصر Transition صحنه متحرک اضافه کنید تا دیگر نیازی به دکمه نباشد:

<OnClick
app:target="@+id/actor"
app:mode="transitionToEnd"/>

به طور مشابه می‌توانید از عنصر onSwipe استفاده کنید تا به کاربر امکان کشیدن دستی ویجت ImageView در عرض صفحه را بدهید. در زمان ایجاد این عنصر باید اطمینان حاصل کنید که جهت صحیح کشیدن و سمت مناسب ویجت که به عنوان دستگیره کشیدن استفاده می‌شود را مشخص ساخته‌اید.

<OnSwipe
app:touchAnchorId="@+id/actor"
app:touchAnchorSide="top"
app:dragDirection="dragUp" />

اگر برنامه را مجدداً اجرا کنید می‌بینید که قادر به کشیدن ویجت ImageView هستید.

نتیجه‌گیری

با مطالعه این راهنما اینک با روش استفاده از ویجت MotionLayout برای ساخت انیمیشن‌های تعاملی سریع و پیچیده در برنامه‌های اندروید خود آشنا شدید. می‌توانید مطمئن باشید تا زمانی از view های تودرتو استفاده نکرده‌اید، انیمیشن‌هایتان بدون هیچ‌گونه گیر یا پرش روی اغلب دستگاه‌ها پخش می‌شوند.

همچنین باید اشاره کنیم که در نسخه‌های بعدی اندروید استودیو یک ویرایشگر حرکت‌های بصری نیز گنجانده خواهد شد که باعث بهبود قابلیت استفاده از این ویجت خواهد شد.

اگر به این نوشته علاقه‌مند بودید، موارد زیر نیز احتمالاً مورد توجه شما قرار خواهند گرفت:

==

بر اساس رای ۳ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
tutsplus
۱ دیدگاه برای «MotionLayout و هر آنچه باید در مورد ایجاد انیمیشن در اندروید بدانید»

سلام . ممنون از شما درباره این مقاله ولی چند تا مشکل داشت
اول اینکه شما باید علاوه بر ترجمه ، اون جاهایی که یکمی گنگ تره رو توضیح کامل تری بدین . و چه خوب بود اگر در اخر کل کد های xml صفحه اصلی و xml هایی ک ساختین رو میگذاشتین .چون بعضی جا ها توی متن والد های تگ هایی ک نوشتین واضح نبودن .

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *