Android Vector Drawables

Karthik Iyer
MDG Space
Published in
8 min readJul 1, 2019

There are two basic ways an image can be represented in: Bitmaps and Vector Drawables. Normal Bitmaps are represented in the form of pixels in a grid. A Vector Drawable defines the image in the form of geometry, i.e as a set of points in the cartesian plane, connected through lines and curves, with their associated color information.

In this post we’ll be focuss
Android Vector Drawables

There are two basic ways an image can be represented in: Bitmaps and Vector Drawables. Normal Bitmaps are represented in the form of pixels…

Note: Changes here will affect how your story appears in public places like Medium’s homepage — not the story itself.

Add or change tags (up to 5) so readers know what your story is about

Android×
Add a tag…

Allow curators to recommend my story to interested readers.ing on static Vector Drawables and some of their advanced features. Animated Vector Drawables will be covered very briefly.

Why Vector Drawables?

Vector Drawables are,

  • Sharp

All Bitmaps have a specific resolution, which when displayed on a different density display may pixelate or might introduce unwanted artifacts. Vector Drawables look sharp on each display independent of display density, i.e. the same file is resized for different screen densities.

  • Small

A Bitmap of higher resolution means more pixels. More pixels means larger file size. Vector Drawables do not store pixels therefore they are of the same size independent of display density. This results in smaller APK files and less developer maintenance.

  • Flexible

Vector Drawables are highly flexible. One can also animate them using multiple XML files.

The support for Vector Drawables was added in API Level 21(Lollipop). Prior to this release if anyone wanted to represent images through Vector Graphics they had to do it manually though Java Code. If Bitmaps were used, developers had to use different images, one for each display resolution. Imagine using animated Bitmaps in such a situation!

There are two classes that help you use Vector Drawables: VectorDrawable and AnimatedVectorDrawable.

The VectorDrawable Class

This class defines a static drawable object. It is defined in a manner similar to the SVG format.

Image Courtesy: developer.android.com

It follows a tree hierarchy consisting of groups and paths. The path contains the actual geometric data to draw the object. The group contains data for transformation of the shape. The path is always a leaf of the tree. The path will be joined in same order in which it appears in the file.

The group text is an internal node of the tree. A path object inherits all the transformation of it’s ancestor groups.

Let’s take a look at a sample Vector Graphic as an SVG file and its corresponding XML Vector Drawable.

SVG

<svg xmlns=”http://www.w3.org/2000/svg" width=”24" height=”24" viewBox=”0 0 24 24">

<path fill-opacity=”.3" d=”M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V8h5.47L13 7v1h4V5.33C17 4.6 16.4 4 15.67 4z”/>

<path d=”M13 12.5h2L11 20v-5.5H9L12.47 8H7v12.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34–1.33V8h-4v4.5z”/>

<path d=”M0 0h24v24H0z” fill=”none”/>

</svg>

XML

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

<path
android:fillColor=”#000000"
android:fillAlpha=”.3"
android:pathData=”M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V8h5.47L13 7v1h4V5.33C17 4.6 16.4 4 15.67 4z” />
<path
android:fillColor=”#000000"
android:pathData=”M13 12.5h2L11 20v-5.5H9L12.47 8H7v12.67C7 21.4 7.6 22 8.33 22h7.33c0.74 0 1.34–0.6 1.34–1.33V8h-4v4.5z” />
<path
android:pathData=”M0 0h24v24H0z” />
</vector>

This Vector Drawable renders an image of a battery in charging mode.

There’s a lot more you can do with Vector Drawables. For example you can specify the tint for the image. You needn’t worry if the SVG the designer gave has the right shade of grey you need. The same Vector Drawable renders with different colors according to the set theme.

The same image rendered in a light and dark theme.(Image Courtesy: Android Dev Summit ‘18)

All you need to do to set the tint is add this attribute to the vector:

android:tint=”?attr/colorControlNormal”

You can also use theme colors for specific parts of your image. For example in order to use colorPrimary in you image add this attribute to your path:

android:fillColor=”?attr/colorPrimary”

The same images rendered with different primary colors.(Image Courtesy: Android Dev Summit ‘18)

Many a times we need to change the color of icons depending on the state of the button. These changes might be as minor as a different colored stroke. You can accomplish this using different colored images, but when the rendering is the same for most of the part, using ColorStateList inside the Vector Drawable is a much better way. You can avoid a lot of duplication in this manner.

The way to accomplish this is the same as that of a regular ColorStateList. Create a ColorStateList in the color resource directory, and use a reference to it in the fillColor attribute in the path.

If your ColorStateList is csl_image.xml, the add this line:

android:fillColor=”@color/csl_image”

Vectors also support gradients. You can have three types of gradients:

  • Linear

<gradient android:startColor=”#1b82bd”android:endColor=”#a242b4" android:startX=”12" android:endX=”12" android:startY=”0" android:endY=”24" android:type=”linear”/>

(Image Courtesy: Android Dev Summit ‘18)
  • Radial

