Using Android's VectorDrawable Class
Introduction
While Android does not support SVGs (Scalable Vector Graphics) directly, Lollipop introduced a new class called VectorDrawable
, which allows designers and developers to draw assets in a similar fashion using only code.
In this article, you will learn how to create a VectorDrawable
with XML files and animate them in your projects. This is only supported for devices running Android 5.0 or above, and currently there are no support-library implementations. The source files of this tutorial can be found on GitHub.
1. Creating a Vector Drawable
The main similarity between a VectorDrawable
and a standard SVG image is that both are drawn out using a path value. While understanding how SVG paths are drawn is out of the scope of this article, official documentation can be found on the W3C website. For this article, you'll simply need to know that the path tag is where the drawing occurs. Let's take a look at the SVG file that draws out the following image:
There are five major parts to this image:
- a square for the CPU body made up of two arches
- four groups of five lines that represent the CPU's wires
The following code draws this image out as an SVG:
1 |
<?xml version="1.0" encoding="utf-8"?>
|
2 |
|
3 |
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
4 |
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" |
5 |
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve"> |
6 |
<path id="cpu" d=" |
7 |
M341.087,157.478c7.417,0,13.435,6.018,13.435,13.435v170.174 c0,7.417-6.018,13.435-13.435,13.435H170.913 c-7.417,0-13.435-6.018-13.435-13.435V170.913 c0-7.417,6.018-13.435,13.435-13.435H341.087z
|
8 |
M390.348,157.478 c0-19.785-16.041-35.826-35.826-35.826H157.479c-19.785,0-35.826,16.041-35.826,35.826v197.043 c0,19.785,16.041,35.826,35.826,35.826h197.043c19.785 0,35.826-16.041,35.826-35.826V157.478z
|
9 |
|
10 |
M193.304,408.261V462h-17.913 v-53.739H193.304z
|
11 |
M264.957,408.261V462h-17.914v-53.739H264.957z
|
12 |
M300.783,408.261V462h-17.914v-53.739H300.783z
|
13 |
M229.13,408.261 V462h-17.913v-53.739H229.13z
|
14 |
M336.609,408.261V462h-17.914v-53.739H336.609z
|
15 |
|
16 |
M193.304,50v53.739h-17.913V50H193.304z
|
17 |
M264.957,50 v53.739h-17.914V50H264.957z
|
18 |
M300.783,50v53.739h-17.914V50H300.783z
|
19 |
M229.13,50v53.739h-17.913V50H229.13z
|
20 |
M336.609,50v53.739 h-17.914V50H336.609z
|
21 |
|
22 |
M408.261,318.695H462v17.914h-53.739V318.695z
|
23 |
M408.261,247.043H462v17.914h-53.739V247.043z
|
24 |
M408.261,211.217 H462v17.913h-53.739V211.217z
|
25 |
M408.261,282.869H462v17.914h-53.739V282.869z
|
26 |
M408.261,175.391H462v17.913h-53.739V175.391z
|
27 |
|
28 |
M50,318.695h53.739v17.914H50V318.695z
|
29 |
M50,247.043h53.739v17.914H50V247.043z
|
30 |
M50,211.217h53.739v17.913H50V211.217z
|
31 |
M50,282.869 h53.739v17.914H50V282.869z
|
32 |
M50,175.391h53.739v17.913H50V175.391z" /> |
33 |
</svg>
|
While this may look a little overwhelming, you don't actually need to fully understand how everything is drawn out to implement a VectorDrawable
in your code. However, it should be noted that I separated each of the five sections into their own unique block in the code for readability.
The top section consists of two arches to draw out the rounded square and the sections that follow represent the bottom, top, right, and left sets of lines respectively. To turn this SVG code into a VectorDrawable
, you first need to define the vector
object in XML. The following code is taken from the vector_drawable_cpu.xml file in the sample code for this article.
1 |
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
2 |
android:height="64dp" |
3 |
android:width="64dp" |
4 |
android:viewportHeight="600" |
5 |
android:viewportWidth="600" > |
6 |
|
7 |
</vector>
|
Next, you can add in the path data. The following code is broken up into five different path tags rather than one large path.
1 |
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
2 |
android:height="64dp" |
3 |
android:width="64dp" |
4 |
android:viewportHeight="600" |
5 |
android:viewportWidth="600" > |
6 |
|
7 |
<path
|
8 |
android:name="cpu" |
9 |
android:fillColor="#000000" |
10 |
android:pathData=" |
11 |
M341.087,157.478 c7.417,0,13.435,6.018,13.435,13.435 v170.174c0,7.417-6.018,13.435-13.435,13.435 H170.913 c-7.417,0-13.435-6.018-13.435-13.435V170.913c0-7.417,6.018-13.435,13.435-13.435H341.087z
|
12 |
M390.348,157.478 c0-19.785-16.041-35.826-35.826-35.826H157.479c-19.785,0-35.826,16.041-35.826,35.826v197.043 c0,19.785,16.041,35.826,35.826,35.826h197.043c19.785,0,35.826-16.041,35.826-35.826V157.478z"
|
13 |
/>
|
14 |
|
15 |
<path
|
16 |
android:name="wires_bottom" |
17 |
android:fillColor="#000000" |
18 |
android:pathData=" |
19 |
M193.304,408.261V462h-17.913 v-53.739H193.304z
|
20 |
M264.957,408.261V462h-17.914v-53.739H264.957z
|
21 |
M300.783,408.261V462h-17.914v-53.739H300.783z
|
22 |
M229.13,408.261 V462h-17.913v-53.739H229.13z
|
23 |
M336.609,408.261V462h-17.914v-53.739H336.609z"
|
24 |
/>
|
25 |
|
26 |
<path
|
27 |
android:name="wires_top" |
28 |
android:fillColor="#000000" |
29 |
android:pathData=" |
30 |
M193.304,50v53.739h-17.913V50H193.304z
|
31 |
M264.957,50 v53.739h-17.914V50H264.957z
|
32 |
M300.783,50v53.739h-17.914V50H300.783z
|
33 |
M229.13,50v53.739h-17.913V50H229.13z
|
34 |
M336.609,50v53.739 h-17.914V50H336.609z"
|
35 |
/>
|
36 |
|
37 |
<path
|
38 |
android:name="wires_right" |
39 |
android:fillColor="#000000" |
40 |
android:pathData=" |
41 |
M408.261,318.695H462v17.914h-53.739V318.695z
|
42 |
M408.261,247.043H462v17.914h-53.739V247.043z
|
43 |
M408.261,211.217 H462v17.913h-53.739V211.217z
|
44 |
M408.261,282.869H462v17.914h-53.739V282.869z
|
45 |
M408.261,175.391H462v17.913h-53.739V175.391z"
|
46 |
/>
|
47 |
|
48 |
<path
|
49 |
android:name="wires_left" |
50 |
android:fillColor="#000000" |
51 |
android:pathData=" |
52 |
M50,318.695h53.739v17.914H50V318.695z
|
53 |
M50,247.043h53.739v17.914H50V247.043z
|
54 |
M50,211.217h53.739v17.913H50V211.217z
|
55 |
M50,282.869 h53.739v17.914H50V282.869z
|
56 |
M50,175.391h53.739v17.913H50V175.391z"
|
57 |
/>
|
58 |
|
59 |
</vector>
|
As you can see, each path section simply uses the pathData
attribute for drawing. You can now include the VectorDrawable
XML file as a drawable in a standard ImageView
and it will scale to any size your app requires, without needing to use any Java code.
2. Animating Vector Drawables
Now that you know how to create images using only code, it's time to have a little fun and animate them. In the following animation, you'll notice that each of the groups of wires are pulsing towards and away from the CPU.
To achieve this effect, you will need to wrap each section that you want to animate in a <group>
tag. The updated version of vector_drawable_cpu.xml then looks like this:
1 |
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
2 |
android:height="64dp" |
3 |
android:width="64dp" |
4 |
android:viewportHeight="600" |
5 |
android:viewportWidth="600" > |
6 |
|
7 |
<group
|
8 |
android:name="cpu_box"> |
9 |
<path
|
10 |
android:name="cpu" |
11 |
android:fillColor="#000000" |
12 |
android:pathData=" |
13 |
M341.087,157.478 c7.417,0,13.435,6.018,13.435,13.435 v170.174c0,7.417-6.018,13.435-13.435,13.435 H170.913 c-7.417,0-13.435-6.018-13.435-13.435V170.913c0-7.417,6.018-13.435,13.435-13.435H341.087z
|
14 |
M390.348,157.478 c0-19.785-16.041-35.826-35.826-35.826H157.479c-19.785,0-35.826,16.041-35.826,35.826v197.043 c0,19.785,16.041,35.826,35.826,35.826h197.043c19.785,0,35.826-16.041,35.826-35.826V157.478z "/> |
15 |
</group>
|
16 |
<group
|
17 |
android:name="bottom"> |
18 |
<path
|
19 |
android:name="wires_bottom" |
20 |
android:fillColor="#000000" |
21 |
android:pathData=" |
22 |
M193.304,408.261V462h-17.913 v-53.739H193.304z
|
23 |
M264.957,408.261V462h-17.914v-53.739H264.957z
|
24 |
M300.783,408.261V462h-17.914v-53.739H300.783z
|
25 |
M229.13,408.261 V462h-17.913v-53.739H229.13z
|
26 |
M336.609,408.261V462h-17.914v-53.739H336.609z" /> |
27 |
</group>
|
28 |
<group android:name="top"> |
29 |
<path
|
30 |
android:name="wires_top" |
31 |
android:fillColor="#000000" |
32 |
android:pathData=" |
33 |
M193.304,50v53.739h-17.913V50H193.304z
|
34 |
M264.957,50 v53.739h-17.914V50H264.957z
|
35 |
M300.783,50v53.739h-17.914V50H300.783z
|
36 |
M229.13,50v53.739h-17.913V50H229.13z
|
37 |
M336.609,50v53.739 h-17.914V50H336.609z " /> |
38 |
</group>
|
39 |
<group android:name="right"> |
40 |
<path
|
41 |
android:name="wires_right" |
42 |
android:fillColor="#000000" |
43 |
android:pathData=" |
44 |
M408.261,318.695H462v17.914h-53.739V318.695z
|
45 |
M408.261,247.043H462v17.914h-53.739V247.043z
|
46 |
M408.261,211.217 H462v17.913h-53.739V211.217z
|
47 |
M408.261,282.869H462v17.914h-53.739V282.869z
|
48 |
M408.261,175.391H462v17.913h-53.739V175.391z" /> |
49 |
</group>
|
50 |
<group android:name="left"> |
51 |
<path
|
52 |
android:name="wires_left" |
53 |
android:fillColor="#000000" |
54 |
android:pathData=" |
55 |
M50,318.695h53.739v17.914H50V318.695z
|
56 |
M50,247.043h53.739v17.914H50V247.043z
|
57 |
M50,211.217h53.739v17.913H50V211.217z
|
58 |
M50,282.869 h53.739v17.914H50V282.869z
|
59 |
M50,175.391h53.739v17.913H50V175.391z" /> |
60 |
</group>
|
61 |
|
62 |
</vector>
|
Next, you will want to create animators for each animation type. In this case, there is one for each group of wires for a total of four. Below is an example of the top group's animation and you will also need one for the bottom, left, and right. Each of the animator XML files can be found in the sample code.
1 |
<?xml version="1.0" encoding="utf-8"?>
|
2 |
<set xmlns:android="http://schemas.android.com/apk/res/android"> |
3 |
<objectAnimator
|
4 |
android:propertyName="translateY" |
5 |
android:valueType="floatType" |
6 |
android:valueFrom="0" |
7 |
android:valueTo="-10" |
8 |
android:repeatMode="reverse" |
9 |
android:repeatCount="infinite" |
10 |
android:duration="250" /> |
11 |
</set>
|
As you can see, the propertyName
is set to translateY
, which means the animation will move along the Y axis. The valueFrom
and valueTo
control the begin and end location. By setting repeatMode
to reverse
and repeatCount
to infinite
, the animation will loop forever as long as the VectorDrawable
is visible. The duration
of the animation is set to 250
, which is the time in milliseconds.
To apply the animations to your drawable file, you will need to create a new animated-vector
XML file to associate the animators with the VectorDrawable
groups. The following code is used to create the animated_cpu.xml file.
1 |
<?xml version="1.0" encoding="utf-8"?>
|
2 |
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" |
3 |
android:drawable="@drawable/vector_drawable_cpu"> |
4 |
|
5 |
<target
|
6 |
android:animation="@animator/pulse_top" |
7 |
android:name="top" /> |
8 |
|
9 |
<target
|
10 |
android:animation="@animator/pulse_right" |
11 |
android:name="right" /> |
12 |
|
13 |
<target
|
14 |
android:animation="@animator/pulse_left" |
15 |
android:name="left" /> |
16 |
|
17 |
<target
|
18 |
android:animation="@animator/pulse_bottom" |
19 |
android:name="bottom" /> |
20 |
</animated-vector>
|
When all of your XML are ready to go, you can use the animated_cpu.xml drawable in an ImageView
to display it.
1 |
<ImageView
|
2 |
android:id="@+id/cpu" |
3 |
android:layout_width="64dp" |
4 |
android:layout_height="64dp" |
5 |
android:src="@drawable/animated_cpu" /> |
To start your animation, you will need to get an instance of the Animatable
from the ImageView
and call start
.
1 |
ImageView mCpuImageView = (ImageView) findViewById( R.id.cpu ); |
2 |
Drawable drawable = mCpuImageView.getDrawable(); |
3 |
if (drawable instanceof Animatable) { |
4 |
((Animatable) drawable).start(); |
5 |
}
|
After start
has been called, the wires on the CPU image will start to move with very minimal Java code used.
3. Transforming Vector Drawables
A common use case for a VectorDrawable
is transforming one image into another, such as the action bar icon that changes from a hamburger icon into an arrow. To do this, both the source and destination paths must follow an identical format for the number of elements. For this example we will define the left and right facing arrows seen above as strings.
1 |
<string name="left_arrow">M300,70 l 0,70 -70,-70 0,0 70,-70z</string> |
2 |
<string name="right_arrow">M300,70 l 0,-70 70,70 0,0 -70,70z</string> |
Next, you will need to create an initial drawable for an arrow using the path for left_arrow. In the sample code, it is called vector_drawable_left_arrow.xml.
1 |
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
2 |
android:height="64dp" |
3 |
android:width="64dp" |
4 |
android:viewportHeight="600" |
5 |
android:viewportWidth="600" > |
6 |
|
7 |
<path
|
8 |
android:name="left_arrow" |
9 |
android:fillColor="#000000" |
10 |
android:pathData="@string/left_arrow"/> |
11 |
</vector>
|
The main difference between the CPU animation and the transformation lies in the animator_left_right_arrow.xml file.
1 |
<?xml version="1.0" encoding="utf-8"?>
|
2 |
<set xmlns:android="http://schemas.android.com/apk/res/android"> |
3 |
|
4 |
<objectAnimator
|
5 |
android:duration="1000" |
6 |
android:propertyName="pathData" |
7 |
android:valueFrom="@string/left_arrow" |
8 |
android:valueTo="@string/right_arrow" |
9 |
android:valueType="pathType" |
10 |
android:repeatMode="reverse" |
11 |
android:repeatCount="-1"/> |
12 |
|
13 |
</set>
|
You'll notice the valueFrom
and valueTo
properties reference the path data for the left and right arrow, the valueType
is set to pathType
and propertyName
is set to pathData
. When these are set, the animator will know to change one set of path data to the other. When the animator is finished, you need to associate the VectorDrawable
with the objectAnimator
using a new animated-vector
object.
1 |
<?xml version="1.0" encoding="utf-8"?>
|
2 |
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" |
3 |
android:drawable="@drawable/vector_drawable_left_arrow"> |
4 |
|
5 |
<target
|
6 |
android:name="left_arrow" |
7 |
android:animation="@animator/animator_left_right_arrows" /> |
8 |
</animated-vector>
|
Finally, you'll simply need to associate the animated drawable with an ImageView
and start the animation in your Java code.
1 |
<ImageView
|
2 |
android:id="@+id/left_right_arrow" |
3 |
android:layout_width="64dp" |
4 |
android:layout_height="64dp" |
5 |
android:layout_below="@+id/cpu" |
6 |
android:src="@drawable/animated_arrow" /> |
1 |
mArrowImageView = (ImageView) findViewById( R.id.left_right_arrow ); |
2 |
drawable = mArrowImageView.getDrawable(); |
3 |
if (drawable instanceof Animatable) { |
4 |
((Animatable) drawable).start(); |
5 |
}
|
Conclusion
As you have seen, the VectorDrawable
class is fairly straightforward to use and allows for a lot of customization to add simple animations. While the VectorDrawable
class is currently only available for devices running Android 5.0 and above, they will be invaluable as more devices support Lollipop and future Android releases.