Animating a strike through on Android with AnimatedVectorDrawable and AnimatedStateListDrawable
We recently added a neat little animation to Firefox Preview:
In Firefox Preview, you can grant a website access to your location and microphone. When you toggle those permissions off, a line appears and grows diagonally through the original icon as it fades to gray. To create the animation, I considered many details such as colors, lines, curves, and clip paths.
In Android, an animation can be broken down into three states: The start, the end, and the in-between. Android automatically creates the in-between parts of the animation, so you just need to define the start and the end.
For color, that’s pretty simple. When the permission is turned on, the icon is purple. When the permission is turned off, the icon is gray. Android handles the transition between the two colors.
You can preview animations with a tool called Shape Shifter.
Unlike colors, which are single values, shapes are more complex. Even a simple line has multiple points to define its edges.
In Firefox, our icons are defined as vectors, meaning that an image file specifies some points and the computer connects the dots. This is different than bitmap images which store many coloured squares. Since the computer is just connecting dots, the image can be scaled up without losing any detail.
Android can also animate these dots. A line (really a thin rectangle) can be represented as 4 dots for each corner. When it disappears, it’s really just moving the dots so that the rectangle is so small you can’t see it.
Here you can see how the animation looks, with the start of the animation on the left and the end on the right.
This animation for square lines is great, but it doesn’t work for Firefox. The lines we use for Firefox’s icons are curved at the end, rather than flat. When we try to make this line animate, the ends shrink and grow oddly. Look closely at the upper right corner of the animation and how the line width adjusts.
That’s not ideal. Luckily there’s another way to hide the line when it shrinks.
You can hide parts of an image by putting it behind an “ clip path “, a special path that hides everything outside of it. It’s akin to a cookie cutter that extracts a shape from the rest of the cookie dough. Outside the clip path, the line becomes invisible.
By using a clip path, we can hide the end of the line without changing the relative positioning of its dots.
If we put our animated line on top of another icon, this is what we end up with.
The line unfortunately blends in with the icon. To emphasize the strike through, we want to create a small gap right under the line and create something like this:
Luckily, we already know how to hide parts of an icon. We can use a clip path again. Since clip paths are just another shape created by connecting dots, they can also be animated in parallel to the original line.
Here’s the final product. Using a combination of color transitions, shape transitions, and clip paths we’ve achieved a clean line effect. Looks great!
Using the animation on Android
Android has tools to create an animation like this with just XML, without Java or Kotlin. You need to create a few different files to represent different states and how to transition between them. The finished files are available to download from GitHub too.
You can't perform that action at this time. You signed in with another tab or window. You signed out in another tab or…
First of all we start with a Vector Drawable, which contains the icons and the paths we designed before. This goes inside the Android project’s
We also create a second Vector Drawable to represent the icon with a line through it.
Unfortunately there’s no simple way to play the animation in reverse, for when you want to remove the strike-through. You need to create two different sets of animations for the enable → disable transition and disable → enable transition.
Creating animation resources
Now that we have the base icon resource, we create a property animator for each path that we want to animate. These files go inside the
res/animator resources folder. We animate three paths: The color of the main icon (
android:name="icon"), the shape of the clip path (
android:name="strike_thru_gap"), and the color and shape of the line (
android:name="strike_thru_path"). We also have to create another 3 property animators for the reverse direction.
Changing the color of the icon
We create two ObjectAnimators representing the color property of an object. These two animator files are nearly identical, but the
android:valueTo fields have been flipped. These fields indicate the starting and ending values (in this case, the two colors).
android:propertyName specifies the attribute that we're animating, and
android:valueType specifies the type of the values in
android:duration specifies the length of the animation, in milliseconds. The value of
"500" corresponds to 500 milliseconds or half a second.
Changing the shape of the clip path
To change the shape of the clip path, create another ObjectAnimator like before. Instead of animating between two colors, we now animate between two paths.
valueType is now
"pathType" as the values represent shape paths rather than colors. Again, you can create the
strike_thru_gap_disabled_to_enabled.xml file by copying
strike_thru_gap_enabled_to_disabled.xml and switching around
valueTo. (Or download the source code.)
Changing two values on the same path at once
Finally we create animators for the color and shape of the line. Since there are two values to animate, we use a AnimatorSet. The set is just a simple container for other animation elements.
The two individual object animators are very similar to the previous shape and color object animators we wrote. However, they are still distinct animators since the path values are different. Here, they run in tandem on the line.
Again, the corresponding animator for the reverse direction is left as an exercise for you. You can also just download the source code.
Using Animated Vector Drawables
At this point we’ve created the icon, and created animators for each part of the icon we want to animate. Now it’s time to start putting them together.
We create two AnimatedVectorDrawables to represent the enable → disable animation and disable → enable animation.
<animated-vector> tag has an
android:drawable attribute where we specify the drawable that we want to animate. The two VectorDrawables we created above are referenced by name here.
Inside the tag, we create
<target> tags for each path we want to animate. The
android:name field indicates the name of the path to animate, as specified in the VectorDrawables. The
android:animator tag references one of the property animators we created. Each animator is matched to its corresponding animation and target.
Now we have two assets that contain all our animation code. Now, we create one final asset that references these two.
Android has a built in concept of “states” for its views, such as
pressed when a user presses on a view or
checked when a checkbox is selected. The
enabled state is a nice general-purpose state that we'll use to represent whether or not the icon should have the line drawn through it.
The StateListDrawable is a special drawable type that changes the drawable displayed within depending on the state of its container. We can reference our static VectorDrawables again for the enabled and disabled states.
android:state_enabled attribute specifies which drawable corresponds to the enabled state. The item with no modifier will automatically be used for the disabled state.
While this will change the displayed icon, there’s no animation when switching between the two. For that, we need to use an AnimatedStateListDrawable. This drawable enhances the StateListDrawable by allowing you to specify transitions between states.
We change the XML tag from
<animated-selector> and add two
<transition> elements. The transitions specify their starting state and ending state with the
android:toId attributes, which correspond to the values in the
<item android:id="..."> elements.
Actually displaying the icon
Finally we have a single super special drawable asset to use in our app. It can be used just like any other drawable, including in an ImageView.
From your code, just change the
enabled value and you'll see the icon automatically transition!
This article has broken down how to implement animation in Android step-by-step. The same principles apply to not only strike-throughs, but other animations as well. In addition to colors and paths, you can animate position, rotation, transparency, and more. I can’t wait to see what you all build!
Originally published at https://tigeroakes.com on August 12, 2019.