An animated drawable issue in Lollipop
A common pattern in Android is to use View.setVisibility()
to swap widgets in your hierarchy. Unfortunately, in Lollipop, there is a risk of running afoul of the new RippleDrawable
when doing so.
Let's look at a sample project. It swaps the visibility of two Buttons
when they are clicked. Here it is in action:
That's odd... why is the ripple effect displaying when a Button
appears, rather than disappears? It seems to be queueing the animation, then only playing it when the Button
is visible again. You would expect that when a View
becomes invisible the ripple would disappear as well, but it doesn't.
There are two conditions that need to be satisfied to reproduce this issue:
- You must have a
View
that is going to run an animatedDrawable
(say, on click). - You must change the visibility of a parent
View
before the animation plays.
It's key (in reproducing this bug) that the visibility of the parent is what changes. Android handles the simple case properly - that is, changing the visibility of the Button
itself.
While this sounds esoteric, it's really quite common; anytime you have a button clearing a set of widgets it can happen. For a real-life example, check out Google Keep, which does it when you add/clear a reminder on a note.
Luckily, there is a solution: Drawable.jumpToCurrentState()
. It basically fast-forwards any Drawable
animation to its final destination. I prefer using ViewCompat.jumpDrawablesToCurrentState()
, since it operates on all Drawables
associated with a View
at once and it's backwards compatible.
When you want to reveal a View
which may have an animated Drawable
queued up, just use some code like this:
myButtonContainer.setVisibility(View.VISIBLE);
ViewCompat.jumpDrawablesToCurrentState(myButtonContainer);
And the result will be correct:
If you want to play around with this problem, check out the sample.