Mobile Development 50 min read

Master Android VectorDrawable & AnimatedVectorDrawable: From SVG to Code and Animation

This article explains how Android handles SVG-like vector graphics using VectorDrawable and AnimatedVectorDrawable, covering their XML structures, supported tags, code examples, conversion from SVG, usage in layouts, and a detailed source code walkthrough of rendering and animation processes.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Master Android VectorDrawable & AnimatedVectorDrawable: From SVG to Code and Animation

01 VectorDrawable

Android supports a subset of SVG features through VectorDrawable. The format uses an XML tree with a <vector> root and supports tags such as <group>, <path>, and <clip-path>. Attributes like android:name, android:fillColor, android:strokeColor, and android:pathData correspond closely to SVG attributes.

1.1 Elements and Attributes

The supported tags and attributes are defined in attrs.xml. The most common tag is <path>, whose pathData attribute uses the same command set as SVG (M, L, H, V, C, S, Q, T, A, Z).

<declare-styleable name="VectorDrawablePath">
    <attr name="name" />
    <attr name="strokeColor" format="color" />
    <attr name="strokeWidth" format="float" />
    <attr name="fillColor" format="color" />
    <attr name="fillAlpha" format="float" />
    <attr name="strokeAlpha" format="float" />
    <attr name="pathData" format="string" />
    <!-- other attributes omitted for brevity -->
</declare-styleable>

1.2 Basic Shapes

Shapes are defined with <path> elements. Below is a simple test1.xml that draws a triangle, square, circle, and star.

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="40dp"
    android:height="40dp"
    android:viewportWidth="40"
    android:viewportHeight="40">
    <group
        android:name="icon_group"
        android:translateX="0"
        android:translateY="0"
        android:rotation="0">
        <path
            android:name="triangle"
            android:fillColor="#00FF00"
            android:pathData="M0,0 L0,10 L10,0z" />
        <path
            android:name="square"
            android:fillColor="#00FF00"
            android:pathData="M10,0 L20,0 L20,10 L10,10 Z" />
        <path
            android:name="circle"
            android:fillColor="#00FF00"
            android:pathData="M0,18 A5,5 0 1,1 10,18 A5,5 0 1,1 0,18 Z" />
        <path
            android:name="star"
            android:fillColor="#00FF00"
            android:pathData="M16,13 L17,17 21,17 18,19 20,23 16,20 12,23 14,19 11,17 15,17 Z" />
    </group>
</vector>

1.3 Transformations

The <group> tag supports rotation, translation, and scaling, while <path> does not. By moving <path> elements into a <group>, you can apply the same transformation to multiple shapes.

<vector ...>
    <group
        android:name="icon_group"
        android:translateX="10"
        android:translateY="0"
        android:rotation="10">
        <path
            android:name="triangle"
            android:fillColor="#00FF00"
            android:strokeColor="#FF0000"
            android:strokeWidth="0.5"
            android:pathData="M0,0 L0,10 L10,0z" />
        <!-- other paths omitted for brevity -->
    </group>
</vector>

1.4 Gradients

Both fillColor and strokeColor can reference gradient resources using the aapt:attr tag.

<vector ...>
    <path
        android:name="oval1"
        android:strokeColor="#ff0000"
        android:strokeWidth="2"
        android:pathData="M10,60 a30,40 0 1,0 80,0 a30,40 0 1,0 -80,0">
        <aapt:attr name="android:fillColor">
            <gradient
                android:startX="0"
                android:startY="0"
                android:endX="0"
                android:endY="100"
                android:type="linear">
                <item android:offset="0" android:color="#FF0000"/>
                <item android:offset="1" android:color="#0000FF"/>
            </gradient>
        </aapt:attr>
    </path>
</vector>

1.5 Clip Path

The <clip-path> tag defines a clipping region for subsequent drawing commands.

<vector ...>
    <group>
        <clip-path
            android:name="clip1"
            android:pathData="M0,0 h100 v80 h-100 Z" />
        <path
            android:name="oval1"
            android:fillColor="#0000ff"
            android:strokeColor="#ff0000"
            android:strokeWidth="2"
            android:pathData="M10,60 a30,40 0 1,0 80,0 a30,40 0 1,0 -80,0 Z" />
    </group>
</vector>

1.6 Importing SVG

Android Studio’s Vector Asset tool can import system vector assets or local SVG files. Simple SVGs that contain only <path> elements convert cleanly; more complex SVGs may require manual adjustments.

Vector Asset tool
Vector Asset tool

02 AnimatedVectorDrawable

Android does not support SVG animation tags. Instead, AnimatedVectorDrawable combines a static VectorDrawable with property animations defined by ObjectAnimator or AnimatorSet.

2.1 Elements and Attributes

The root tag is <animated-vector> with a required android:drawable attribute that references a VectorDrawable. Each <target> specifies the name of a vector element and an animation resource.

<declare-styleable name="AnimatedVectorDrawable">
    <attr name="drawable" />
</declare-styleable>

<declare-styleable name="AnimatedVectorDrawableTarget">
    <attr name="name" />
    <attr name="animation" />
</declare-styleable>

2.2 Using Animated Vectors

