ساختن ترانزیشن های زیبا با حرکت مواد برای اندروید

۱. مقدمه

طراحی متریال سیستمی برای ساخت محصولات دیجیتال جسورانه و زیبا است. با متحد کردن سبک، برندسازی، تعامل و حرکت تحت مجموعه‌ای از اصول و اجزای سازگار، تیم‌های محصول می‌توانند به بزرگترین پتانسیل طراحی خود دست یابند.

logo_components_color_2x_web_96dp.png

کامپوننت‌های متریال (MDC) به توسعه‌دهندگان در پیاده‌سازی طراحی متریال کمک می‌کنند. MDC که توسط تیمی از مهندسان و طراحان UX در گوگل ایجاد شده است، ده‌ها کامپوننت رابط کاربری زیبا و کاربردی را ارائه می‌دهد و برای اندروید، iOS، وب و Flutter.material.io/develop در دسترس است.

سیستم حرکتی متریال برای اندروید چیست؟

سیستم حرکت متریال برای اندروید مجموعه‌ای از الگوهای انتقال در کتابخانه MDC-Android است که می‌تواند به کاربران در درک و پیمایش یک برنامه، همانطور که در دستورالعمل‌های طراحی متریال توضیح داده شده است، کمک کند.

چهار الگوی اصلی انتقال مواد به شرح زیر است:

  • تبدیل کانتینر: انتقال بین عناصر رابط کاربری که شامل یک کانتینر هستند؛ با تبدیل یکپارچه یک عنصر به عنصر دیگر، یک اتصال قابل مشاهده بین دو عنصر رابط کاربری مجزا ایجاد می‌کند.
  • محور مشترک: انتقال بین عناصر رابط کاربری که رابطه مکانی یا ناوبری دارند؛ از یک تبدیل مشترک روی محور x، y یا z برای تقویت رابطه بین عناصر استفاده می‌کند.
  • محو شدن تدریجی (Fade Through): انتقال بین عناصر رابط کاربری که رابطه‌ی قوی با یکدیگر ندارند؛ از محو شدن تدریجی (fade out) و محو شدن تدریجی (fade in) با مقیاس عنصر ورودی استفاده می‌کند.
  • محو شدن: برای عناصر رابط کاربری که وارد یا خارج از محدوده صفحه نمایش می‌شوند، استفاده می‌شود.

کتابخانه MDC-Android کلاس‌های انتقالی را برای این الگوها ارائه می‌دهد که بر اساس کتابخانه انتقالی AndroidX ( androidx.transition ) و چارچوب انتقالی اندروید ( android.transition ) ساخته شده‌اند:

اندروید ایکس

  • موجود در بسته‌ی com.google.android.material.transition
  • پشتیبانی از API سطح ۱۴+
  • از Fragmentها و Viewها پشتیبانی می‌کند، اما از Activityها یا Windows پشتیبانی نمی‌کند.
  • شامل رفع اشکالات بک‌پورت شده و رفتار سازگار در سطوح مختلف API است

چارچوب

  • موجود در بسته‌ی com.google.android.material.transition.platform
  • پشتیبانی از API سطح ۲۱+
  • پشتیبانی از قطعات، نماها، فعالیت‌ها و پنجره‌ها
  • رفع اشکالات، بک‌پورت نشده‌اند و ممکن است در سطوح مختلف API رفتار متفاوتی داشته باشند.

در این آزمایشگاه کد، شما از انتقال‌های متریال ساخته شده بر روی کتابخانه AndroidX استفاده خواهید کرد، به این معنی که عمدتاً بر روی Fragments و Views تمرکز خواهید کرد.

آنچه خواهید ساخت

این آزمایشگاه کد شما را در ساخت برخی از انتقال‌ها در یک برنامه ایمیل اندروید نمونه به نام Reply با استفاده از Kotlin راهنمایی می‌کند تا نشان دهد چگونه می‌توانید از انتقال‌ها از کتابخانه MDC-Android برای سفارشی‌سازی ظاهر و حس برنامه خود استفاده کنید.

