Fragment Transitions with Shared Elements using Android Navigation
Anyone that isn’t using the newest Android Studio tools should definitely change that, especially if that means they aren’t using Android Jetpack’s Navigation component. This component makes maneuvering through the screens of your app a breeze and its chock full of features that can be used to make the navigation itself appear elegant. In this article I’ll be explaining how you can use the navigation component to create transitions with shared elements.
Shared Elements
What are shared elements? In the singular sense, a shared element is a pair of views that are present in two different fragments or activities. These views display the same information, such as a string or an image, but they may have different sizes or locations on screen. To give the the user the illusion that the view is being preserved during navigation, a transition is used to move and reshape the first view so that it “becomes” the second view. Since shared elements actually consist of two different views, they are technically not shared between destinations.
Setup
We’ll be creating an app that performs a simple transition from one fragment to another with multiple shared elements. In Android Studio, create a new project using the “Bottom Navigation Activity” project template.
File -> New -> New Project -> Bottom Navigation Activity
This template gets us up and running pretty quick since there is already a navigation action between HomeFragment and HomeSecondFragment.
In addition to the basic project setup, you should also verify the following items are checked off:
- Increase the minSdkVersion in build.grade(Module:app) to 21
- Implement navigation component in build.gradle (Module:app):
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.2.1
- Enable window content transitions in style.xml
<item name="android:windowActivityTransitions">true</item>
- Create a new transition folder in app/res.
Right-click on the res folder -> New -> Android Resource Director
resource type = transition
- Create a new transition resource in the transition folder (change_bounds.xml):
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeBounds/>
</transitionSet>
ChangeBounds Transition — Single View
Now that everything’s set up, let’s work on executing a simple shared element transition with one TextView. Add one TextView to HomeFragment and one TextView to HomeSecondFragment. Move them to different locations on screen, constrain them each appropriately, and make them say the same thing (ex. “Example”).
If we navigate between these two fragments as is, there is no way to know that the first TextView is associated with the second TextView. For this reason, we need to add the android:transitionName property to both TextViews. All shared elements require unique transitionNames. Note that the transitionName property will not be available if windowActivityTransitions are not enable in your theme.
Now, in the onClickListener in HomeFragment, create a new val extras that maps the view in the starting fragment to the view in the destination fragment (textView to “exampleText”). Then, pass the extras argument to the navigate method of the navController.
The last step is to handle the incoming shared element in the destination fragment. To do this, we set sharedElementEnterTransition to the change_bounds.xml transition we created earlier. Add the same thing for sharedElementReturnTransition so that the transition works in the reverse direction.
With that, BAM! Transition works like a charm. You can move the TextViews anywhere on screen and it will still work fine.
Although not totally related to this tutorial, you need to add this line to MainActivity.kt to make the back button in the upper left corner work properly:
override fun onSupportNavigateUp() = findNavController(R.id.nav_host_fragment).navigateUp()
ChangeBounds Adjustments
You can also apply a ChangeBounds transition using the following code:
sharedElementEnterTransition = ChangeBounds().apply {
duration = 750
}
Using this method, you can adjust specific aspects of the transition.
- Duration (duration = ## in ms)
- Interpolator
- Propogation (CircularPropogation or SidePropogation)
ChangeBounds Transition-Nested View
A shared element nested inside another view (such as a CardView or a ConstraintLayout) will not transition properly if the parent view is not also treated as a shared element. For example, if our TextView is nested inside a ConstraintLayout in fragment 1 and we do not map the ConstraintLayout to the destination fragment, we get something super choppy like this:
To fix this, the parent View also needs to be transitioned. Using the same ConstraintLayout example, the proper way to make the transition is to map the ConstraintLayout from the first fragment to the root ConstraintLayout in the second fragment like this:
In conclusion, Shared Elements are pretty simple to use but there are a few gotchas that can make creating transitions a troubleshooting marathon. Code carefully!