In this article, we will analyze the interesting points of the animation Fragment API.
You can read the previous parts of the article at the link below.
We can define simple animations for transitions between fragments in the res/anim folder. But if we want to control any attributes of our fragment's view, we must specify animations in the res/animator folder. Moreover, we can easily combine them within a transaction.
fragmentManager.commit {
setReorderingAllowed(true)
// Must be specified before add/replace otherwise they will be ignored
setCustomAnimations(
R.animator.anim_enter, // InnerFragment appears on the screen
R.anim.anim_exit, // OuterFragment goes off screen
R.anim.anim_pop_enter, // OuterFragment returns to screen
R.animator.anim_pop_exit // InnerFragment goes off screen
)
replace<InnerFragment>(R.id.container)
addToBackStack(null)
}
These animations are automatically applied to all subsequent transactions using this fragmentManager
.
<!-- animator/anim_enter.xml -->
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="400"
android:valueFrom="0"
android:valueTo="180"
android:propertyName="rotation" />
<!-- anim/anim_exit.xml -->
<alpha
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="400"
android:interpolator="@android:anim/decelerate_interpolator"
android:fromAlpha="1"
android:toAlpha="0" />
<!-- anim/anim_pop_enter.xml -->
<alpha
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="400"
android:interpolator="@android:anim/decelerate_interpolator"
android:fromAlpha="0"
android:toAlpha="1" />
<!-- animator/anim_pop_exit.xml -->
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="400"
android:valueFrom="180"
android:valueTo="360"
android:propertyName="rotation" />
If you do not want to prescribe each animation, you can use Transition - for example, prepared Fade (). It is specified in InnerFragment
and overrides the animation in the transaction if one was specified.
// InnerFragment.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Animation when moving to the screen
enterTransition = Fade()
// Animation when leaving the screen via fragmentManager.popBackStack()
// If not specified, will be used
enterTransition exitTransition = Fade()
// Animation when exiting the screen is not via fragmentManager.popBackStack()
// For example, via replace()
// If not specified, will be used
enterTransition returnTransition = Fade()
// Animation when returning to the screen via
fragmentManager.popBackStack()
// If not specified, will be used
enterTransition reenterTransition = Fade()
}
These are not yet the top animation features in the Fragment API. The next stage of development is the general transitions of elements, with the help of which you can get the implementation of the transition.
To create such an animation, we will use the FragmentTransaction.addSharedElement(View, String)
method and the standard transition ChangeBounds().
First, we need to make the view elements we want to animate unique within the transitionName
markup. This can be done through xml or in code. In InnerFragment
we specify animations:
<!--- fragment_outer_layout.xml -->
<TextView
android:id="@+id/textViewStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Outer Fragment"
android:transitionName="text_start" />
<!--- fragment_inner_layout.xml -->
<TextView
android:id="@+id/textViewDestination"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Inner Fragment"
android:transitionName="text_destination" />
// OuterFragment.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
ViewCompat.setTransitionName(imageViewStart, "image_start")
}
// InnerFragment.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Animation when opening a fragment
sharedElementEnterTransition = ChangeBounds()
// Animations when closing a fragment
// If not specified, sharedElementEnterTransition will be used
sharedElementReturnTransition = ChangeBounds()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
ViewCompat.setTransitionName(imageViewDestination, "image_destination")
}
Вызываем транзакцию:
// OuterFragment.kt
parentFragmentManager.commit {
setReorderingAllowed(true)
addSharedElement(imageViewStart, "image_destination")
addSharedElement(textViewStart, "text_destination")
replace<InnerFragment>(R.id.container)
addToBackStack(null)
}
If all views are rendered synchronously, we can get the animation shown above just like that.
If we use a shared element transition with RecyclerView, then we need to remember that it draws its items after the screen layout is drawn. It turns out that the transition animation needs to be suspended until the list of items is ready to be drawn.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// Pausing the transition
postponeEnterTransition()
// Waiting for everything to load
viewModel.data.observe(viewLifecycleOwner) {
// Passing data to RecyclerView adapter
adapter.setData(it)
// We are waiting for all the elements to be ready for drawing, and start
// the animation
(view.parent as? ViewGroup)?.doOnPreDraw {
startPostponedEnterTransition()
}
}
}
The postponeEnterTransition() method requires FragmentTransaction.setReorderingAllowed(true) to be used.
I will also attach a link to the official documentation from Google on Fragment animation here.