کد آغازین برای برنامه Reply ارائه خواهد شد و شما انتقال‌های متریال زیر را در برنامه وارد خواهید کرد که می‌توانید در GIF تکمیل‌شده codelab در زیر مشاهده کنید:

  • انتقال کانتینر از لیست ایمیل به صفحه جزئیات ایمیل
  • انتقال کانتینر از FAB به صفحه نوشتن ایمیل
  • انتقال محور Z مشترک از آیکون جستجو به صفحه نمایش جستجو
  • محو شدن در هنگام انتقال بین صفحات صندوق پستی
  • انتقال تبدیل کانتینر از تراشه آدرس ایمیل به نمای کارت

The domain of the requested iframe (youtu.be) has not been whitelisted.

آنچه نیاز دارید

  • آشنایی اولیه با توسعه اندروید و کاتلین
  • اندروید استودیو (اگر ندارید، از اینجا دانلود کنید)
  • یک شبیه‌ساز یا دستگاه اندروید (از طریق اندروید استودیو قابل دسترسی است)
  • کد نمونه (به مرحله بعدی مراجعه کنید)

سطح تجربه خود در ساخت برنامه‌های اندروید را چگونه ارزیابی می‌کنید؟

تازه کار متوسط ماهر

۲. محیط توسعه خود را تنظیم کنید

راه اندازی اندروید استودیو

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

گزینه ۱: کپی کردن برنامه اولیه codelab از گیت‌هاب

برای کپی کردن این codelab از گیت‌هاب، دستورات زیر را اجرا کنید:

git clone https://github.com/material-components/material-components-android-motion-codelab.git
cd material-components-android-motion-codelab

گزینه ۲: فایل زیپ برنامه codelab را دانلود کنید

برنامه‌ی آغازین در دایرکتوری material-components-android-motion-codelab-develop قرار دارد.

کد شروع را در اندروید استودیو بارگذاری کنید

  1. پس از اتمام مراحل نصب و نمایش پنجره Welcome to Android Studio ، روی Open an existing Android Studio project کلیک کنید.

e3f200327a67a53.png

  1. به دایرکتوری که کد نمونه را در آن نصب کرده‌اید بروید و دایرکتوری نمونه را برای باز کردن پروژه انتخاب کنید.
  2. لحظه‌ای صبر کنید تا اندروید استودیو پروژه را بسازد و همگام‌سازی کند، همانطور که توسط نشانگرهای فعالیت در پایین پنجره اندروید استودیو نشان داده شده است.
  1. در این مرحله، ممکن است اندروید استودیو به دلیل فقدان SDK اندروید یا ابزارهای ساخت، مانند آنچه در زیر نشان داده شده است، برخی خطاهای ساخت را ایجاد کند. برای نصب/به‌روزرسانی این موارد و همگام‌سازی پروژه خود، دستورالعمل‌های موجود در اندروید استودیو را دنبال کنید. اگر هنوز با مشکل مواجه هستید، راهنمای به‌روزرسانی ابزارهای خود با SDK Manager را دنبال کنید.

6e026ae171f5b1eb.png

وابستگی‌های پروژه را تأیید کنید

این پروژه به یک وابستگی به کتابخانه MDC-Android نیاز دارد. کد نمونه‌ای که دانلود کرده‌اید باید از قبل این وابستگی را داشته باشد، اما برای اطمینان بیشتر، نگاهی به پیکربندی آن می‌اندازیم.

به فایل build.gradle ماژول app بروید و مطمئن شوید که بلوک dependencies شامل وابستگی به MDC-Android است:

implementation 'com.google.android.material:material:1.2.0'

برنامه شروع کننده را اجرا کنید

  1. مطمئن شوید که پیکربندی ساخت در سمت چپ انتخاب دستگاه، app باشد.
  2. برای ساخت و اجرای برنامه، دکمه سبز اجرا / پخش (Run / Play) را فشار دهید.

