Animation / Material Design


One of the fundamental principles of Material Design is “motion provides meaning” and one important area where this applies is giving visual feedback to the user when they interact with our app. The standard method of doing this has always been using StateListDrawable to tie the visual appearance of a control to its state (i.e. enabled, disabled, pressed, etc.) and this has been available to use since API 1. While useful, this does not meet the “motion provides meaning” principle because the control appearance jumps between fixed appearances. StateListAnimator was introduced in API 21 along with Material Design and is a really simple way to smoothly transition between visual states. In this series we’ll take a look at how to use StateListAnimator to its full potential.

Before we dive in, let’s quickly recap on StateListDrawable as there is some commonality which will become clear as we proceed. Let’s start with a simple layout:

We have a standard style applied to the TextView which sets a background drawable, some padding, a small elevation, and make it clickable so that the TextView has a pressed state. I won’t display the style here, but it’s in the source code if you want to check it out.

The key thing that we do from the layout is apply a foreground drawable which is a StateListDrawable:

This is where the magic happens – we’ve defined a default state which has a transparent fill, and a pressed state which will apply a colour – in this case a slightly transparent green. If we run this we can see standard behaviour:


It’s functional in that it clearly provides some visual feedback that the TextView has been tapped, but it’s not very inspiring and certainly doesn’t have any motion to it.

So how can we improve upon it? Well the Material Design specs suggest that buttons should be behave in a material-like manner and rise to meet your finger, so we could achieve this by altering the elevation, but doing this alone won’t give us motion – it would just jump again. Also, StateListDrawable is not a View, and so we cannot change the elevation of the View from there. This is where StateListAnimator comes in. It allows us to apply changes as the View state changes, but enables us to run property animators on the View:

This is structured similarly to a StateListDrawable in that it has a selector containing items for each state, but it is the content of each item which is different – we now use objectAnimator instead of a shape as we did previously. These are standard objectAnimators and allow us to animate properties of the View – in this case we’re going to animate translationZ which will alter the elevation. One important thing is that we don’t specify a valueFrom attribute on either animator. As a result the starting value will be the current translationZ value for the View.

Next we need to add a second TextView, and apply this:

The important thing here is that we don’t apply this as the foreground, there is a specific attribute named stateListAnimator which we need to use to apply this. If we now run this we can see the TextView seeming to rise to meet our touch:


One thing worth adding is we can apply more than one animation but wrapping them inside a set. To demonstrate this, suppose we wanted to further suggest the rising of the TextView b making it slightly larger as it rises. We can achieve this by adding objectAnimators to animate the scaleX and scaleY properties of the TextView:

This subtly enhances the rising effect:


The use of property animators here should give a hint as to how flexible and powerful StateListAnimator is, and many more really cool effects can be created using this really useful technique.

The source code for this article is available here.

© 2016, Mark Allison. All rights reserved.

CC BY-NC-SA 4.0 StateListAnimator by Styling Android is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Permissions beyond the scope of this license may be available at

Leave a Reply

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