یکی از خصوصیات مهم «متریال دیزاین» (Material Design)، گذار عناصر و کامپوننتها برای بیان رابطه بین آنها و خروجیها و موجود بودن اکشن است. از نسخه 1.2.0-alpha05 به بعد کامپوننتهای متریال برای کتابخانه اندروید از Transitions و Motion بهره میگیرند. اینها مجموعهای از کلاسها با استفاده آسان برای پیادهسازی نوع متفاوتی از گذار و انیمیشن قابل سفارشیسازی هستند. در این مقاله با کاربردهای متریال دیزاین موشن در اندروید آشنا خواهیم شد.
الگوهای متفاوتی وجود دارند که سیستم موشن متریال دیزاین را برای ایجاد گذار بین نماهای تمام صفحه تشکیل میدهند. نکته مهم این است که الگوی گذاری با نوع مناسب را برای هر مورد انتخاب کنیم. این الگوهای گذار به شرح زیر هستند:
implementation('com.google.android.material:material:1.2.0-beta01')
در ادامه هر یک از موارد فوق را به تفصیل شرح میدهیم.
این الگو برای ایجاد یک ارتباط آشکار بین دو عنصر UI استفاده میشود. این الگو یک عنصر را به عنصر دیگر تبدیل میکند. این یک الگوی رایج UX برای بیان این نکته است که یک عنصر با عنصر دیگر مانند کانتینر جزییات آیتم ارتباط دارد. برای نمونه اگر یک آیتم در لیست به نمای جزییات تبدیل شود، کاربر درک میکند که صفحه جزییات نسخه بسط یافتهای از کارت قبلی محسوب میشود.
از این گذار میتوان به عنوان یک تبدیل دکمه FAB به منو یا برگه نیز استفاده کرد. از سوی دیگر این گذار را میتوان بین اکتیویتیها، فرگمانها یا نماها بسته به این که یک نما در یک اکتیویتی یا فرگمان قرار است به نمایی در اکتیویتی یا فرگمان دیگر تبدیل شود یا نه مورد استفاده قرار داد.
این تبدیل با تعیین مقادیر مختلف برای مشخصههایی که روی گذار تأثیر دارند به صورت کامل قابل سفارشیسازی است. این مشخصهها به شرح زیر هستند.
این مشخصه مدت این گذار را برحسب میلیثانیه تعیین میکند.
نوع درونیابی گذار را تعیین میکند. ما میتوانیم برخی انواع درونیاب از قبیل FastOutSlowIn را تعیین کنیم و یا درونیاب های سفارشی را با استفاده از FastOutSlowIn برای یک منحنی بزیه مکعبی مورد استفاده قرار دهیم.
این مشخصه نوع محو شدن را که برای تعویض محتوای نمای آغازین یا نمای پایانی استفاده میشود، تعیین میکند. چهار حالت محو شدن وجود دارند:
این مشخصه سه گزینه دارد.
رنگ پسزمینه کانتینر مورفشونده را تعیین میکند. این رنگ زیر نماهای آغازی و پایانی رسم میشود. این مشخصه در مواردی که یک یا هر دوی نماها پسزمینه تکرنگی ندارند، بسیار مفید است. از این مشخصه میتوان برای تنظیم رنگ گذار و بهبود تبدیل از یک نما به نمای دیگر نیز بهره برد.
این مشخصه رنگی که باید زیر کانتینر مورفشونده و درون مرزهای drawingView رسم شود را تعیین میکند. این drawingView نمایی است که گذار روی آن همپوشانی خواهد یافت. drawingView را میتوان با استفاده از تابع ()setDrawingViewId مشخص کرد.
private fun buildContainerTransform() =
MaterialContainerTransform().apply {
addTarget(binding.coordinator)
duration = 500
fadeMode = MaterialContainerTransform.FADE_MODE_IN
interpolator = FastOutSlowInInterpolator()
}

این گذار یک FAB را به Menu تبدیل میکند. در این مورد باید نماهای آغازین و پایانی را تعیین کنیم.
private fun buildContainerTransformation() =
MaterialContainerTransform().apply {
scrimColor = Color.TRANSPARENT
duration = 300
interpolator = FastOutSlowInInterpolator()
fadeMode = MaterialContainerTransform.FADE_MODE_IN
}
private fun setClickListeners() {
binding.floatingActionButton.setOnClickListener {
val transition = buildContainerTransformation()
transition.startView = binding.floatingActionButton
transition.endView = binding.card
transition.addTarget(binding.card)
TransitionManager.beginDelayedTransition(findViewById(android.R.id.content), transition)
binding.card.visibility = View.VISIBLE
binding.fabScrim.visibility = View.VISIBLE
binding.floatingActionButton.visibility = View.INVISIBLE
}
}
این گذار یک FAB را به یک اکتیویتی تبدیل میکند. این گذار از MaterialArcMotion برای جابجایی کانتینر در راستای یک منحنی بهره میگیرد.

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