۲۴۲۱۸d0a6ae25803.png

  1. در پنجره Select Deployment Target ، اگر از قبل یک دستگاه اندروید در فهرست دستگاه‌های موجود خود دارید، به مرحله ۸ بروید. در غیر این صورت، روی Create New Virtual Device کلیک کنید.
  2. در صفحه انتخاب سخت‌افزار ، یک دستگاه تلفن مانند Pixel 3 را انتخاب کنید و سپس روی Next کلیک کنید.
  3. در صفحه System Image ، یک نسخه جدید اندروید ، ترجیحاً بالاترین سطح API، را انتخاب کنید. اگر نصب نشده است، روی لینک دانلود نمایش داده شده کلیک کنید و دانلود را تکمیل کنید.
  4. روی بعدی کلیک کنید.
  5. در صفحه دستگاه مجازی اندروید (AVD) ، تنظیمات را همانطور که هستند رها کنید و روی Finish کلیک کنید.
  6. یک دستگاه اندروید را از کادر محاوره‌ای هدف استقرار انتخاب کنید.
  7. روی تأیید کلیک کنید.
  8. اندروید استودیو برنامه را می‌سازد، آن را مستقر می‌کند و به طور خودکار آن را روی دستگاه هدف باز می‌کند.

موفقیت! کد آغازین برای صفحه اصلی Reply باید در شبیه‌ساز شما اجرا شود. شما باید صندوق ورودی (Inbox) حاوی لیستی از ایمیل‌ها را ببینید.

cc73eb0d0f779035.png

اختیاری: کاهش سرعت انیمیشن‌های دستگاه

از آنجایی که این آزمایشگاه کد شامل انتقال‌های سریع اما دقیق است، می‌تواند مفید باشد که انیمیشن‌های دستگاه را کند کنید تا بتوانید برخی از جزئیات دقیق‌تر انتقال‌ها را هنگام پیاده‌سازی مشاهده کنید. این کار را می‌توان با دستورات پوسته adb یا یک کاشی تنظیمات سریع انجام داد. توجه داشته باشید که این روش‌های کاهش سرعت انیمیشن‌های دستگاه، انیمیشن‌های دستگاه خارج از برنامه پاسخ را نیز تحت تأثیر قرار می‌دهد.

روش ۱: دستورات ADB Shell

برای کاهش سرعت انیمیشن‌های دستگاه تا ۱۰ برابر، می‌توانید دستورات زیر را از خط فرمان اجرا کنید:

adb shell settings put global window_animation_scale 10
adb shell settings put global transition_animation_scale 10
adb shell settings put global animator_duration_scale 10

برای تنظیم مجدد سرعت انیمیشن دستگاه به حالت عادی، دستورات زیر را اجرا کنید:

adb shell settings put global window_animation_scale 1
adb shell settings put global transition_animation_scale 1
adb shell settings put global animator_duration_scale 1

روش ۲: کاشی تنظیمات سریع

روش دیگر، برای تنظیم کاشی تنظیمات سریع، ابتدا تنظیمات توسعه‌دهنده را در دستگاه خود فعال کنید، اگر قبلاً این کار را نکرده‌اید:

  1. برنامه «تنظیمات» دستگاه را باز کنید
  2. به پایین صفحه بروید و روی «درباره دستگاه شبیه‌سازی‌شده» کلیک کنید.
  3. به پایین صفحه بروید و سریع روی «شماره ساخت» کلیک کنید تا تنظیمات توسعه‌دهندگان فعال شود.

در مرحله بعد، مراحل زیر را، همچنان در برنامه «تنظیمات» دستگاه، برای فعال کردن کاشی تنظیمات سریع انجام دهید:

  1. روی نماد جستجو یا نوار جستجو در بالای صفحه کلیک کنید
  2. در قسمت جستجو عبارت "tiles" را تایپ کنید.
  3. روی ردیف «کاشی‌های توسعه‌دهنده تنظیمات سریع» کلیک کنید
  4. روی کلید «مقیاس انیمیشن پنجره» کلیک کنید

