AnimatedIcons / AnimatedStateListDrawable / Animation / VectorDrawable

AnimatedIcons: Strikethru

There is a nice micro-animation library at useanimations.com which contains some useful animations which are particularly well suited for animated icons. These are all free to use and downloadable at Lottie animations. For those that already use Lottie they can use these animations as-is. However using them is apps which don’t use Lottie, or in cases where they may need tweaking and you don’t have a designer available that can perform the necessary tweaks in After Effects, it may not be possible to do this. In this occasional series we’ll look at how to create some of these animations as Animated Vector Drawables which will show some useful AVD techniques.

The general technique that we’re going to cover here is for an extremely common use-case, when we have some kind of behaviour toggle and we want to indicate the state change. I have chosen an eye icon to indicate a visible / invisible state as can be seen in the animation on the left. The technique that we’ll look at is incredibly flexible and can be applied to many different base icons, and gives us a pretty standard strikethru behaviour that we can re-use.

I actually decided upon the eye icon because it is quite a complex shape and is a great example of how the technique is reusable because it is actually independent of the base icon path data. It is actually based upon one of the material icons that we can import directly in to Android Studio using Vector Studio (in Android Studio right click on your res/drawable folder and select New...|Vector Asset. By selecting Clip Art in the Asset Type field, we now click on the Clip Art icon and can select from a nice selection of icons. I have used the one named ic_remove_red_eye_black_24dp and changed the colour to white:

We don’t need to concern ourselves with the pathData here because we get it for free thanks to Vector Studio! We have this as our basis:

Now we need to create the off or invisible state which has the strikethru. The easier bit is the visible green line in the animation above which is a quadrilateral shape:

I’m not going to give an explanation of the pathData here, it’s a move three, lines and a close path (draw a line back to the start point) which draws a diagonal quadrilateral starting at the top left down to the bottom right. For those that don’t understand this, then I have blogged about this before, or take a look at the moveto, lineto, and closepath commands here.

We now get this:

However this lacks key element that is common in this visual pattern – an area of the base image that is hidden next to the strikethru. If this doesn’t make sense, then take another look at the animation at the start of this article and you’ll see that when the along with the strike, there is also an area of the eye that gets hidden next to the green strike line as it is drawn.

While it might appear that we could achieve this by drawing a white strikethru line along with the green one, this isn’t practical for a couple of reasons. Firstly, we’re actually declaring the green line as white in the vector and then using a tint in the layout to apply the green colour. If we try and draw a white line over the eye icon, this will also get tinted so we’ll end up with a single green line, but double the width. The second issue is that the icon background is transparent. If we draw an opaque line then is will be visible if it doesn’t match the background colour upon which the icon is drawn.

What we need to do is prevent the eye path from being drawn in this area, and we can do this using a clip-path. A clip-path takes a pathData attribute, like path, but instead of using this to draw something, it declares an area which controls where subsequent paths will be visible. Think of cutting a hole in a piece of paper, then overlaying this on another whole piece of paper. If you then cover this with paint, and lift off the top piece (with the hole), then you will get a coloured shape on the bottom piece corresponding to the shape of the hole. A clip-path works in much the same way: we define a path in the clip-path and this affects subsequent path elements. When the path elements are rendered only the pixels within the path that fall inside the clip-path will actually be rendered. The shape of the hole in our top piece of paper is the clip-path.

The problem we now face is that a clip-path allows us to define a shape within which subsequent path elements will be drawn, but if we use a similar path to the one we used for the green strike line then it will only allow the part of the eye which falls within it to be drawn which is actually the opposite to what we need. We can fix this by using a compound path which is a close path inside another – think of a doughnut shape with one circle inside another and the inner circle is a hole within the outer one. We can do the same in our clip-path by creating an outer rectangle matching the bounds of the canvas, and then include our strike line inside that, which is positioned slightly up and right of the green strike line:

Again, I won’t go in to detail of the pathData here, but the section before the space declares a rectangle matching the canvas, and the section after the space is a separate path which is the area within which we want to prevent the eye from being drawn. If we were to render the clip-path as an actual path it would look like this:

The green areas are those in which pixels will be drawn, but the white area will be masked so that subsequent path operations will not draw anything in this area.

The order of these three elements is important. The clip-path comes first because it controls the parts of the canvas within which subsequent path elements will be drawn. Next cones the eye itself. Finally we have the visible strike line which is drawn over the eye. We get this as a result:

Before we move on it is worth noting one thing: We have given names to both the strike line and the clip-path but not the path that defines the eye itself. We need to referent the two named elements to be able to animate them, but we are doing nothing to the eye path. This should make it clear that this technique works independently of the eye image, so can be applied in exactly the same way elsewhere. We can even re-use the animators that we’ll come to shortly for other assets.

Now we need to animate the transitions between the two states. The basic principle is that we want to grow both the strike line and the clip-path together from top left to bottom right to strike through the eye icon to denote the transition from visible to invisible states; and collapse them from the top left corner in to the bottom right to denote the transition from invisible to visible states. We do this by animating the pathData. Once again, I have covered this before so I won’t go in to the intricacies again here. Essentially we provide the start and end states of the path, and the AnimatedVectorDrawable will interpolate between these as the animation runs.

For the visible to invisible transition we use this path as the start state M4,4L4,4L3,5L3,5Z, and the fully drawn strike line as the end state: M4,4L20,20L19,21L3,5Z. The start state is positioned at the top left, but has no width because the pairs of vertices marking the top left and bottom right edges have precisely the same positions 4,4 and 3,5. In the end state the lop left pair of vertices remain at these positions whereas the bottom right ones are at 20,20 and 19,21. So when this gets animated the line will expand from the top left position down to the bottom right one. The objectAnimator for this is:

We do much the same for the invisible to visible transition by having M4,4L20,20L19,21L3,5Z as the start path, and M20,20L20,20L19,21L19,21Z as the end path. It is pretty much the same as the previous objectAnimator just with different valueFrom and valueTo attributes.

The clip-path is animated in much the same way, the only difference being that the outer rectangle remains constant, and we only animate the diagonal strike line within it to effective animate the area that is masked out. I won’t go in to details here, but it’s all in the source code.

We now define the animated-vector which links the components within the vector to these animators:

The final piece is to link these animated-vectors to the view state transitions which we do using an AnimatedStateListDrawable:

The two items denote the two checked states of the Checkable, and the transitions link these states to the appropriate animated-vector. One again, we’ve looked at AnimatedStateListDrawable in much more depth before, so I won’t give a full explanation here.

With that we just need to specify the drawable named strikethru.xml for any View which implements Checkable and we get this toggle transition automatically.

Once again it is worth repeating that we didn’t do anything specific to the eye icon here – we can apply to strikethru effect by adding the strike path and strike_mask clip path to any icon, and then reuse the same animators in the two animated-vectors for the two transition animations, and create an AnimatedStateListDrawable to tie it all together.

For the purposes of keeping the code understandable, I have embedded the pathData within the relevant vector and objectAnimators. However in the source I have defined these as string resources, so that these could be re-used for other icons, as well.

The source code for this article is available here.

© 2019, Mark Allison. All rights reserved.

Copyright © 2019 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.