In this tutorial, we’ll be discussing AnimatedVectorDrawable and implementing it in various ways in our android application. Having an idea of VectorDrawables would make the below article easier to understand.
AnimatedVectorDrawable
AnimatedVectorDrawable class was introduced since API 21 and is used to animate Vector Drawables beautifully and easily.
Following are some of the things that you can perform with AnimatedVectorDrawable:
- Rotate, Scale, Translate VectorDrawables
- Animate the VectorDrawable such as fill color etc.
- Draw paths and do Path Morphing
Typically to animate the Vector Drawable we define the animations using the ObjectAnimator
class.
ShapeShifter is a popular tool to visually create an AnimatedVectorDrawable.
For this, we need to import a Vector Asset/ SVG file.
We’ve got a sample vector drawable xml file named ic_settings.xml as shown below:
<vector android:height="24dp" android:tint="#E5496D"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="https://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
</vector>
Let’s import this in ShapeShifter to create an AnimatedVectorDrawable:
Firstly, we need to add the vector drawable inside a group layer in order to add animations to it.
We’ve set the vector drawable to rotate by 270 degrees.
You must set the pivotX and pivotY about which the VectorDrawable would rotate.
Following are some of the properties that can be set on the AnimatedVectorDrawable:
- rotation
- pivotX
- pivotY
- scaleX
- scaleY
- translateX
- translateY
- pathData
- fillColor
- strokeColor
- strokeWidth
- strokeAlpha
- fillAlpha
- trimPathStart
- trimPathEnd
- trimPathOffset
Now from the ShapeShifter, click Export – AnimatedVectorDrawable to generate the xml version of the AnimatedVectorDrawable:
avd_settings.xml
<animated-vector xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:aapt="https://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector
android:name="settings_icon"
android:width="48dp"
android:height="48dp"
android:viewportHeight="24"
android:viewportWidth="24">
<group android:name="animate_vector">
<path
android:name="gear"
android:fillColor="#FF000000"
android:pathData="@string/settings_path" />
</group>
</vector>
</aapt:attr>
<target android:name="animate_vector">
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:duration="800"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:propertyName="rotation"
android:repeatCount="infinite"
android:repeatMode="restart"
android:valueFrom="0"
android:valueTo="270"
android:valueType="floatType" />
<objectAnimator
android:duration="800"
android:interpolator="@android:interpolator/fast_out_slow_in"
android:propertyName="pivotX"
android:valueFrom="12"
android:valueTo="12"
android:valueType="floatType" />
<objectAnimator
android:duration="800"
android:interpolator="@android:interpolator/fast_out_slow_in"
android:propertyName="pivotY"
android:valueFrom="12"
android:valueTo="12"
android:valueType="floatType" />
</set>
</aapt:attr>
</target>
</animated-vector>
We’ve set the long path data as a string resource in the strings.xml file.
The name attribute of the target
element in the AnimatedVectorDrawable corresponds to the name of the group, which is animated. The animation attribute takes the animator class which is object animator here.
To make the AnimatedVectorDrawable animate forever, we’ve added a repeatCount to infinite in the ObjectAnimator tag.
Now that we have got the AnimatedVectorDrawable we can set it on our ImageView(any related view) by:
android:src="@drawable/avd_settings"
.
Still the animated vector drawable won’t animate!
In order to make it animate, we need to do:
Animatable animatable = imageView.getDrawable();
animatable.start();
Animatable is an interface that contains methods to handle animations on drawables.
Methods like – start()
, stop()
, isRunning()
Setting AnimatedVectorDrawable Programmatically
AnimatedVectorDrawableCompat animatedVectorDrawableCompat = AnimatedVectorDrawableCompat.create(this, R.drawable.avd_settings);
imageView.setImageDrawable(animatedVectorDrawableCompat);
this
is the Context.
In the following section, we’ll be creating different types of AnimatedVectorDrawable
Project Structure
android {
...
defaultConfig {
...
vectorDrawables.useSupportLibrary = true
...
}
...
}
Code
The code for the activity_main.xml layout is given below:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<ImageView
android:id="@+id/imgSettings"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="4dp"
app:srcCompat="@drawable/avd_settings" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fabSync"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:backgroundTint="@android:color/white" />
<ImageView
android:id="@+id/imgJD"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="4dp"
app:srcCompat="@drawable/avd_jd" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fabTickCross"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:backgroundTint="@android:color/white"
android:src="@drawable/ic_tick" />
</LinearLayout>
We’ve already seen the avd_settings.xml file.
Let’s see avd_jd.xml next.
The ic_jd.xml that we’ve written is:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="https://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportHeight="48.0"
android:viewportWidth="48.0">
<group android:name="thing">
<path
android:name="Dev"
android:pathData="M 25 10 L 12 10 M 25 10 L 25 30 M 25 30 C 20 35 15 30 12 25"
android:strokeColor="#000"
android:strokeWidth="1" />
<path
android:name="Journal"
android:pathData="M 30 10 L 30 30 M 30 10 C 45 15 45 25 30 30"
android:strokeColor="@android:color/holo_red_dark"
android:strokeWidth="1" />
</group>
</vector>
The avd_jd.xml code generated from the ShapeShifter is:
<animated-vector
xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:aapt="https://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector
android:name="vector"
android:width="148dp"
android:height="148dp"
android:viewportWidth="48"
android:viewportHeight="48">
<group android:name="journaldev">
<group
android:name="group_j"
android:pivotX="24"
android:pivotY="24"
android:rotation="270">
<path
android:name="journal"
android:pathData="M 25 10 L 12 10 M 25 10 L 25 30 M 25 30 C 20 35 15 30 12 25"
android:strokeColor="#000000"
android:strokeWidth="1"/>
</group>
</group>
<group
android:name="group_d"
android:pivotX="24"
android:pivotY="24"
android:translateX="10"
android:scaleX="0.5"
android:scaleY="0.5">
<path
android:name="dev"
android:pathData="M 30 10 L 30 30 M 30 10 C 45 15 45 25 30 30"
android:strokeColor="#000000"
android:strokeWidth="1"/>
</group>
</vector>
</aapt:attr>
<target android:name="group_d">
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:propertyName="translateX"
android:duration="400"
android:valueFrom="10"
android:valueTo="0"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="scaleX"
android:duration="600"
android:valueFrom="0.5"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="scaleY"
android:duration="600"
android:valueFrom="0.5"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</set>
</aapt:attr>
</target>
<target android:name="group_j">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="rotation"
android:startOffset="100"
android:duration="500"
android:valueFrom="270"
android:valueTo="0"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</aapt:attr>
</target>
<target android:name="journal">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="strokeColor"
android:startOffset="600"
android:duration="200"
android:valueFrom="#000000"
android:valueTo="#cc0000"
android:valueType="colorType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</aapt:attr>
</target>
</animated-vector>
In the above XML code, we’ve done scale, animate and rotate separately on both the paths.
Notice that each path has a name property which is linked to the respective target.
The XML properties are self-explanatory. And once you get a hang of it, you can write the AnimatedVectorDrawable XML code directly without the need of ShapeShifter.
The avd_cross2tick.xml and avd_tick2cross.xml are used to transform a tickmark vector drawable into a cross vector drawable. Thus it transforms paths.
The codes for the object animators are separately put inside the animator folder inside the res directory.
You can find their complete implementations in the source code/Github repository at the end of this tutorial. We’re skipping this due to space constraints.
The code for the MainActivity.java is given below:
package com.journaldev.androidanimatedvectordrawable;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.design.widget.FloatingActionButton;
import android.support.graphics.drawable.AnimatedVectorDrawableCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private AnimatedVectorDrawable tickToCross, crossToTick;
private boolean isTick = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FloatingActionButton fabSync = findViewById(R.id.fabSync);
FloatingActionButton fabTickCross = findViewById(R.id.fabTickCross);
tickToCross = (AnimatedVectorDrawable) getDrawable(R.drawable.avd_tick2cross);
crossToTick = (AnimatedVectorDrawable) getDrawable(R.drawable.avd_cross2tick);
ImageView imgSettings = findViewById(R.id.imgSettings);
ImageView imgJD = findViewById(R.id.imgJD);
imgSettings.setOnClickListener(this);
imgJD.setOnClickListener(this);
AnimatedVectorDrawableCompat animatedVectorDrawableCompat = AnimatedVectorDrawableCompat.create(this, R.drawable.avd_sync);
fabSync.setImageDrawable(animatedVectorDrawableCompat);
fabSync.setOnClickListener(this);
fabTickCross.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.imgSettings:
Animatable animatable = (Animatable) ((ImageView) view).getDrawable();
if (animatable.isRunning())
animatable.stop();
else
animatable.start();
break;
case R.id.imgJD:
animatable = (Animatable) ((ImageView) view).getDrawable();
if (animatable.isRunning())
animatable.stop();
else
animatable.start();
break;
case R.id.fabSync:
animatable = (Animatable) ((FloatingActionButton) view).getDrawable();
if (animatable.isRunning())
animatable.stop();
else
animatable.start();
break;
case R.id.fabTickCross:
AnimatedVectorDrawable drawable = isTick ? tickToCross : crossToTick;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
drawable.registerAnimationCallback(new Animatable2.AnimationCallback() {
@Override
public void onAnimationStart(Drawable drawable) {
super.onAnimationStart(drawable);
}
@Override
public void onAnimationEnd(Drawable drawable) {
super.onAnimationEnd(drawable);
}
});
}
FloatingActionButton fab = ((FloatingActionButton) view);
fab.setImageDrawable(drawable);
drawable.start();
isTick = !isTick;
break;
}
}
}
Here we’ve set the AnimatedVectorDrawable on two ImageView and two FloatingActionButtons.
In order to listen to animation start and end, we can register Animation Callbacks on the AnimatedVectorDrawable as:
drawable.registerAnimationCallback(new Animatable2.AnimationCallback() {
@Override
public void onAnimationStart(Drawable drawable) {
super.onAnimationStart(drawable);
}
@Override
public void onAnimationEnd(Drawable drawable) {
super.onAnimationEnd(drawable);
}
});
Note: registerAnimationCallback
works on Android M(23) and above only.
The output of the application in action is :
This brings an end to this tutorial. You can download the project from the link below or check out the GitHub repository for the complete XML codes of all the AnimatedVectorDrawables.
Nice article. However, my app crashes whenever path morphing is used. Tried code of my own and one provided in several posts but no luck. An article to help with that shall be more useful.
Sir I am your student. I learnt many things from tutorial. This vector Animation is not working on my devices. I used marshmallow device for testing but it is crashing on ImageView some error occurred. If you give a reply I will send you that crash in next post. Thanks in advance you are good teacher.