در نهایت، در طول کدلاگ، نوار اعلان سیستم را از بالای صفحه پایین بکشید و از c7e3f98200023f6a.png آیکون برای تغییر بین انیمیشن‌های با سرعت آهسته و معمولی.

۳. با کد نمونه برنامه آشنا شوید

بیایید نگاهی به کد بیندازیم. ما برنامه‌ای ارائه داده‌ایم که از کتابخانه کامپوننت Jetpack Navigation برای پیمایش بین چند Fragment مختلف، همه درون یک Activity واحد، MainActivity ، استفاده می‌کند:

  • HomeFragment: فهرستی از ایمیل‌ها را نمایش می‌دهد.
  • EmailFragment: یک ایمیل کامل و واحد را نمایش می‌دهد.
  • ComposeFragment: امکان نوشتن یک ایمیل جدید را فراهم می‌کند.
  • SearchFragment: نمای جستجو را نمایش می‌دهد.

ابتدا، برای درک نحوه تنظیم نمودار ناوبری برنامه، navigation_graph.xml را در پوشه app -> src -> main -> res -> navigation باز کنید:

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:id="@+id/navigation_graph"
   app:startDestination="@id/homeFragment">

   <fragment
       android:id="@+id/homeFragment"
       android:name="com.materialstudies.reply.ui.home.HomeFragment"
       android:label="HomeFragment">
       <argument...>
       <action
           android:id="@+id/action_homeFragment_to_emailFragment"
           app:destination="@id/emailFragment" />
   </fragment>
   <fragment
       android:id="@+id/emailFragment"
       android:name="com.materialstudies.reply.ui.email.EmailFragment"
       android:label="EmailFragment">
       <argument...>
   </fragment>
   <fragment
       android:id="@+id/composeFragment"
       android:name="com.materialstudies.reply.ui.compose.ComposeFragment"
       android:label="ComposeFragment">
       <argument...>
   </fragment>
   <fragment
       android:id="@+id/searchFragment"
       android:name="com.materialstudies.reply.ui.search.SearchFragment"
       android:label="SearchFragment" />
   <action
       android:id="@+id/action_global_homeFragment"
       app:destination="@+id/homeFragment"
       app:launchSingleTop="true"
       app:popUpTo="@+id/navigation_graph"
       app:popUpToInclusive="true"/>
   <action
       android:id="@+id/action_global_composeFragment"
       app:destination="@+id/composeFragment" />
   <action
       android:id="@+id/action_global_searchFragment"
       app:destination="@+id/searchFragment" />
</navigation>

به نحوه‌ی حضور تمام فرگمنت‌های ذکر شده در بالا توجه کنید، به طوری که فرگمنت راه‌اندازی پیش‌فرض از طریق app:startDestination="@id/homeFragment" روی HomeFragment تنظیم شده است. این تعریف XML از گراف مقصد فرگمنت، و همچنین اکشن‌ها، کد ناوبری کاتلین تولید شده‌ای را که هنگام اتصال انتقال‌ها با آن مواجه خواهید شد، اطلاع می‌دهد.

activity_main.xml

در مرحله بعد، نگاهی به طرح‌بندی activity_main.xml در پوشه app -> src -> main -> res -> layout بیندازید. NavHostFragment را خواهید دید که با نمودار ناوبری بالا پیکربندی شده است:

<fragment
   android:id="@+id/nav_host_fragment"
   android:name="androidx.navigation.fragment.NavHostFragment"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   app:defaultNavHost="true"
   app:navGraph="@navigation/navigation_graph"/>

این NavHostFragment صفحه را پر می‌کند و تمام تغییرات ناوبری قطعه کد تمام صفحه را در برنامه مدیریت می‌کند. BottomAppBar و FloatingActionButton متصل به آن، که آن هم در activity_main.xml قرار دارد، در بالای قطعه کد فعلی نمایش داده شده توسط NavHostFragment قرار می‌گیرند و بنابراین بسته به مقصد قطعه کد ارائه شده در نمونه برنامه، نمایش داده یا پنهان می‌شوند.

