The recently released Material Actions system, part of the MDC-Android library (v 1.2.0), combines commonly used transition effects into a set of simple patterns to provide a smoother and easier to understand user experience. Material dynamic effect currently includes four transition effects:
- Container Transform
- Shared Axis
- Fade in and out
- Fade (Fade)
We’ve implemented this transition effect on the Android platform and the AndroidX transition system to make it easy to switch between activities, Fragments, and Views.
This article introduces each of these patterns and explains how to apply them to your application. I’ll illustrate each step by implementing the corresponding effect in the sample application Reply, an easy-to-use mail client. The Reply app uses these transitions for three actions: opening the message, opening the search page, and switching the email.
If you’re not content with just getting started and would rather dive into the source code, see the Material Action Codelab for a step-by-step guide to the technique. Codelab also provides additional information on how to use these transitions on Android.
Container conversion: Open the message
Container transformations are the protagonist of transitions, which are used to convert one element to another. What does that mean? For example, a list of examples expands into a detail page, a FAB morphs into a toolbar, or a Chip expands into a floating card. In each scenario, one component transforms to another and animates the “internal” content, while maintaining a shared “external” container. Using container transformations, animations switch between views to help strengthen the connection between them and maintain a user’s navigation context.
In the Reply example, we added a container transformation between the Fragment showing the mailing list (HomeFragment) and the message detail Fragment (EmailFragment). If you’re familiar with Android shared element transitions, it’s set up very similarly to container transitions.
First, identify the views for the two shared elements and add a transition name for each view. The first is a card for a single mailing list item, and we’ll use data binding to ensure that each list item has a unique transition name.
android:transitionName="@{@string/email_card_transition_name(email.id)}"
The second is the full screen card component inside the EmailFragment, which can set a static transition name because it is the only view in the view hierarchy. Note that two shared elements do not need to use the same transition name.
These two views will be used by our container transformation. Here’s how it works: They’re all placed inside a Drawable, and the boundaries of the Drawable are clipped to the “container,” which animates its shape from a list item to a detail page. During the transition, the contents of the container (list items and detail pages) are exchanged as the incoming page fades in on the outgoing screen.
Now that we have marked the view of the Shared elements, you can create a destination sharedElementEnterTransition fragments, and it is set to a MaterialContainerTransform instance. By default, when returning from details page, the sharedElementEnterTransition automatically reverse and play.
SharedElementEnterTransition = MaterialContainerTransform (). The apply {/ / drawingViewId view id, in its upper part, Container transformations will take the form drawingViewID = R.ID.NAV_Host_Fragment Duration = in z-space Resources.getInteger (R.Integer.reply_motion_duration_large).tolong () // Since we also want to animate the list page into the view, So scrimColor will be TRANSPARENT scrimColor = color.transparent setAllContainerColors(requireContext().themeColor(R.attr.colorSurface)) }
The relevant
MaterialContainerTransform
For more information about parameters, seeDynamic effect document
When an email is clicked, all we need to do is provide a mapping between the start view and end view transition names for the Fragment transaction. With that information, sharing email details Fragment element transition can use our provide MaterialContinaerTransform find and animation to switch between the two views.
override fun onEmailClicked(cardView: View, email: Email) {
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()
}
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)
}
In the code snippet above, we also set up the outgoing page mailing list Fragment
exit
和reenter
The transition effect of. The Material component provides two transition AIDS:Hold 和 MaterialElevationScaleTo animate smoothly the Fragment that will be replaced. Except for Fade, MaterialElevationScale
It also zooms in and out of the mailing list page when it exits and zooms back in when it re-enters the mailing list. HOLD simply holds the mailing list. If we didn’t set the exit transition, our mailing list would be deleted immediately and disappear from the view.
If we run the code from the detail pager back to the mailing list page at this point, the return transition will not take place. This is because when the transition begins, the mailing list adapter has not been populated, and the transition system cannot find the two views that correspond to the transition name. Fortunately, there are two simple methods can be used for us: postponeEnterTransition and startPostponedEnterTransition. These two methods allow us to delay the transition until we know that our shared element has been laid out and can be discovered by the transition system. In the Reply application, we can delay the transition using the following code until we are sure that the RecyclerView adapter is populated and the list item is bound to the transition name:
postponeEnterTransition()
view.doOnPreDraw { startPostponedEnterTransition() }
In your own application, you might want to try both approaches to find the right time to start delaying the transition, depending on how and when you fill the UI. If you find that your return animation is not executed, you may have started the transition before the shared element is ready.
Next, go to our search page.
Shared Axis: Open the search page
The Shared Axis pattern is used for transitions between UI elements that have spatial and navigational relationships. In the Reply application, opening the search page takes the user to a new page at the top of the mailing list. To introduce this 3D model, we can use a shared Z-axis transition between a mailing list (HomeFragment) and a search page (SearchFragment).
Shared axis transitions create the final, choreographed transition effect while operating on both targets. This means that “paired” transitions will work together to create successive directed animations. For Fragments, this pair of transitions includes:
- The FragmentA
exitTransition
And FragmentBenterTransition
- The FragmentA
reenterTransition
And FragmentBreturnTransition
The MaterialShadAxis is a class that implements the SharedAxis pattern and accepts the forward attribute to control the concept of directionality. In each transition pair, the forward must be set to the same value in order to properly coordinate the pair of animations.
For more detailed information about Shared Axis Directionality, seeDynamic effect document.
In the Reply application, this is how we set up the exit and reentry transition for the current Fragment (HomeFragment).
currentNavigationFragment? .apply { exitTransition = MaterialSharedAxis( MaterialSharedAxis.Z, /* forward= */ true ).apply { duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong() } reenterTransition = MaterialSharedAxis( MaterialSharedAxis.Z, /* forward= */ false ).apply { duration =resources.getInteger(R.integer.reply_motion_duration_large).toLong() } }
In our target Fragment (SearchFragment), we create the entry and return transitions.
enterTransition = MaterialSharedAxis(
MaterialSharedAxis.Z,
/* forward= */ true
).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
returnTransition = MaterialSharedAxis(
MaterialSharedAxis.Z,
/* forward= */ false
).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
Note: The exit transition of the current Fragment uses the same forward value of -true as the entry transition of the search Fragment, as does the reentry transition of the current Fragment and the return transition of the search Fragment.
Next, by default, transitions run on all subviews within the scene root hierarchy, which means that a shared axis transition is applied to every message on the mailing list and to every subview of the search page. This is a great feature if you want to “spread” or “stagger” animations, but since we need to animate the root of each Fragment as a whole, We need to set android: TransitionGroup =”true” in the mailing list’s root view group and our search page’s root view group.
This way, we have a nice shared Z-axis transition as we go in and out of the search page! Shared Axis is a very flexible transition that can be applied to many different scenarios, from page transitions to intelligent reply selection to entry or vertical step flows. Now that you have configured the Settings, you can also try using the Axis parameters of the MaterialSharidAxis to see what the other axis animations look like.
Fade in and out: Toggle mailbox
The last pattern we’ll cover is the Fade-In pattern. Fade in and out can be used to transition between UI elements that do not have strong relationships. When transitioning between two mailboxes, we don’t want users to think that the mail they’ve sent is navigatively related to their inbox. Since each mailbox is a top destination, fading in and out is an appropriate choice. In the Reply application, we will replace the email list (HomeFragment) with a different email list (HomeFragment with the new parameter).
Since the MaterialFadeThrough has no direction, it is easier to set up. We only need to set an exit transition for the outgoing Fragment and an entry transition for the incoming Fragment.
currentNavigationFragment? .apply { exitTransition = MaterialFadeThrough().apply { duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong() } }
enterTransition = MaterialFadeThrough().apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}}
The requirement to set android:transitionGroup=”true” on the mailing list also applies here, but we have addressed this in the RecyclerView Shared Axis configuration step.
That’s the fade-in transition! You can use the fade-in mode in interesting areas of your project, such as switching the bottom navigation bar, swapping list items, or replacing a toolbar menu.
Forward!
This article briefly introduces the Material dynamic system of Android. By using the patterns provided by the system, you can do a lot of things when you customize your actions to make them part of the brand experience. In this article we saw the transitions of fragments, but dynamic systems can also be used to transition between activities or even views. Check out the full dynamic specification documentation for more inspiration on where you can improve the core experience of your application, or add extra fun in small areas.
To continue learning, check out these other resources:
- Material Activity Development Documentation: You can find many custom options and suggestions for animating between activities and views in the Material Android Activity Development Documentation.
- Material Action CodeLab: A complete step-by-step developer tutorial on how to add Material Action to the Reply application.
- Android Google cloud disk: You can see the active system running in the Android Google cloud disk application. Clicking on folders, opening search, and switching between bottom navigation all use the MDC-Android transition effect.