Having a look at Androids VectorDrawable

 

With Android Lollipop some nifty features were added. Among those VectorDrawable‘s and AnimatedVectorDrawable‘s. Let’s have a look at them.

VectorDrawables lets you declare and use vector images on Android and even run nice animations on them. As usual you would declare vector resources in XML. The notation is the same as for Scalable Vector Graphics (SVG). Although as of now, only a subset of SVG is supported.

For demonstration purposes we’ll declare a star shaped vector image, add some rotation, scaling and path morphing animations. We’ll be morphing into a pentagon shape. The shapes are not that hard to construct. You might just need a vector graphics tool that is able to export to SVG. If the shapes are not too complex that is, since not all of SVGs features are supported. I created the star and the pentagon using Inkscape with some manual tweaking. If you are creating by hand using RaphaelJS on jsfiddle is pretty convenient. The shapes I created look like this:

 

<!-- values/vectorpaths.xml -->
<string name="svg_pathdata_star">M 48,54 L 31,42 15,54 21,35 6,23 25,23 32,4 40,23 58,23 42,35 z</string>
<string name="svg_pathdata_pentagon">M 48,54 L 31,54 15,54 10,35 6,23 25,10 32,4 40,10 58,23 54,35 z</string>

vectordrawable-pentagon

vectordrawable-star

You can also check out my public fiddle if you feel like playing around and getting your hands dirty on SVG.

And here is how we declare our vector drawable:

<!-- drawable/star.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="200dp"
    android:width="200dp"
    android:viewportHeight="64"
    android:viewportWidth="64">

    <group
        android:name="star_group"
        android:pivotX="32"
        android:pivotY="32">
        <path
            android:name="star"
            android:pathData="@string/svg_pathdata_star"
            android:fillColor="#ffff00"
            android:strokeWidth="1"
            android:strokeColor="#000000" />
    </group>

</vector>

 

The group is optional, since we have only a single path that describes our star shape. But the group lets us define pivot points – which is used for our rotation. Of course the good thing about vector images in general is that we do not need to care about dimensions and densities as much and you would assume that is the case here as well. But not quite. If you declare it too low, you’ll end up with a pixelated image and if you’ll declare it too high you might run into memory issues, since the framework would internally render and cache bitmap image representations for our vectors. Group- and Pathname are arbitrary and no resource ID (like you would declare with @+id/some_id ), but we’ll be referring to it when declaring the animations on it.

<!-- drawable/star_animated.xml -->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/star">

    <target
        android:animation="@anim/rotate360"
        android:name="star_group" />

    <target
        android:animation="@anim/scaledown50"
        android:name="star_group" />

    <target
        android:animation="@anim/morph_star2pentagon"
        android:name="star" />

</animated-vector>

 

In the animated-vector resource we’ll define the vector drawable to run animations on (here @drawable/star) and the animations to run – in this case the rotate, scale and morph animations that we declare as objectAnimator like this:

<!-- rotate animation -->
<objectAnimator
    android:propertyName="rotation"
    android:duration="@integer/anim_duration"
    android:valueFrom="0.0"
    android:valueTo="360.0" />

<!-- scale animation -->
<objectAnimator
    android:propertyName="scaleX"
    android:duration="@integer/anim_duration"
    android:valueFrom="1.0"
    android:valueTo="0.5" />
<objectAnimator
    android:propertyName="scaleY"
    android:duration="@integer/anim_duration"
    android:valueFrom="1.0"
    android:valueTo="0.5" />

<!-- path morph animation -->
<objectAnimator
    android:duration="@integer/anim_duration"
    android:propertyName="pathData"
    android:valueFrom="@string/svg_pathdata_star"
    android:valueTo="@string/svg_pathdata_pentagon"
    android:valueType="pathType" />

 

Now we just need to trigger the animation whenever we feel like it. Here’s how to trigger our vector animation from onClick:

vectorImage = (ImageView) findViewById(R.id.activity_vector_drawable_image);
vectorImage.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        ((Animatable) vectorImage.getDrawable()).start();
    }
});

 

Notice the cast to Animatable which is implemented by AnimatedVectorDrawable. To have the animations run infinitely we can of course add android:repeatCount="-1" to the objectAnimator in question.

 

The resulting animated vector drawables in action:

 

And here only the path morphing animation:

 

Some closing thoughts

  • Path morphing animations are awesome. I guess the closest you get to this with regular drawables are with cross fade animations, but it’s still not quite the same.
  • You can save quite some space on bitmaps if you have multiple for different screen sizes and resolutions. But that would obviously only work for rather simple shapes, and let’s be honest: Who really cares if the APK is a few megs larger or smaller?
  • Being able to instantiate (Animated)VectorDrawables programmatically at runtime would be great.
  • As will be better tools support and documentation.

Otherwise: nifty little feature.

 

The source code is available on VectorDrawableDemo@Github. And be sure to check out Tenghui Zhu’s great DevByte on Androids Vector Graphics as well:

 

Leave a Reply

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