علاوه بر این، BottomNavDrawerFragment در activity_main.xml یک کشوی پایینی است که شامل منویی برای پیمایش بین صندوق‌های پستی ایمیل مختلف است که به صورت شرطی از طریق دکمه لوگوی BottomAppBar Reply نمایش داده می‌شود.

فعالیت اصلی.kt

در نهایت، برای دیدن نمونه‌ای از یک اکشن ناوبری که استفاده می‌شود، MainActivity.kt در مسیر app -> src -> main -> java -> com.materialstudies.reply.ui باز کنید. تابع navigateToSearch() را پیدا کنید که باید چیزی شبیه به این باشد:

private fun navigateToSearch() {
   val directions = SearchFragmentDirections.actionGlobalSearchFragment()
   findNavController(R.id.nav_host_fragment).navigate(directions)
}

این نشان می‌دهد که چگونه می‌توانید بدون هیچ گونه انتقال سفارشی، به صفحه نمای جستجو بروید. در طول این آزمایشگاه کد، شما به MainActivity مربوط به Reply و چهار قطعه اصلی برای تنظیم انتقال‌های Material که همزمان با اقدامات مختلف ناوبری در سراسر برنامه کار می‌کنند، خواهید پرداخت.

حالا که با کد آغازین آشنا شدید، بیایید اولین گذار خود را پیاده‌سازی کنیم.

۴. اضافه کردن کانتینر تبدیل انتقال از لیست ایمیل به صفحه جزئیات ایمیل

برای شروع، هنگام کلیک روی یک ایمیل، یک گذار اضافه خواهید کرد. برای این تغییر ناوبری، الگوی تبدیل کانتینر (container transform pattern) مناسب است، زیرا برای گذار بین عناصر رابط کاربری که شامل یک کانتینر هستند طراحی شده است. این الگو یک اتصال قابل مشاهده بین دو عنصر رابط کاربری ایجاد می‌کند.

قبل از اضافه کردن هر کدی، برنامه Reply را اجرا کنید و روی یک ایمیل کلیک کنید. باید یک پرش ساده انجام شود، به این معنی که صفحه بدون هیچ انتقالی جایگزین می‌شود:

f0e8a92eb2216bce.gif

با اضافه کردن یک ویژگی transitionName به MaterialCardView در email_item_layout.xml همانطور که در قطعه کد زیر نشان داده شده است، شروع کنید:

email_item_layout.xml

android:transitionName="@{@string/email_card_transition_name(email.id)}"

نام گذار یک منبع رشته‌ای به همراه یک پارامتر می‌گیرد. شما باید از شناسه هر ایمیل استفاده کنید تا مطمئن شوید که هر transitionName در EmailFragment ما منحصر به فرد است.

حالا که نام گذار آیتم لیست ایمیل خود را تنظیم کرده‌اید، بیایید همین کار را در طرح‌بندی جزئیات ایمیل انجام دهیم. در fragment_email.xml ، transitionName MaterialCardView را روی منبع رشته‌ای زیر تنظیم کنید:

fragment_email.xml

android:transitionName="@string/email_card_detail_transition_name"

در HomeFragment.kt ، کد موجود در onEmailClicked را با قطعه کد زیر جایگزین کنید تا نگاشت از نمای شروع (آیتم لیست ایمیل) و نمای پایان (صفحه جزئیات ایمیل) ایجاد شود:

HomeFragment.kt

val emailCardDetailTransitionName = getString(R.string.email_card_detail_transition_name)
val extras = FragmentNavigatorExtras(cardView to emailCardDetailTransitionName)
val directions = HomeFragmentDirections.actionHomeFragmentToEmailFragment(email.id)
findNavController().navigate(directions, extras)

