AnimatedVectorDrawable.java revision e5e92602a41a4ddc7b42cd1c171a0edfbd09b8da
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15package android.graphics.drawable; 16 17import android.animation.Animator; 18import android.animation.AnimatorInflater; 19import android.content.res.Resources; 20import android.content.res.Resources.Theme; 21import android.content.res.TypedArray; 22import android.graphics.Canvas; 23import android.graphics.ColorFilter; 24import android.graphics.Rect; 25import android.util.AttributeSet; 26import android.util.Log; 27 28import com.android.internal.R; 29 30import org.xmlpull.v1.XmlPullParser; 31import org.xmlpull.v1.XmlPullParserException; 32 33import java.io.IOException; 34import java.util.ArrayList; 35 36/** 37 * AnimatedVectorDrawable can use ObjectAnimator and AnimatorSet to animate 38 * the property of the VectorDrawable. 39 * 40 * @hide 41 */ 42public class AnimatedVectorDrawable extends Drawable implements Animatable { 43 private static final String LOGTAG = AnimatedVectorDrawable.class.getSimpleName(); 44 45 private static final String ANIMATED_VECTOR = "animated-vector"; 46 private static final String TARGET = "target"; 47 48 private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false; 49 50 private final AnimatedVectorDrawableState mAnimatedVectorState; 51 52 53 public AnimatedVectorDrawable() { 54 mAnimatedVectorState = new AnimatedVectorDrawableState( 55 new AnimatedVectorDrawableState(null)); 56 } 57 58 private AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res, 59 Theme theme) { 60 // TODO: Correctly handle the constant state for AVD. 61 mAnimatedVectorState = new AnimatedVectorDrawableState(state); 62 if (theme != null && canApplyTheme()) { 63 applyTheme(theme); 64 } 65 } 66 67 @Override 68 public ConstantState getConstantState() { 69 return null; 70 } 71 72 @Override 73 public void draw(Canvas canvas) { 74 mAnimatedVectorState.mVectorDrawable.draw(canvas); 75 if (isRunning()) { 76 invalidateSelf(); 77 } 78 } 79 80 @Override 81 protected void onBoundsChange(Rect bounds) { 82 mAnimatedVectorState.mVectorDrawable.setBounds(bounds); 83 } 84 85 @Override 86 public int getAlpha() { 87 return mAnimatedVectorState.mVectorDrawable.getAlpha(); 88 } 89 90 @Override 91 public void setAlpha(int alpha) { 92 mAnimatedVectorState.mVectorDrawable.setAlpha(alpha); 93 } 94 95 @Override 96 public void setColorFilter(ColorFilter colorFilter) { 97 mAnimatedVectorState.mVectorDrawable.setColorFilter(colorFilter); 98 } 99 100 @Override 101 public int getOpacity() { 102 return mAnimatedVectorState.mVectorDrawable.getOpacity(); 103 } 104 105 @Override 106 public int getIntrinsicWidth() { 107 return mAnimatedVectorState.mVectorDrawable.getIntrinsicWidth(); 108 } 109 110 @Override 111 public int getIntrinsicHeight() { 112 return mAnimatedVectorState.mVectorDrawable.getIntrinsicHeight(); 113 } 114 115 @Override 116 public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme) 117 throws XmlPullParserException, IOException { 118 119 int eventType = parser.getEventType(); 120 while (eventType != XmlPullParser.END_DOCUMENT) { 121 if (eventType == XmlPullParser.START_TAG) { 122 final String tagName = parser.getName(); 123 if (ANIMATED_VECTOR.equals(tagName)) { 124 final TypedArray a = obtainAttributes(res, theme, attrs, 125 R.styleable.AnimatedVectorDrawable); 126 int drawableRes = a.getResourceId( 127 R.styleable.AnimatedVectorDrawable_drawable, 0); 128 if (drawableRes != 0) { 129 mAnimatedVectorState.mVectorDrawable = (VectorDrawable) res.getDrawable( 130 drawableRes); 131 } 132 a.recycle(); 133 } else if (TARGET.equals(tagName)) { 134 final TypedArray a = obtainAttributes(res, theme, attrs, 135 R.styleable.AnimatedVectorDrawableTarget); 136 final String target = a.getString( 137 R.styleable.AnimatedVectorDrawableTarget_name); 138 139 int id = a.getResourceId( 140 R.styleable.AnimatedVectorDrawableTarget_animation, 0); 141 if (id != 0) { 142 Animator objectAnimator = AnimatorInflater.loadAnimator(res, theme, id); 143 setupAnimatorsForTarget(target, objectAnimator); 144 } 145 a.recycle(); 146 } 147 } 148 149 eventType = parser.next(); 150 } 151 } 152 153 @Override 154 public boolean canApplyTheme() { 155 return super.canApplyTheme() || mAnimatedVectorState != null 156 && mAnimatedVectorState.canApplyTheme(); 157 } 158 159 @Override 160 public void applyTheme(Theme t) { 161 super.applyTheme(t); 162 163 final VectorDrawable vectorDrawable = mAnimatedVectorState.mVectorDrawable; 164 if (vectorDrawable != null && vectorDrawable.canApplyTheme()) { 165 vectorDrawable.applyTheme(t); 166 } 167 } 168 169 private static class AnimatedVectorDrawableState extends ConstantState { 170 int mChangingConfigurations; 171 VectorDrawable mVectorDrawable; 172 ArrayList<Animator> mAnimators; 173 174 public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy) { 175 if (copy != null) { 176 mChangingConfigurations = copy.mChangingConfigurations; 177 // TODO: Make sure the constant state are handled correctly. 178 mVectorDrawable = new VectorDrawable(); 179 mAnimators = new ArrayList<Animator>(); 180 } 181 } 182 183 @Override 184 public Drawable newDrawable() { 185 return new AnimatedVectorDrawable(this, null, null); 186 } 187 188 @Override 189 public Drawable newDrawable(Resources res) { 190 return new AnimatedVectorDrawable(this, res, null); 191 } 192 193 @Override 194 public Drawable newDrawable(Resources res, Theme theme) { 195 return new AnimatedVectorDrawable(this, res, theme); 196 } 197 198 @Override 199 public int getChangingConfigurations() { 200 return mChangingConfigurations; 201 } 202 } 203 204 private void setupAnimatorsForTarget(String name, Animator animator) { 205 Object target = mAnimatedVectorState.mVectorDrawable.getTargetByName(name); 206 animator.setTarget(target); 207 mAnimatedVectorState.mAnimators.add(animator); 208 if (DBG_ANIMATION_VECTOR_DRAWABLE) { 209 Log.v(LOGTAG, "add animator for target " + name + " " + animator); 210 } 211 } 212 213 @Override 214 public boolean isRunning() { 215 final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators; 216 final int size = animators.size(); 217 for (int i = 0; i < size; i++) { 218 final Animator animator = animators.get(i); 219 if (animator.isRunning()) { 220 return true; 221 } 222 } 223 return false; 224 } 225 226 @Override 227 public void start() { 228 final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators; 229 final int size = animators.size(); 230 for (int i = 0; i < size; i++) { 231 final Animator animator = animators.get(i); 232 if (animator.isPaused()) { 233 animator.resume(); 234 } else if (!animator.isRunning()) { 235 animator.start(); 236 } 237 } 238 invalidateSelf(); 239 } 240 241 @Override 242 public void stop() { 243 final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators; 244 final int size = animators.size(); 245 for (int i = 0; i < size; i++) { 246 final Animator animator = animators.get(i); 247 animator.pause(); 248 } 249 } 250} 251