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:
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.
This is a great write up. So much better than the official documentation here: https://developer.android.com/reference/android/graphics/drawable/AnimatedVectorDrawable.html
Thank you for this write up. I’m well on my way to understanding how aapt works with animated vector drawables now with your help.