حالا که پیکربندی piping را انجام داده‌اید، می‌توانید یک container transform ایجاد کنید. در متد EmailFragment onCreate ، با اضافه کردن قطعه کد زیر، sharedElementEnterTransition را روی یک نمونه جدید از MaterialContainerTransform تنظیم کنید (نسخه com.google.android.material.transition را به جای نسخه com.google.android.material.transition.platform وارد کنید):

EmailFragment.kt

sharedElementEnterTransition = MaterialContainerTransform().apply {
   drawingViewId = R.id.nav_host_fragment
   duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
   scrimColor = Color.TRANSPARENT
   setAllContainerColors(requireContext().themeColor(R.attr.colorSurface))
}

حالا دوباره سعی کنید برنامه را اجرا کنید.

ed62cedec31da268.gif

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

برای رفع مشکل انتقال بازگشتی، دو خط زیر را به متد onViewCreated در HomeFragment.kt اضافه کنید:

HomeFragment.kt

postponeEnterTransition()
view.doOnPreDraw { startPostponedEnterTransition() }

برنامه را دوباره اجرا کنید. فشردن دکمه بازگشت (back) بعد از باز کردن یک ایمیل، آن را به لیست برمی‌گرداند. عالی! بیایید به بهبود انیمیشن ادامه دهیم.

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

قطعه کد زیر را به متد HomeFragment onEmailClicked اضافه کنید تا لیست ایمیل‌ها هنگام خروج به طور نامحسوس کوچک و هنگام ورود مجدد به حالت اولیه برگردد:

HomeFragment.kt

exitTransition = MaterialElevationScale(false).apply {
   duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
reenterTransition = MaterialElevationScale(true).apply {
   duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}

در مرحله بعد، برای اطمینان از اینکه گذار MaterialElevationScale به جای هر یک از نماهای جداگانه در سلسله مراتب، به طور کلی روی صفحه اصلی اعمال می‌شود، RecyclerView در fragment_home.xml به عنوان یک گروه گذار علامت‌گذاری کنید.

fragment_home.xml

android:transitionGroup="true"

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

9df2b39d5a150418.gif

۵. اضافه کردن انتقال Container Transform از FAB به صفحه نوشتن ایمیل

بیایید با تبدیل کانتینر ادامه دهیم و یک گذار از دکمه اکشن شناور به ComposeFragment اضافه کنیم و FAB را به یک ایمیل جدید که قرار است توسط کاربر نوشته شود، گسترش دهیم. ابتدا برنامه را دوباره اجرا کنید و روی FAB کلیک کنید تا ببینید هنگام راه‌اندازی صفحه نوشتن ایمیل هیچ گذار وجود ندارد.

d242c9708abd382c.gif

اگرچه ما از همان کلاس انتقال استفاده می‌کنیم، اما نحوه پیکربندی این نمونه متفاوت خواهد بود زیرا FAB ما در MainActivity قرار دارد و ComposeFragment ما درون کانتینر میزبان ناوبری MainActivity قرار دارد.

در ComposeFragment.kt ، قطعه کد زیر را به متد onViewCreated اضافه کنید و مطمئن شوید که نسخه androidx.transition از Slide را وارد می‌کنید.

ComposeFragment.kt

enterTransition = MaterialContainerTransform().apply {
   startView = requireActivity().findViewById(R.id.fab)
   endView = emailCardView
   duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
   scrimColor = Color.TRANSPARENT
   containerColor = requireContext().themeColor(R.attr.colorSurface)
   startContainerColor = requireContext().themeColor(R.attr.colorSecondary)
   endContainerColor = requireContext().themeColor(R.attr.colorSurface)
}
returnTransition = Slide().apply {
   duration = resources.getInteger(R.integer.reply_motion_duration_medium).toLong()
   addTarget(R.id.email_card_view)
}

علاوه بر پارامترهایی که برای پیکربندی تبدیل کانتینر قبلی ما استفاده شدند، startView و endView نیز در اینجا به صورت دستی تنظیم می‌شوند. به جای استفاده از ویژگی‌های transitionName برای اینکه به سیستم انتقال اندروید اطلاع دهید کدام نماها باید تبدیل شوند، می‌توانید در صورت لزوم این موارد را به صورت دستی مشخص کنید.

حالا، برنامه را دوباره اجرا کنید. باید ببینید که FAB به صفحه نوشتن تبدیل می‌شود (به GIF انتهای این مرحله مراجعه کنید).

مشابه مرحله قبل، باید یک گذار به HomeFragment اضافه کنید تا پس از حذف و جایگزینی با ComposeFragment ناپدید نشود.

قطعه کد زیر را قبل از navigate NavController navigation در متد navigateToCompose در MainActivity کپی کنید.

فعالیت اصلی.kt

currentNavigationFragment?.apply {
   exitTransition = MaterialElevationScale(false).apply {
       duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
   }
   reenterTransition = MaterialElevationScale(true).apply {
       duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
   }
}

این تمام مرحله‌ی ماست! شما باید یک صفحه‌ی انتقال از FAB به صفحه‌ی نوشتن کد به شکل زیر داشته باشید:

81b68391ac4b0a9.gif

۶. اضافه کردن انتقال محور Z مشترک از آیکون جستجو به صفحه نمایش جستجو

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

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

499e1a677b4216bb.gif

برای شروع، متد navigateToSearch را در MainActivity پیدا کنید و قطعه کد زیر را قبل از فراخوانی متد navigate NavController اضافه کنید تا انتقال‌های Z-Axis مربوط به خروج و ورود مجدد قطعه کد فعلی MaterialSharedAxis تنظیم شوند.

فعالیت اصلی.kt

currentNavigationFragment?.apply {
   exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).apply {
       duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
   }
   reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false).apply {
       duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
   }
}

