Animate all the things. Transitions in Android
Hey, Android Developer. I would like to tell you something new about animations. Google finally made a statement with Material Design: animations are not only for iOS. Part of the new concept is Material motion.
“Motion provides meaning. Objects are presented to the user without breaking the continuity of experience even as they transform and reorganize. Motion in the world of material design is used to describe spatial relationships, functionality, and intention with beauty and fluidity.”
- Material Design guidelines.
In fact, the process of creating animations takes time. It’s sure tempting to just call setVisibility(View.VISIBLE) and move on to business logic of your awesome new feature, given that it’s already past all deadlines. But remember: every time you ignore opportunity to add meaningful UI transition another sad designer appears somewhere in the world.
What if I told you that animations require less effort than you think? Have you ever heard about Transitions API? Yes, it is that thing promoted by Google for fancy animations between Activities. Sadly, it is available only starting from Android 5.0. So no one is actually using it. But imagine that this API can be efficiently used in many different cases and, what’s even more exciting, available on older Android versions.
Let’s start with some history
New animateLayoutChange parameter was introduced for ViewGroup in Android 4.0. But even with calling getLayoutTransition() and configuring some stuff inside it, it was still unstable and not flexible enough. So you couldn’t do much with it.
Version 4.4 Kitkat brings us idea of Scenes and Transitions. Scene is technically the state of all views in our Scene root (layout container). Transition is the set of Animators which will be applied for the view to perform smooth transition from the one scene to another. Will it be more fascinating with the examples? Yeah.
Imagine we have a button
After a click we want to have a text appeared below the button. Here is our layout:
And in Java we have a click listener:
Not bad. Animations with just one line of code. What’s interesting here is that not only the text visibility was animated, but also a button position. The Transition framework automatically animates layout change caused by appearance of TextView, so you don’t have to do it by yourself. You can even start new animation while old one is still running. The Transition framework will stop animation in progress and then continue to animate views from their current position. All under the hood, automagically.
We can specify the exact Transition types that will be applied via the second parameter of beginDelayedTransition method.
Simple types of Transition
- ChangeBounds. It animates changes to view position and size. This one moves the button in our example.
- Fade. It extends Visibility class and performs most popular animations — fade in and fade out. In example it’s applied to TextView.
- TransitionSet. It’s Transition that is actually a set of another Transitions. They can be started together or sequential, to change it call setOrdering.
- AutoTransition. It’s TransitionSet that contains Fade out, ChangeBounds and Fade in in sequential order. At first, views that don’t exist in the second scene are faded out, then change bounds applied for changes of position and size, and, finally, new views appear with fade in. AutoTransition used by default when you don’t specify any transition in the second argument of beginDelayedTransition.
Everyone wants to write only one implementation that will have consistent behaviour on every Android version. Hopefully, we can achieve that while still using Transitions API. Some time ago I had found two quite similar backport libraries on githab, but they are no longer maintained and both of them missed some things that still could be backported. So, I’ve created my own library based on both of them and added a lot of new stuff for compatibility with older Android versions. All API changes from Lollipop and Marshmallow have been merged too.
So, there we have it: Transitions Everywhere is a backport of Transitions API for Android 4.0 and above. To start using it just specify gradle dependency:
And for all related classes replace import from android.transition.* to com.transitionseverywhere.*
Google has released Support Library for Transitions framework, but it still contains some bugs fixed in my library. I wrote an article to compare them. Please check it out.
What else we can do
First of all, we can change duration, interpolator and start delay for animators inside Transition:
Let’s have a look at other Transition types available.
Like Fade transition, extends Visibility class. It helps new view in scene to slide in from one of the sides. Example with Slide(Gravity.RIGHT):
Explode and Propagation
Explode is a lot like the Slide, but the view will slide in some calculated direction depending on a Transition epicenter (you should provide it with setEpicenterCallback method).
TransitionPropagation calculates start delays for each animators. For example, Explode use CircularPropagation by default. Delay for animation depends on distance between view and epicenter. To apply it, call setPropagation in Transition.
Let’s say, we have RecyclerView with GridLayoutManager and we want to remove all elements after a tap on any specific element. Like this:
ChangeImageTransform. Will animate changes of image matrix. It’s useful for situations when we change scaleType of ImageView. In most cases you’ll want to use it in pair with ChangeBounds to animate position, size and scaleType changes.
Path (Curved) motion
“Real-world forces, like gravity, inspire an element’s movement along an arc rather than in a straight line.”
- Material Design guidelines.
For every Transition that operates with two dimension coordinates (for example change of view position with ChangeBounds) we can apply curved motion with setPathMotion method.
Let say we need to remove all views from container and add the new set of views. And some of the new elements are actually the same as they were before recreating. How we can help framework to understand which items were removed and which elements were moved to a new position? Easy. Just call static method TransitionManager.setTransitionName(View v, String transitionName) to provide any unique name depending on your data model for every view.
For example if we want to create a list of the titles, and shuffle it with recreating views on every click of the button.
This one is not actually a part of Transitions API and was added by me. It allows us to animate visibility change with scale animation. Simple example with new Scale():
Also can be used in combination with other transitions, for example, Fade. We can put in constructor custom final scale:
Try to figure out what this one is for. Exactly! Animated change of background and/or text color.
No time to explain, example:
Helps to add simple fade animation for the text changes.
Transitions are easy to configure. You can specify target views for any Transition. And only them will be animated.
Methods to add target:
- addTarget(View target) — view itself
- addTarget(int targetViewId) — id of view
- addTarget(String targetName) — do you remember about method TransitionManager.setTransitionName?
- addTarget(Class targetType) — for example android.widget.TextView.class
To remove target:
- removeTarget(View target)
- removeTarget(int targetId)
- removeTarget(String targetName)
- removeTarget(Class target)
To exclude some views:
- excludeTarget(View target, boolean exclude)
- excludeTarget(int targetId, boolean exclude)
- excludeTarget(Class type, boolean exclude)
- excludeTarget(Class type, boolean exclude)
And for excluding all children of some ViewGroup:
- excludeChildren(View target, boolean exclude)
- excludeChildren(int targetId, boolean exclude)
- excludeChildren(Class type, boolean exclude)
Create Transition with xml
Transition can be inflated from xml. Xml should be placed in res/anim folder. Example:
Activity and Fragment transitions
Activity Transitions can’t be backported, sorry (sadpanda). A lot of logic is hidden in the Activity. The same applies to Fragment transitions. We should create our own Fragment to change transitions logic.
Transitions can be used for every purpose, for every View. Let’s try to create something unique - our own Transition.
All that we should do is to implement three methods: captureStartValues, captureEndValues and createAnimator. First two are for capturing state of view before and after scene change.
We will create transition for smooth progress change for horizontal ProgressBar:
And how to use our brand new Transition?
All samples from article are available as sample project in library repository.
Never stop moving (and transitioning).