آشنایی با متریال دیزاین موشن در اندروید – از صفر تا صد


یکی از خصوصیات مهم «متریال دیزاین» (Material Design)، گذار عناصر و کامپوننتها برای بیان رابطه بین آنها و خروجیها و موجود بودن اکشن است. از نسخه 1.2.0-alpha05 به بعد کامپوننتهای متریال برای کتابخانه اندروید از Transitions و Motion بهره میگیرند. اینها مجموعهای از کلاسها با استفاده آسان برای پیادهسازی نوع متفاوتی از گذار و انیمیشن قابل سفارشیسازی هستند. در این مقاله با کاربردهای متریال دیزاین موشن در اندروید آشنا خواهیم شد.
الگوهای متفاوتی وجود دارند که سیستم موشن متریال دیزاین را برای ایجاد گذار بین نماهای تمام صفحه تشکیل میدهند. نکته مهم این است که الگوی گذاری با نوع مناسب را برای هر مورد انتخاب کنیم. این الگوهای گذار به شرح زیر هستند:
- تبدیل کانتینر (Container Transform) – این الگو در گذارهایی استفاده میشود که شامل عنصری دائمی از قبیل یک لیست، سطح کارت یا دکمه باشند.
- محور مشترک (Shared Axis) – این الگو برای گذارهایی بین عناصر UI که روابط فضایی یا ناوبری دارند مورد استفاده قرار میگیرد.
- محو شدن روی هم (Fade Through) – زمانی که یک رابطه بین عناصر مهم نیست یا وجود ندارد مورد استفاده قرار میگیرد.
- محو شدن (Fade) – این الگو در مواردی استفاده میشود که عناصر UI باید وارد صفحه و یا از آن خارج شوند.
implementation('com.google.android.material:material:1.2.0-beta01')
در ادامه هر یک از موارد فوق را به تفصیل شرح میدهیم.
تبدیل کانتینر
این الگو برای ایجاد یک ارتباط آشکار بین دو عنصر UI استفاده میشود. این الگو یک عنصر را به عنصر دیگر تبدیل میکند. این یک الگوی رایج UX برای بیان این نکته است که یک عنصر با عنصر دیگر مانند کانتینر جزییات آیتم ارتباط دارد. برای نمونه اگر یک آیتم در لیست به نمای جزییات تبدیل شود، کاربر درک میکند که صفحه جزییات نسخه بسط یافتهای از کارت قبلی محسوب میشود.
از این گذار میتوان به عنوان یک تبدیل دکمه FAB به منو یا برگه نیز استفاده کرد. از سوی دیگر این گذار را میتوان بین اکتیویتیها، فرگمانها یا نماها بسته به این که یک نما در یک اکتیویتی یا فرگمان قرار است به نمایی در اکتیویتی یا فرگمان دیگر تبدیل شود یا نه مورد استفاده قرار داد.
این تبدیل با تعیین مقادیر مختلف برای مشخصههایی که روی گذار تأثیر دارند به صورت کامل قابل سفارشیسازی است. این مشخصهها به شرح زیر هستند.
مدت (Duration)
این مشخصه مدت این گذار را برحسب میلیثانیه تعیین میکند.
Interpolator (درونیاب)
نوع درونیابی گذار را تعیین میکند. ما میتوانیم برخی انواع درونیاب از قبیل FastOutSlowIn را تعیین کنیم و یا درونیاب های سفارشی را با استفاده از FastOutSlowIn برای یک منحنی بزیه مکعبی مورد استفاده قرار دهیم.
FadeMode (حالت محو شدن)
این مشخصه نوع محو شدن را که برای تعویض محتوای نمای آغازین یا نمای پایانی استفاده میشود، تعیین میکند. چهار حالت محو شدن وجود دارند:
- FADE_MODE_IN – محتوای ورودی را بدون تغییر دادن میزان مات بودن محتوای خروجی محو میکند. این حالت پیشفرض است.
- FADE_MODE_OUT – محتوای خروجی را بدون تغییر دادن میزان مات بودن محتوای ورودی محو میکند.
- FADE_MODE_CROSS – محتوای ورودی و خروجی را به صورت متقاطع محو میکند.
- FADE_MODE_THROUGH - به صورت ترتیبی محتوای خروجی را فیدآوت و محتوای ورودی را فیداین میکند.
FitMode
این مشخصه سه گزینه دارد.
- FIT_MODE_HEIGHT - در طی مقیاس انیمیشن، محتوای ورودی را با ارتفاع محتوای خروجی تطبیق میدهد.
- FIT_MODE_WIDTH – در طی مقیاس انیمیشن، محتوای ورودی را با عرض محتوای خروجی تطبیق میدهد.
- FIT_MODE_AUTO – به صورت خودکار از FIT_MODE_HEIGHT یا FIT_MODE_WIDTH استفاده میکند.
containerColor
رنگ پسزمینه کانتینر مورفشونده را تعیین میکند. این رنگ زیر نماهای آغازی و پایانی رسم میشود. این مشخصه در مواردی که یک یا هر دوی نماها پسزمینه تکرنگی ندارند، بسیار مفید است. از این مشخصه میتوان برای تنظیم رنگ گذار و بهبود تبدیل از یک نما به نمای دیگر نیز بهره برد.
scrimColor
این مشخصه رنگی که باید زیر کانتینر مورفشونده و درون مرزهای drawingView رسم شود را تعیین میکند. این drawingView نمایی است که گذار روی آن همپوشانی خواهد یافت. drawingView را میتوان با استفاده از تابع ()setDrawingViewId مشخص کرد.
1 private fun buildContainerTransform() =
2 MaterialContainerTransform().apply {
3 addTarget(binding.coordinator)
4 duration = 500
5 fadeMode = MaterialContainerTransform.FADE_MODE_IN
6 interpolator = FastOutSlowInInterpolator()
7 }
FAB to Menu
این گذار یک FAB را به Menu تبدیل میکند. در این مورد باید نماهای آغازین و پایانی را تعیین کنیم.
1 private fun buildContainerTransformation() =
2 MaterialContainerTransform().apply {
3 scrimColor = Color.TRANSPARENT
4 duration = 300
5 interpolator = FastOutSlowInInterpolator()
6 fadeMode = MaterialContainerTransform.FADE_MODE_IN
7 }
8
9
10 private fun setClickListeners() {
11 binding.floatingActionButton.setOnClickListener {
12 val transition = buildContainerTransformation()
13
14 transition.startView = binding.floatingActionButton
15 transition.endView = binding.card
16
17 transition.addTarget(binding.card)
18
19 TransitionManager.beginDelayedTransition(findViewById(android.R.id.content), transition)
20 binding.card.visibility = View.VISIBLE
21 binding.fabScrim.visibility = View.VISIBLE
22
23 binding.floatingActionButton.visibility = View.INVISIBLE
24 }
25 }
FAB to Activity
این گذار یک FAB را به یک اکتیویتی تبدیل میکند. این گذار از MaterialArcMotion برای جابجایی کانتینر در راستای یک منحنی بهره میگیرد.

