Animation / Drawable / VectorDrawable

AnimatedVectorDrawable Bundles

Those that know me and regular readers of Styling Android will be already know that I have a particular passion for moving stuff around, and animating all the things! Quite predictably I’m a big fan of AnimatedVectorDrawable and there was a small addition to this most useful of components that was announced an Google IO 2016 but bypassed many of us, myself included. That is until Nick Butcher posted about it. The feature itself is fairly minor, but helps to keep our code self-contained and therefore easier to maintain – it is a different way of packaging AnimatedVectorDrawable at source level. In this article we’ll take a look at AnimatedVectorDrawable Bundles.

Those familiar with AnimatedVectorDrawable will be aware that an AnimatedVectorDrawable consists of a minimum of three separate files (but it can be many more): The first containing a <vector> definition which is the VectorDrawable which is going to be animated; then there are one or more <objectAnimator> definitions which are the animations that we wish to apply; the final file is the <animated-vector> definition which ties the animators to the components of the VectorDrawable. For a full description please check out VectorDrawables – Part 2. Having all of these files dotted around can make it difficult to understand what an AnimatedVectorDrawable is doing – and this is particularly true when it comes to code reviewing AnimatedVectorDrawable implementations.

AnimatedVectorDrawable Bundles are simply a mechanism for combining these components in to a single file. The syntax is very similar to what we’re familiar with, so there’s nothing much more to learn. However there are a couple of things worth considering to ensure that we make the best use of this new feature.

Before we dive in to the code, let’s first discuss how these Bundles are actually implemented. Our bundles actually get broken in to their components by AAPT during the build – so what actually goes in to the APK is actually identical (with some caveats, which we’ll cover later). All that the bundle format allows us is just code organisation. In order to use bundles you must use Build Tools 24.0.0 or later.

So let’s start with a simple AnimatedVectorDrawable which animates between a plus and minus symbol:

animated-vector

This consists of a VectorDrawable:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
  android:viewportHeight="24"
  android:viewportWidth="24"
  android:width="240dp"
  android:height="240dp">

  <group
    android:name="plus_group"
    android:pivotX="12"
    android:pivotY="12">
    <path
      android:name="plus_path"
      android:strokeColor="?attr/colorPrimary"
      android:strokeWidth="3"
      android:pathData="M12,0L12,24M0,12,L24,12" />
  </group>
</vector>

Two Animators, the first performs the pathData animation:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
  android:duration="1000"
  android:propertyName="pathData"
  android:repeatCount="infinite"
  android:repeatMode="reverse"
  android:valueFrom="M12,0L12,24M0,12,L24,12"
  android:valueTo="M12,0L12,24M12,12,L12,12"
  android:valueType="pathType" />

And another Animator to perform the rotation:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
  android:duration="1000"
  android:propertyName="rotation"
  android:repeatCount="infinite"
  android:repeatMode="reverse"
  android:valueFrom="0"
  android:valueTo="90"
  android:valueType="floatType" />

These are all tied together by the AnimatedVectorDrawable:

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
  android:drawable="@drawable/plus">

  <target
    android:animation="@animator/rotate_clockwise"
    android:name="plus_group" />

  <target
    android:animation="@animator/plus_to_minus"
    android:name="plus_path" />

</animated-vector>

The Bundle format allows us to combine all of these in to a single file:

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:aapt="http://schemas.android.com/aapt"
  xmlns:tools="http://schemas.android.com/tools"
  tools:ignore="MissingPrefix">

  <aapt:attr name="android:drawable">

    <vector
      android:viewportHeight="24"
      android:viewportWidth="24"
      android:width="240dp"
      android:height="240dp">

      <group
        android:name="plus_group"
        android:pivotX="12"
        android:pivotY="12">

        <path
          android:name="plus_path"
          android:strokeColor="?attr/colorPrimary"
          android:strokeWidth="3"
          android:pathData="M12,0L12,24M0,12,L24,12" />

      </group>

    </vector>

  </aapt:attr>

  <target
    android:name="plus_group" >

    <aapt:attr name="android:animation">

      <objectAnimator
        android:duration="1000"
        android:propertyName="rotation"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        android:valueFrom="0"
        android:valueTo="90"
        android:valueType="floatType" />

    </aapt:attr>

  </target>

  <target
    android:name="plus_path" >


    <aapt:attr name="android:animation">

      <objectAnimator
        android:duration="1000"
        android:propertyName="pathData"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        android:valueFrom="M12,0L12,24M0,12,L24,12"
        android:valueTo="M12,0L12,24M12,12,L12,12"
        android:valueType="pathType" />

    </aapt:attr>

  </target>

</animated-vector>

This follows the structure of the XML format, but we just embed the other files in to this by using a declarative attribute. In the traditional format we need to include a `android:drawable` attribute on the element; but in the Bundle format we can declare this attribute inline using <aapt:attr name="android:drawable"> as we have in line 7. Similarly we need to declare the two android:animation attributes inline as well – lines 35 & 54.

One thing worth noting is that we need to use the aapt namespace for attribute inlining. The other thing that you’ll find if you try this is that Android Studio will show lots of errors on this because it does not yet recognise the bundle format. Also lint will throw errors for the same reason so I needed to add the tools:ignore="MissingPrefix" suppression at line 5.

So that’s simple enough, but what about the caveats I mentioned earlier? Currently there does not to be any duplication detection in the AAPT pass – so if we use the same animation multiple times then each inline occurrence will be stripped out to a separate file – therefore we’ll bloat our APK with lots of duplicate animator files if we inline everything. It is worth bearing this in mind and be diligent when using bundles to avoid lots of duplication.

Once trick that we can use is we can mix and match the traditional structure and bundle structure even within the same file. For example the following is perfectly acceptable:

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:aapt="http://schemas.android.com/aapt"
  xmlns:tools="http://schemas.android.com/tools"
  tools:ignore="MissingPrefix">

  <aapt:attr name="android:drawable">

    <vector
      android:viewportHeight="24"
      android:viewportWidth="24"
      android:width="240dp"
      android:height="240dp">

      <group
        android:name="plus_group"
        android:pivotX="12"
        android:pivotY="12">

        <path
          android:name="plus_path"
          android:strokeColor="?attr/colorPrimary"
          android:strokeWidth="3"
          android:pathData="M12,0L12,24M0,12,L24,12" />

      </group>

    </vector>

  </aapt:attr>

  <target
    android:animation="@animator/rotate_clockwise"
    android:name="plus_group" />

  <target
    android:name="plus_path" >


    <aapt:attr name="android:animation">

      <objectAnimator
        android:duration="1000"
        android:propertyName="pathData"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        android:valueFrom="M12,0L12,24M0,12,L24,12"
        android:valueTo="M12,0L12,24M12,12,L12,12"
        android:valueType="pathType" />

    </aapt:attr>

  </target>

</animated-vector>

We’re using the bundle structure for the drawable and one of the animator attributes (the path animator which is specific to the drawable), and the traditional structure for the other animator (a much more generic rotation animator). By doing this we can share common components where necessary, and inline the one-off components.

That’s really all there is to it – Bundles are pretty useful, but it’s worth being mindful the potential APK bloat issue from duplicated resources.

The source code for this article is available here.

© 2016, Mark Allison. All rights reserved.

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

1 Comment

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.