<gradient android:startColor=”#1b82bd”android:endColor=”#a242b4" android:centerX=”0" android:centerY=”12" android:type=”radial”/>

(Image Courtesy: Android Dev Summit ‘18)
  • Sweep

<gradient android:startColor=”#1b82bd”android:endColor=”#a242b4" android:centerX=”0" android:centerY=”12" android:type=”sweep”/>

(Image Courtesy: Android Dev Summit ‘18)

We can also define individual color stops using <item> tag to get more fine grained gradients as show below:

<gradient…>
<item android:offset=”0.0" android:color=”#1b82bd”/>
<item android:offset=”0.72" android:color=”#6f5fb8"/>
<item android:offset=”1.0" android:color=”#a242b4"/>
</gradient>

This sets the color at 72% of the gradient direction parameter.

(Image Courtesy: Android Dev Summit ‘18)

Just as we define ColorStateLists in the color resource directory, we can define gradients and then add their reference in our Vector Drawable in the fillColor attribute.

An alternative to this is to define the gradient using inline resource syntax to embed it inside the vector definition itself as shown below.

<vector…>
<path…>
<aapt:attr name=”android:fillColor”>
<gradient
android:type=”sweep”
android:centerX=”0"
android:centerY=”0"
android:startColor=”#1b82bd”
android:endColor=”#a242b4"

</aapt:attr>

</path>
</vector>

Here the AAPT(Android Asset Packaging Tool) will extract this to a color resource at build time and insert a reference for it.Gradients are very useful in situations where you need shadows in Vector Drawables. Vector don’t support shadows, but it can be faked using gradients. You can also use it to create customized spinners using a radial gradient.

If the gradient doesn’t fill the entire image, you may choose to so any of the following:

  • Clamp

This is the default mode. It just continues the color outwards from the last offset point

You may accomplish this by adding the following line in the gradient:

<gradient…
android:tileMode=”clamp”/>

(Image Courtesy: Android Dev Summit ‘18)
  • Repeat

This repeats the gradient until the whole image is filled.

You may accomplish this by adding the following line in the gradient:

<gradient…
android:tileMode=”repeat”/>

(Image Courtesy: Android Dev Summit ‘18)
  • Mirror

This goes back and forth through the gradient.

You may accomplish this by adding the following line in the gradient:

<gradient…
android:tileMode=”mirror”/>

(Image Courtesy: Android Dev Summit ‘18)

We can also make gradients that do not go from color to color but have regions of solid color.

This can be accomplished by using the same color between two color stops, as shown below:

<gradient…>
<item android:offset=”0.0"
android:color=”#1b82bd”/>
<item android:offset=”0.5"
android:color=”#1b82bd”/>
<item android:offset=”0.5"
android:color=”#a242b4"/>
<item android:offset=”1.0"
android:color=”#a242b4"/>
</gradient>

The AnimatedVectorDrawable Class

This class adds animation to the properties of a VectorDrawable. We can define animated vector drawables in two ways:

  • Using three XML files

A VectorDrawable file, an AnimatedVectorDrawable file and an animator XML file.

  • Using a single XML file
  • You can also merge related XML files using XML Bundle Format.

To support vector drawable and animated vector drawable on devices running platform versions lower than Android 5.0 (API level 21), VectorDrawableCompat and AnimatedVectorDrawableCompat are available through two support libraries: support-vector-drawable and animated-vector-drawable, respectively.

Support Library 25.4.0 and higher supports the following features:

  • Path Morphing (PathType evaluator) Used to morph one path into another path.
  • Path Interpolation Used to define a flexible interpolator (represented as a path) instead of the system-defined interpolators like LinearInterpolator.

Support Library 26.0.0-beta1 and higher supports the following features:

  • Move along path The geometry object can move around, along an arbitrary path, as part of an animation.

For more information about Animated Vector Drawables you can refer to the links posted at the end of the post.

Performance tradeoff for Vector Drawables

Although the XML file used for Vector Drawables is usually smaller than conventional PNG images, it has significant computational overhead at runtime in case the drawn object is complex. When the image is rendered for the first time, a Bitmap cache is created to optimize redrawing performance. Whenever the image needs to be redrawn, the same Bitmap cache is used unless the image size is changed. So, for example you rotate your device, if the size of the rendered image remains the same, it will be rendered using the same cach otherwise the cache will be rebuilt. Therefore in comparison to raster images they take longer to render for the first time.

As the time taken to draw them is longer, Google recommends a maximum size of 200 x 200 dp.

Conclusion

Vector Drawables are great to reduce the size of the APK file, and reduce the number of images required.They are extremely flexible, but sometimes they come with a significant performance tradeoff. Therefore VectorDrawables should be used only when the image is simple upto a maximum size of 200 x 200 dp, for example icons for Buttons. Shapes should be used instead of Vector Graphics wherever possible.

Useful Links

--

--

Karthik Iyer
MDG Space

Computer Graphics Enthusiast | GSoC`19 at Swift for TensorFlow | Android Developer