Home Design Using vector assets in Android apps

Using vector assets in Android apps

by Androidhunt
Nick Butcher

In previous posts we’ve looked at Android’s VectorDrawable image format and what it can do:

In this post we’ll dive into how to use them in your apps. VectorDrawable was introduced in Lollipop (API 21) and is also available in AndroidX (as VectorDrawableCompat) bringing support all the way back to API 14 (over 99% of devices). This post will outline advice for actually using VectorDrawables in your apps.

  • Both nonZero and evenOdd path fillTypes —the two common ways of defining the inside of a shape, often used in SVGs (evenOdd added to platform impl in API 24)
  • Gradient & ColorStateList fills/strokes (added to platform impl in API 24)
  • Bug fixes

In fact, AndroidX will use the compat implementation even on some platforms where a native implementation exists (currently APIs 21–23) to deliver the above benefits. Otherwise it delegates to the platform implementation, so still receives any improvements on newer releases (for example VectorDrawable was re-implemented in C in API 24 for increased performance).

For these reasons you should always use AndroidX, even if you’re fortunate enough to have a minSdkVersion of 24. There’s little downside and if/when VectorDrawable is extended with new capabilities in the future and they’re also added to AndroidX, then they’ll be available straight away without having to revisit your code.

Alex Lockwood gets it:

(VDC == VectorDrawableCompat)

1. Enable Support

android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}

This flag prevents the Android Gradle Plugin from generating PNG versions of your vector assets if your minSdkVersion is

It is also passed to the build toolchain. By default AAPT (Android Asset Packaging Tool) versions resources. That means if you declare a VectorDrawable in res/drawable/ it will move it to res/drawable-v21/ for you as it knows this is when the VectorDrawable class was introduced.

This prevents attribute ID clashes — the attributes you use in VectorDrawables (android:pathData, android:fillColor etc) each have an integer ID associated with them, which were added in API 21. On older versions of Android, there was nothing preventing OEMs from using any ‘unclaimed’ IDs, making it unsafe to use a newer attribute on an older platform.

This versioning would prevent the asset from being accessed on older platforms, making a backport impossible—the gradle flag disables this versioning for vector drawables. This is why you use android:pathData etc within your vectors rather than having to switch to app:pathData etc like other backported functionality.

2. Load with AndroidX

VectorDrawableCompat also offers a create method. I’d recommend always using AppCompatResources instead as this adds a layer of caching.

If you want to set drawables declaratively (i.e. in your layouts) then appcompat offers a number of *Compat attributes that you should use instead of the standard platform ones:

ImageView, ImageButton:

  • Don’t: android:src
  • Do: app:srcCompat

CheckBox, RadioButton:

  • Don’t: android:button
  • Do: app:buttonCompat

TextView (as of appcompat:1.1.0):

  • Don’t: android:drawableStart android:drawableTop etc.
  • Do: app:drawableStartCompat app:drawableTopCompat etc.

As these attributes are part of the appcompat library, be sure to use the app: namespace. Internally these AppCompat* views use AppCompatResources themselves to enable loading vectors.

If you want to understand how appcompat swaps out the TextView etc you declare for an AppCompatTextView which enables this functionality then check out this article: https://helw.net/2018/08/06/appcompat-view-inflation/

Views without compat attributes

If you are using Data Binding then this can be accomplished using a custom binding adapter:

Note that we don’t want data binding to load the drawable for us (as it doesn’t use AppCompatResources to load drawables currently) so can’t refer to the drawable directly like @{@drawable/foo}. Instead we want to pass the drawable id to the binding adapter, so need to import the R class to reference it:

Nested drawables

To work around this, you can create drawables in code; i.e. use AppCompatResources to inflate the vector and then create the InsetDrawable drawable manually.

One exception is a recent addition to AndroidX (from appcompat:1.0.0) back-ported AnimatedStateListDrawables. This is a version of StateListDrawable with animated transitions between states (in the form of AnimatedVectorDrawables). But there is nothing requiring you to declare transitions. So if you just need a StateListDrawable which can inflate child vectors using AndroidX, then you could use this:

Credit for this genius hack: https://twitter.com/alexjlockwood/status/1029088247131996160

There is a way to enable vectors in nested drawables using AppCompatDelegate#setCompatVectorFromResourcesEnabled but it has a number of drawbacks. Be sure to read the javadoc carefully.

Out of process loading

You can of course use vectors on API 21+ but be aware that you might not enjoy the features/bugfixes provided by AndroidX. For example while it’s great that AndroidX backports fillType="evenOdd", a vector which uses this outside of AndroidX support on an API 21–23 device won’t understand this attribute. For this specific example, I’ll cover how to convert fillType at design time in the next article. Otherwise, you may need to provide alternate resources for different API levels:

res/
drawable-xxhdpi/
foo.png drawable-anydpi-v21/
foo.xml drawable-anydpi-v24/
foo.xml

Note that we need to include the anydpi resource qualifier here in addition to the api level qualifier. This is due to the way that resource qualifier precedence works; any asset in drawable-dpi would be considered a better match then one in just drawable-v21.

Now that we understand both why and how you should use vectors, the next article dives into how to create them.

Coming soon: Creating vector assets for Android
Coming soon: Profiling Android
VectorDrawables

You may also like

Leave a Reply