enoent

Compat libraries incompatibilities

Compat libraries are great. They allow us to work with newest Android APIs, without thinking (much) about your minimum API level. Instead of thousands of devices, you can reach billions. With nearly no changes in your code.

But sometimes, they’re not so great…

Support for vector drawables (mainly SVGs files) has been added in API 21. They come in two kinds: still and animated. They’re great for many reasons: they scale properly, you don’t have to keep multiple densities of the same image anymore, you can reference colors and dimensions from resources, you can do path morphing… well, almost.

In order to use vector drawables on pre-21 APIs, Google released a set of two support libraries, com.android.support:vector-drawable and com.android.support:animated-vector-drawable. The first one works starting with API 7, the latest with API 11. Everything is explained here.

Still drawables should work exactly the same regarding the API level you’re running. On the animated version, however, there’s a catch: you can’t animate all the properties you can on 21+. From the documentation:

Note that the animation in AnimatedVectorDrawableCompat has to be valid and functional based on the SDK version the app will be running on. Before SDK version 21, the animation system didn’t support the following features:

  • Path Morphing (PathType evaluator). This is used for morphing one path into another.
  • Path Interpolation. This is used to defined a flexible interpolator (represented as a path) instead of the system defined ones like LinearInterpolator.
  • Animating 2 values in one ObjectAnimator according to one path’s X value and Y value. One usage is moving one object in both X and Y dimensions along an path.

Let’s say you want an animation using path morphing. Place your vector drawable in the drawable-v21 folder and add a fallback with a rotation or whatever in the drawable one, and you’re good to go, right? Right?

The same page of the documentation also mentions this:

For API 24 and above, this class is delegating to the framework’s AnimatedVectorDrawable. For older API version, this class uses ObjectAnimator and AnimatorSet to animate the properties of a VectorDrawableCompat to create an animated drawable.

And here come troubles. The reasoning behind this is that SDK’s implementation on APIs 21 to 23 contains some bugs. However, using an AnimatedVectorDrawableCompat on an API 21 device comes with the same limitations as running on an API 19 device: you’re using the SDK’s ObjectAnimator and AnimatorSet, and they do not know the support libraries symbols. More specifically, they don’t know how to animate any android.support.graphics.drawable.PathParser$PathDataNode (what they do know about is the android.util.PathParser$PathDataNode class).

If you’re looking for more technical bits, I wrote some more notes on this StackOverflow question as I stumbled upon this issue.

As a result, on APIs 21 to 23, any path-morphing animation fails silently when using the support libraries. There’s a work-around, though. You can use the SDK’s implementation to load your vector drawable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    // Casting the drawable to an AnimatedVectorDrawable is useless here
    // It's just to show that you don't get an AnimatedVectorDrawableCompat
    AnimatedVectorDrawable drawable = (AnimatedVectorDrawable) getDrawable(R.drawable.ic_animated_drawable_32dp);
    mBinding.imageView.setImageDrawable(drawable);
} else {
    mBinding.imageView.setImageResource(R.drawable.ic_animated_drawable_32dp);
}

// Starting the animation works whatever the implementation we're using now
final Drawable animation = mBinding.imageView.getDrawable();
if (animation instanceof Animatable) {
    ((Animatable) animation).start();
}

However, using the SDK’s implementation obviously means not making use of the support library’s bug-fixes. It will probably work for simple drawables, but may fail on more complex ones.

As a final note: if you’re using animated vector drawables, remember to test not only on whatever your minimum SDK is and your latest shiny device, but also on APIs 21 to 23. You might be surprised.

Comments