Below is test8.xml, which animates a simple check‑mark into a cross while changing its stroke color.

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt">
    <aapt:attr name="android:drawable">
        <vector android:width="24dp" android:height="24dp"
            android:viewportWidth="24" android:viewportHeight="24">
            <path android:name="root"
                android:strokeWidth="2"
                android:strokeLineCap="square"
                android:strokeColor="#FF0000"
                android:pathData="M4.8,13.4 L9,17.6 M10.4,16.2 L19.6,7" />
        </vector>
    </aapt:attr>
    <target android:name="root">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:propertyName="pathData"
                android:valueFrom="M4.8,13.4 L9,17.6 M10.4,16.2 L19.6,7"
                android:valueTo="M6.4,6.4 L17.6,17.6 M6.4,17.6 L17.6,6.4"
                android:duration="6000"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:valueType="pathType" />
        </aapt:attr>
    </target>
    <target android:name="root">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:propertyName="strokeColor"
                android:valueFrom="#FF0000"
                android:valueTo="#0000FF"
                android:duration="6000"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:valueType="colorType" />
        </aapt:attr>
    </target>
</animated-vector>

Use the drawable in a layout:

<ImageView
    android:id="@+id/img_2"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:src="@drawable/test8" />

Start the animation in code:

ImageView imageView = findViewById(R.id.img_2);
AnimatedVectorDrawable avd = (AnimatedVectorDrawable) imageView.getDrawable();
if (avd != null) {
    avd.start();
}

2.3 Source Code Walkthrough

The Android framework parses animated-vector in DrawableInflater.inflateFromTag, creates an AnimatedVectorDrawable, and calls its inflate method. Inside AnimatedVectorDrawable.inflate the drawable attribute is resolved to a VectorDrawable, and each target loads an Animator (usually an ObjectAnimator) via AnimatorInflater.loadAnimator.

// DrawableInflater.java (simplified)
Drawable drawable = inflateFromTag(name);
if (drawable == null) {
    drawable = inflateFromClass(name);
}
 drawable.inflate(mRes, parser, attrs, theme);
return drawable;

// AnimatedVectorDrawable.inflate(...)
if (ANIMATED_VECTOR.equals(tagName)) {
    TypedArray a = obtainAttributes(res, theme, attrs, R.styleable.AnimatedVectorDrawable);
    int drawableRes = a.getResourceId(R.styleable.AnimatedVectorDrawable_drawable, 0);
    if (drawableRes != 0) {
        VectorDrawable vectorDrawable = (VectorDrawable) res.getDrawable(drawableRes, theme).mutate();
        state.mVectorDrawable = vectorDrawable;
    }
    a.recycle();
} else if (TARGET.equals(tagName)) {
    TypedArray a = obtainAttributes(res, theme, attrs, R.styleable.AnimatedVectorDrawableTarget);
    String target = a.getString(R.styleable.AnimatedVectorDrawableTarget_name);
    int animResId = a.getResourceId(R.styleable.AnimatedVectorDrawableTarget_animation, 0);
    if (animResId != 0) {
        Animator animator = AnimatorInflater.loadAnimator(res, theme, animResId, pathErrorScale);
        updateAnimatorProperty(animator, target, state.mVectorDrawable, state.mShouldIgnoreInvalidAnim);
        state.addTargetAnimator(target, animator);
    }
    a.recycle();
}

The method updateAnimatorProperty binds the animator’s PropertyValuesHolder to the corresponding vector element’s Property (e.g., VFullPath.PATH_DATA).

private static void updateAnimatorProperty(Animator animator, String targetName,
        VectorDrawable vectorDrawable, boolean ignoreInvalidAnim) {
    if (animator instanceof ObjectAnimator) {
        PropertyValuesHolder[] holders = ((ObjectAnimator) animator).getValues();
        for (PropertyValuesHolder pvh : holders) {
            String propertyName = pvh.getPropertyName();
            Object targetObj = vectorDrawable.getTargetByName(targetName);
            Property property = null;
            if (targetObj instanceof VectorDrawable.VObject) {
                property = ((VectorDrawable.VObject) targetObj).getProperty(propertyName);
            }
            if (property != null && containsSameValueType(pvh, property)) {
                pvh.setProperty(property);
            }
        }
    } else if (animator instanceof AnimatorSet) {
        for (Animator child : ((AnimatorSet) animator).getChildAnimations()) {
            updateAnimatorProperty(child, targetName, vectorDrawable, ignoreInvalidAnim);
        }
    }
}

When the animation starts, AnimatedVectorDrawable.start() creates an AnimatorSet that drives the ObjectAnimator. The animation loop is driven by Android’s Choreographer via AnimationHandler. Each frame calls ObjectAnimator.calculateValue, which uses a PathDataEvaluator to interpolate between the start and end PathData. The resulting PathParser.PathData is set on the vector element through the static Property<VPath, PathData> PATH_DATA defined in VectorDrawable.VPath. Finally, the updated vector is drawn by VectorDrawable.draw(), which delegates to native rendering code.

03 Summary

VectorDrawable provides a lightweight, tree‑structured format for static vector graphics on Android. It supports a limited set of tags ( vector, group, path, clip-path) and attributes that map closely to SVG, making it ideal for icons and simple UI elements.

AnimatedVectorDrawable extends this model with property animations. By linking ObjectAnimator (or AnimatorSet) to vector elements, developers can animate path data, colors, and other properties without external libraries. The animation engine reuses Android’s core animation framework (Animator, Choreographer) and performs path interpolation in native code for performance.

Both formats are compact, resolution‑independent, and integrate seamlessly with Android layouts, offering a powerful way to create crisp, animated UI components.

Animation flow diagram
Animation flow diagram
mobile developmentGraphicsAndroidSVGAnimatedVectorDrawableVectorDrawable
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.