در مرحله بعد، قطعه کد زیر را به متد onCreate در SearchFragment اضافه کنید، که انتقال‌های ورودی و خروجی MaterialSharedAxis آن را پیکربندی می‌کند.

SearchFragment.kt

enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).apply {
   duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false).apply {
   duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}

در نهایت، برای اطمینان از اینکه گذار MaterialSharedAxis به جای اعمال روی هر یک از نماهای جداگانه در سلسله مراتب، روی کل صفحه جستجو اعمال می‌شود، LinearLayout در fragment_search.xml به عنوان یک گروه گذار علامت‌گذاری کنید.

fragment_search.xml

android:transitionGroup="true"

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

e5c0b0a130e807db.gif

۷. اضافه کردن گذار محوشونده بین صفحات صندوق پستی

در این مرحله، یک انتقال بین صندوق‌های پستی مختلف اضافه خواهیم کرد. از آنجایی که نمی‌خواهیم بر یک رابطه مکانی یا سلسله مراتبی تأکید کنیم، از یک محوشدگی برای انجام یک "جابجایی" ساده بین لیست‌های ایمیل استفاده خواهیم کرد.

قبل از اضافه کردن هرگونه کد اضافی، برنامه را اجرا کنید، روی لوگوی پاسخ در نوار پایین برنامه ضربه بزنید و صندوق‌های پستی را تغییر دهید. لیست ایمیل‌ها باید بدون هیچ تغییری تغییر کند.

2c874c0a4588e8fb.gif

برای شروع، متد navigateToHome را در MainActivity پیدا کنید و قطعه کد زیر را قبل از فراخوانی متد navigate NavController اضافه کنید تا گذار خروجی MaterialFadeThrough قطعه کد فعلی تنظیم شود.

فعالیت اصلی.kt

currentNavigationFragment?.apply {
   exitTransition = MaterialFadeThrough().apply {
       duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
   }
}

سپس، HomeFragment را باز کنید. در onCreate ، مقدار enterTransition مربوط به فرگمنت را روی یک نمونه جدید از MaterialFadeThrough تنظیم کنید.

HomeFragment.kt

enterTransition = MaterialFadeThrough().apply {
   duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}

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

f61dfd58ea7bd3fd.gif

