FileDocCategorySizeDatePackage
AnimatedVectorDrawable.javaAPI DocAndroid 5.1 API18823Thu Mar 12 22:22:30 GMT 2015android.graphics.drawable

AnimatedVectorDrawable

public class AnimatedVectorDrawable extends Drawable implements Animatable
This class uses {@link android.animation.ObjectAnimator} and {@link android.animation.AnimatorSet} to animate the properties of a {@link android.graphics.drawable.VectorDrawable} to create an animated drawable.

AnimatedVectorDrawable are normally defined as 3 separate XML files.

First is the XML file for {@link android.graphics.drawable.VectorDrawable}. Note that we allow the animation happen on the group's attributes and path's attributes, which requires they are uniquely named in this xml file. Groups and paths without animations do not need names.

  • Here is a simple VectorDrawable in this vectordrawable.xml file.
    <vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="64dp"
    android:width="64dp"
    android:viewportHeight="600"
    android:viewportWidth="600" >
    <group
    android:name="rotationGroup"
    android:pivotX="300.0"
    android:pivotY="300.0"
    android:rotation="45.0" >
    <path
    android:name="v"
    android:fillColor="#000000"
    android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
    </group>
    </vector>
    
  • Second is the AnimatedVectorDrawable's xml file, which defines the target VectorDrawable, the target paths and groups to animate, the properties of the path and group to animate and the animations defined as the ObjectAnimators or AnimatorSets.

  • Here is a simple AnimatedVectorDrawable defined in this avd.xml file. Note how we use the names to refer to the groups and paths in the vectordrawable.xml.
    <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vectordrawable" >
    <target
    android:name="rotationGroup"
    android:animation="@anim/rotation" />
    <target
    android:name="v"
    android:animation="@anim/path_morph" />
    </animated-vector>
    
  • Last is the Animator xml file, which is the same as a normal ObjectAnimator or AnimatorSet. To complete this example, here are the 2 animator files used in avd.xml: rotation.xml and path_morph.xml.

  • Here is the rotation.xml, which will rotate the target group for 360 degrees.
    <objectAnimator
    android:duration="6000"
    android:propertyName="rotation"
    android:valueFrom="0"
    android:valueTo="360" />
    
  • Here is the path_morph.xml, which will morph the path from one shape to the other. Note that the paths must be compatible for morphing. In more details, the paths should have exact same length of commands , and exact same length of parameters for each commands. Note that the path string are better stored in strings.xml for reusing.
    <set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
    android:duration="3000"
    android:propertyName="pathData"
    android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z"
    android:valueTo="M300,70 l 0,-70 70,0 0,140 -70,0 z"
    android:valueType="pathType"/>
    </set>
    
  • attr
    ref android.R.styleable#AnimatedVectorDrawable_drawable
    attr
    ref android.R.styleable#AnimatedVectorDrawableTarget_name
    attr
    ref android.R.styleable#AnimatedVectorDrawableTarget_animation

    Fields Summary
    private static final String
    LOGTAG
    private static final String
    ANIMATED_VECTOR
    private static final String
    TARGET
    private static final boolean
    DBG_ANIMATION_VECTOR_DRAWABLE
    private AnimatedVectorDrawableState
    mAnimatedVectorState
    private boolean
    mMutated
    private final Callback
    mCallback
    Constructors Summary
    public AnimatedVectorDrawable()

    
          
            this(null, null);
        
    private AnimatedVectorDrawable(AnimatedVectorDrawableState state, android.content.res.Resources res)

            mAnimatedVectorState = new AnimatedVectorDrawableState(state, mCallback, res);
        
    Methods Summary
    public voidapplyTheme(android.content.res.Resources.Theme t)

            super.applyTheme(t);
    
            final VectorDrawable vectorDrawable = mAnimatedVectorState.mVectorDrawable;
            if (vectorDrawable != null && vectorDrawable.canApplyTheme()) {
                vectorDrawable.applyTheme(t);
            }
        
    public booleancanApplyTheme()

            return (mAnimatedVectorState != null && mAnimatedVectorState.canApplyTheme())
                    || super.canApplyTheme();
        
    public booleancanReverse()

    hide

            final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
            final int size = animators.size();
            for (int i = 0; i < size; i++) {
                final Animator animator = animators.get(i);
                if (!animator.canReverse()) {
                    return false;
                }
            }
            return true;
        
    public voidclearMutated()

    hide

            super.clearMutated();
            mAnimatedVectorState.mVectorDrawable.clearMutated();
            mMutated = false;
        
    public voiddraw(android.graphics.Canvas canvas)

            mAnimatedVectorState.mVectorDrawable.draw(canvas);
            if (isStarted()) {
                invalidateSelf();
            }
        
    public intgetAlpha()

            return mAnimatedVectorState.mVectorDrawable.getAlpha();
        
    public intgetChangingConfigurations()

            return super.getChangingConfigurations() | mAnimatedVectorState.mChangingConfigurations;
        
    public ConstantStategetConstantState()

            mAnimatedVectorState.mChangingConfigurations = getChangingConfigurations();
            return mAnimatedVectorState;
        
    public intgetIntrinsicHeight()

            return mAnimatedVectorState.mVectorDrawable.getIntrinsicHeight();
        
    public intgetIntrinsicWidth()

            return mAnimatedVectorState.mVectorDrawable.getIntrinsicWidth();
        
    public intgetOpacity()

            return mAnimatedVectorState.mVectorDrawable.getOpacity();
        
    public voidgetOutline(android.graphics.Outline outline)

            mAnimatedVectorState.mVectorDrawable.getOutline(outline);
        
    public voidinflate(android.content.res.Resources res, org.xmlpull.v1.XmlPullParser parser, android.util.AttributeSet attrs, android.content.res.Resources.Theme theme)

    
            int eventType = parser.getEventType();
            float pathErrorScale = 1;
            while (eventType != XmlPullParser.END_DOCUMENT) {
                if (eventType == XmlPullParser.START_TAG) {
                    final String tagName = parser.getName();
                    if (ANIMATED_VECTOR.equals(tagName)) {
                        final 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();
                            vectorDrawable.setAllowCaching(false);
                            vectorDrawable.setCallback(mCallback);
                            pathErrorScale = vectorDrawable.getPixelSize();
                            if (mAnimatedVectorState.mVectorDrawable != null) {
                                mAnimatedVectorState.mVectorDrawable.setCallback(null);
                            }
                            mAnimatedVectorState.mVectorDrawable = vectorDrawable;
                        }
                        a.recycle();
                    } else if (TARGET.equals(tagName)) {
                        final TypedArray a = obtainAttributes(res, theme, attrs,
                                R.styleable.AnimatedVectorDrawableTarget);
                        final String target = a.getString(
                                R.styleable.AnimatedVectorDrawableTarget_name);
    
                        int id = a.getResourceId(
                                R.styleable.AnimatedVectorDrawableTarget_animation, 0);
                        if (id != 0) {
                            Animator objectAnimator = AnimatorInflater.loadAnimator(res, theme, id,
                                    pathErrorScale);
                            setupAnimatorsForTarget(target, objectAnimator);
                        }
                        a.recycle();
                    }
                }
    
                eventType = parser.next();
            }
        
    public booleanisRunning()

            final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
            final int size = animators.size();
            for (int i = 0; i < size; i++) {
                final Animator animator = animators.get(i);
                if (animator.isRunning()) {
                    return true;
                }
            }
            return false;
        
    private booleanisStarted()

            final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
            final int size = animators.size();
            for (int i = 0; i < size; i++) {
                final Animator animator = animators.get(i);
                if (animator.isStarted()) {
                    return true;
                }
            }
            return false;
        
    public booleanisStateful()

            return mAnimatedVectorState.mVectorDrawable.isStateful();
        
    public Drawablemutate()

            if (!mMutated && super.mutate() == this) {
                mAnimatedVectorState = new AnimatedVectorDrawableState(
                        mAnimatedVectorState, mCallback, null);
                mMutated = true;
            }
            return this;
        
    protected voidonBoundsChange(android.graphics.Rect bounds)

            mAnimatedVectorState.mVectorDrawable.setBounds(bounds);
        
    protected booleanonLevelChange(int level)

            return mAnimatedVectorState.mVectorDrawable.setLevel(level);
        
    protected booleanonStateChange(int[] state)

            return mAnimatedVectorState.mVectorDrawable.setState(state);
        
    public voidreverse()
    Reverses ongoing animations or starts pending animations in reverse.

    NOTE: Only works if all animations support reverse. Otherwise, this will do nothing.

    hide

            // Only reverse when all the animators can be reverse. Otherwise, partially
            // reverse is confusing.
            if (!canReverse()) {
                Log.w(LOGTAG, "AnimatedVectorDrawable can't reverse()");
                return;
            }
            final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
            final int size = animators.size();
            for (int i = 0; i < size; i++) {
                final Animator animator = animators.get(i);
                animator.reverse();
            }
        
    public voidsetAlpha(int alpha)

            mAnimatedVectorState.mVectorDrawable.setAlpha(alpha);
        
    public voidsetColorFilter(android.graphics.ColorFilter colorFilter)

            mAnimatedVectorState.mVectorDrawable.setColorFilter(colorFilter);
        
    public voidsetHotspot(float x, float y)

            mAnimatedVectorState.mVectorDrawable.setHotspot(x, y);
        
    public voidsetHotspotBounds(int left, int top, int right, int bottom)

            mAnimatedVectorState.mVectorDrawable.setHotspotBounds(left, top, right, bottom);
        
    public voidsetLayoutDirection(int layoutDirection)
    {@hide}

            mAnimatedVectorState.mVectorDrawable.setLayoutDirection(layoutDirection);
        
    public voidsetTintList(android.content.res.ColorStateList tint)

            mAnimatedVectorState.mVectorDrawable.setTintList(tint);
        
    public voidsetTintMode(PorterDuff.Mode tintMode)

            mAnimatedVectorState.mVectorDrawable.setTintMode(tintMode);
        
    public booleansetVisible(boolean visible, boolean restart)

            mAnimatedVectorState.mVectorDrawable.setVisible(visible, restart);
            return super.setVisible(visible, restart);
        
    private voidsetupAnimatorsForTarget(java.lang.String name, android.animation.Animator animator)

            Object target = mAnimatedVectorState.mVectorDrawable.getTargetByName(name);
            animator.setTarget(target);
            if (mAnimatedVectorState.mAnimators == null) {
                mAnimatedVectorState.mAnimators = new ArrayList<Animator>();
                mAnimatedVectorState.mTargetNameMap = new ArrayMap<Animator, String>();
            }
            mAnimatedVectorState.mAnimators.add(animator);
            mAnimatedVectorState.mTargetNameMap.put(animator, name);
            if (DBG_ANIMATION_VECTOR_DRAWABLE) {
                Log.v(LOGTAG, "add animator  for target " + name + " " + animator);
            }
        
    public voidstart()

            // If any one of the animator has not ended, do nothing.
            if (isStarted()) {
                return;
            }
            // Otherwise, kick off every animator.
            final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
            final int size = animators.size();
            for (int i = 0; i < size; i++) {
                final Animator animator = animators.get(i);
                animator.start();
            }
            invalidateSelf();
        
    public voidstop()

            final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
            final int size = animators.size();
            for (int i = 0; i < size; i++) {
                final Animator animator = animators.get(i);
                animator.end();
            }