1 private fun buildContainerTransform() =
2 MaterialContainerTransform().apply {
3 addTarget(binding.coordinator)
4 setAllContainerColors(MaterialColors.getColor(binding.root, R.attr.colorSurface))
5 pathMotion = MaterialArcMotion()
6 duration = 500
7 interpolator = FastOutSlowInInterpolator()
8 fadeMode = MaterialContainerTransform.FADE_MODE_IN
9 }
MaterialCardView to Activity
این گذار یک آیتم لیست را به یک اکتیویتی تبدیل میکند. ما میتوانیم تفاوت بین FADE_MODE_IN و FADE_MODE_OUT را مشاهده کنیم.

1 inner class ViewHolder(val binding: ItemNoteBinding) : RecyclerView.ViewHolder(binding.root) {
2 fun bind(note: Note) {
3 binding.note = note
4 binding.noteCard.transitionName = note.id.toString()
5 binding.noteCard.setOnClickListener {
6 noteClickListener.onNoteClick(note.id, binding.noteCard)
7 }
8 }
9 }
1 val adapter = NotesAdapter(notes)
2 adapter.noteClickListener = object : NotesAdapter.NoteClickListener {
3 override fun onNoteClick(id: Int, noteCard: MaterialCardView) {
4 val intent = Intent(this@NotesActivity, NoteDetailActivity::class.java)
5 val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
6 this@NotesActivity, noteCard, id.toString()
7 )
8 intent.putExtra("noteId", id)
9 startActivity(intent, options.toBundle())
10 }
11 }
1class NoteDetailActivity : AppCompatActivity() {
2 private lateinit var binding: NoteDetailActivityBinding
3
4 override fun onCreate(savedInstanceState: Bundle?) {
5 window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
6 binding = NoteDetailActivityBinding.inflate(layoutInflater)
7 val noteId = intent.getIntExtra("noteId", 0)
8 val note = notes.find { it.id == noteId }
9 binding.note = note
10 binding.coordinator.transitionName = noteId.toString()
11 setEnterSharedElementCallback(MaterialContainerTransformSharedElementCallback())
12 window.sharedElementEnterTransition = buildContainerTransform()
13 window.sharedElementReturnTransition = buildContainerTransform()
14 setContentView(binding.root)
15 super.onCreate(savedInstanceState)
16 }
17
18 private fun buildContainerTransform() =
19 MaterialContainerTransform().apply {
20 addTarget(binding.coordinator)
21 duration = 300
22 interpolator = FastOutSlowInInterpolator()
23 fadeMode = MaterialContainerTransform.FADE_MODE_IN
24 }
25}
تبدیل کانتینر در کامپوننت ناوبری اندروید
1 val adapter = AlbumsAdapter(albums, requireContext())
2 adapter.albumClickListener = object : AlbumsAdapter.AlbumClickListener {
3 override fun onAlbumClick(id: Int, cardView: MaterialCardView) {
4 val extras = FragmentNavigatorExtras(
5 cardView to id.toString()
6 )
7 val action = AlbumsFragmentDirections.navToAlbumFragment(id)
8 findNavController().navigate(action, extras)
9 }
10 }
1 override fun onCreate(savedInstanceState: Bundle?) {
2 super.onCreate(savedInstanceState)
3 sharedElementEnterTransition = buildContainerTransform()
4 sharedElementReturnTransition = buildContainerTransform()
5 }
6
7 private fun buildContainerTransform() =
8 MaterialContainerTransform().apply {
9 drawingViewId = R.id.nav_host_fragment
10 interpolator = FastOutSlowInInterpolator()
11 containerColor = Color.WHITE
12 fadeMode = MaterialContainerTransform.FADE_MODE_OUT
13 duration = 300
14 }
محور مشترک (Shared Axis)
این الگو برای گذار بین عناصر UI که روابط فضایی یا ناوبری خاصی دارند مورد استفاده قرار میگیرد. این گذار از تبدیلهای روی محورهای x، y یا z برای تأکید مجدد روی رابطه بین عناصر بهره میگیرد. به این ترتیب عناصر با جابجایی در جهت یکسان، با همدیگر مرتبط تصور میشوند.
1 val forward = true
2 val fragment = MyFragment.newInstance()
3 // axis: MaterialSharedAxis.X, Y or Z
4 // forward: true if it move in the forward direction or false if it move in the backward direction
5 fragment.enterTransition = MaterialSharedAxis(MaterialSharedAxis.Y, forward)
6 supportFragmentManager
7 .beginTransaction()
8 .replace(R.id.fragment_container, fragment)
9 .commit()
محور X
در تصویر فوق مثالی از استفاده از گذار MaterialSharedAxis روی محور X برای یک ناوبری در اپلیکیشن را میبینید.
محور Y
در تصویر فوق یک stepper عمودی با استفاده از گذار MaterialSharedAxis روی محور Y میبینید.
محور Z
گذارهای محور Z شامل جابجایی یک سطح به سمت بالا یا پایین در سلسله مراتب اپلیکیشن است.
Fade Through
این الگو برای گذار بین عناصر UI که رابطهای قوی با همدیگر دارند مورد استفاده قرار میگیرد. گذار Fade through بهترین گزینه در یک نمای ناوبری تحتانی محسوب میشود، زیرا متدها غالب در وظایف عمدهای گروهبندی شدهاند که ممکن است با همدیگر مرتبط نباشند.
1class ArtistsFragment : Fragment() {
2
3 override fun onCreate(savedInstanceState: Bundle?) {
4 super.onCreate(savedInstanceState)
5 enterTransition = MaterialFadeThrough()
6 }
7
8 ...
9}
Fade
این الگو برای آن عناصر UI استفاده میشود که درون مرزهای صفحه وارد یا از آن خارج میشوند. از جمله این موارد شامل یک دیالوگ است که از سمت مرکز در یک نما فیداین یا فیدآوت میشود.
1// import androidx.transition.TransitionManager
2
3 binding.floatingActionButton.post {
4 val transition = MaterialFade().apply {
5 duration = 1500
6 }
7 TransitionManager.beginDelayedTransition(binding.root, transition)
8 binding.floatingActionButton.visibility = View.VISIBLE
9 }
سخن پایانی
به این ترتیب به پایان این مقاله با موضوع معرفی متریال دیزاین موشن برای اندروید میرسیم. در این مطلب تلاش کردیم با انواع انیمیشن و گذارهای کامپوننت محبوب متریال دیزاین در اندروید آشنا شویم. امیدواریم این مطلب مورد توجه شما قرار گرفته باشد.