۸. اضافه کردن انتقال Container Transform از تراشه آدرس ایمیل به نمای کارت

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

قبل از اضافه کردن هر کدی، برنامه Reply را اجرا کنید، روی یک ایمیل کلیک کنید، روی FAB «پاسخ» کلیک کنید و سپس روی تراشه مخاطب گیرنده کلیک کنید. تراشه باید فوراً ناپدید شود و کارتی حاوی آدرس‌های ایمیل آن مخاطب بدون هیچ انیمیشنی نمایش داده شود.

6200c682da2382d5.gif

شما در این مرحله با ComposeFragment کار خواهید کرد. تراشه‌های گیرنده (که به طور پیش‌فرض قابل مشاهده هستند) و یک کارت گیرنده (که به طور پیش‌فرض نامرئی هستند) از قبل در طرح ComposeFragment اضافه شده‌اند. یک تراشه گیرنده و این کارت دو نمایی هستند که شما یک تبدیل کانتینر بین آنها ایجاد خواهید کرد.

برای شروع، ComposeFragment باز کنید و متد expandChip را پیدا کنید. این متد زمانی فراخوانی می‌شود که chip ارائه شده کلیک شود. قطعه کد زیر را بالای خطوطی که recipientCardView و chip visibility را جابجا می‌کنند، اضافه کنید، که باعث فعال شدن تبدیل کانتینر ثبت شده از طریق beginDelayedTransition می‌شود.

ComposeFragment.kt

val transform = MaterialContainerTransform().apply {
   startView = chip
   endView = binding.recipientCardView
   scrimColor = Color.TRANSPARENT
   endElevation = requireContext().resources.getDimension(
       R.dimen.email_recipient_card_popup_elevation_compat
   )
   addTarget(binding.recipientCardView)
}

TransitionManager.beginDelayedTransition(binding.composeConstraintLayout, transform)

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

در متد collapseChip در ComposeFragment ، قطعه کد زیر را برای جمع کردن مجدد کارت به داخل تراشه اضافه کنید.

ComposeFragment.kt

val transform = MaterialContainerTransform().apply {
   startView = binding.recipientCardView
   endView = chip
   scrimColor = Color.TRANSPARENT
   startElevation = requireContext().resources.getDimension(
       R.dimen.email_recipient_card_popup_elevation_compat
   )
   addTarget(chip)
}

TransitionManager.beginDelayedTransition(binding.composeConstraintLayout, transform)

برنامه را دوباره اجرا کنید. کلیک کردن روی تراشه باید تراشه را به یک کارت تبدیل کند، در حالی که کلیک کردن روی کارت، کارت را دوباره به داخل تراشه جمع می‌کند. عالی شد!

e823b28e2890e05d.gif

۹. همه چیز تمام شد

کتابخانه MDC-Android با استفاده از کمتر از ۱۰۰ خط کد کاتلین و مقداری نشانه‌گذاری XML اولیه، به شما کمک کرده است تا انتقال‌های زیبایی را در یک برنامه موجود ایجاد کنید که با دستورالعمل‌های طراحی متریال مطابقت دارد و همچنین در تمام دستگاه‌های اندروید به طور مداوم ظاهر و رفتار می‌کند.

454a47ba96017a25.gif

مراحل بعدی

برای اطلاعات بیشتر در مورد سیستم حرکت متریال، حتماً مشخصات و مستندات کامل توسعه‌دهنده را بررسی کنید و سعی کنید چند انتقال متریال به برنامه خود اضافه کنید!

ممنون که حرکت مواد را امتحان کردید. امیدواریم از این آزمایشگاه کدنویسی لذت برده باشید!

من توانستم این آزمایشگاه کد را با مقدار قابل توجهی از زمان و تلاش تکمیل کنم.

کاملاً موافقم موافق خنثی مخالف کاملاً مخالفم

من دوست دارم در آینده به استفاده از سیستم حرکت مواد ادامه دهم.

کاملاً موافقم موافق خنثی مخالف کاملاً مخالفم