ier class ViewHolder(val binding: ItemNoteBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(note: Note) {
binding.note = note
binding.noteCard.transitionName = note.id.toString()
binding.noteCard.setOnClickListener {
noteClickListener.onNoteClick(note.id, binding.noteCard)
}
}
}
val adapter = NotesAdapter(notes)
adapter.noteClickListener = object : NotesAdapter.NoteClickListener {
override fun onNoteClick(id: Int, noteCard: MaterialCardView) {
val intent = Intent(this@NotesActivity, NoteDetailActivity::class.java)
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
this@NotesActivity, noteCard, id.toString()
)
intent.putExtra("noteId", id)
startActivity(intent, options.toBundle())
}
}class NoteDetailActivity : AppCompatActivity() {
private lateinit var binding: NoteDetailActivityBinding
override fun onCreate(savedInstanceState: Bundle?) {
window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
binding = NoteDetailActivityBinding.inflate(layoutInflater)
val noteId = intent.getIntExtra("noteId", 0)
val note = notes.find { it.id == noteId }
binding.note = note
binding.coordinator.transitionName = noteId.toString()
setEnterSharedElementCallback(MaterialContainerTransformSharedElementCallback())
window.sharedElementEnterTransition = buildContainerTransform()
window.sharedElementReturnTransition = buildContainerTransform()
setContentView(binding.root)
super.onCreate(savedInstanceState)
}
private fun buildContainerTransform() =
MaterialContainerTransform().apply {
addTarget(binding.coordinator)
duration = 300
interpolator = FastOutSlowInInterpolator()
fadeMode = MaterialContainerTransform.FADE_MODE_IN
}
}
val adapter = AlbumsAdapter(albums, requireContext())
adapter.albumClickListener = object : AlbumsAdapter.AlbumClickListener {
override fun onAlbumClick(id: Int, cardView: MaterialCardView) {
val extras = FragmentNavigatorExtras(
cardView to id.toString()
)
val action = AlbumsFragmentDirections.navToAlbumFragment(id)
findNavController().navigate(action, extras)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedElementEnterTransition = buildContainerTransform()
sharedElementReturnTransition = buildContainerTransform()
}
private fun buildContainerTransform() =
MaterialContainerTransform().apply {
drawingViewId = R.id.nav_host_fragment
interpolator = FastOutSlowInInterpolator()
containerColor = Color.WHITE
fadeMode = MaterialContainerTransform.FADE_MODE_OUT
duration = 300
}
این الگو برای گذار بین عناصر UI که روابط فضایی یا ناوبری خاصی دارند مورد استفاده قرار میگیرد. این گذار از تبدیلهای روی محورهای x، y یا z برای تأکید مجدد روی رابطه بین عناصر بهره میگیرد. به این ترتیب عناصر با جابجایی در جهت یکسان، با همدیگر مرتبط تصور میشوند.
val forward = true
val fragment = MyFragment.newInstance()
// axis: MaterialSharedAxis.X, Y or Z
// forward: true if it move in the forward direction or false if it move in the backward direction
fragment.enterTransition = MaterialSharedAxis(MaterialSharedAxis.Y, forward)
supportFragmentManager
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit()

در تصویر فوق مثالی از استفاده از گذار MaterialSharedAxis روی محور X برای یک ناوبری در اپلیکیشن را میبینید.

در تصویر فوق یک stepper عمودی با استفاده از گذار MaterialSharedAxis روی محور Y میبینید.
گذارهای محور Z شامل جابجایی یک سطح به سمت بالا یا پایین در سلسله مراتب اپلیکیشن است.

این الگو برای گذار بین عناصر UI که رابطهای قوی با همدیگر دارند مورد استفاده قرار میگیرد. گذار Fade through بهترین گزینه در یک نمای ناوبری تحتانی محسوب میشود، زیرا متدها غالب در وظایف عمدهای گروهبندی شدهاند که ممکن است با همدیگر مرتبط نباشند.
class ArtistsFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialFadeThrough()
}
...
}

این الگو برای آن عناصر UI استفاده میشود که درون مرزهای صفحه وارد یا از آن خارج میشوند. از جمله این موارد شامل یک دیالوگ است که از سمت مرکز در یک نما فیداین یا فیدآوت میشود.
// import androidx.transition.TransitionManager
binding.floatingActionButton.post {
val transition = MaterialFade().apply {
duration = 1500
}
TransitionManager.beginDelayedTransition(binding.root, transition)
binding.floatingActionButton.visibility = View.VISIBLE
}

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