AnimatedVectorDrawableCompat.java revision 408cb9f78c1bedffac5fabbc59c8cbf1932d3610
1/* 2 * Copyright (C) 2015 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.support.graphics.drawable; 16 17import android.animation.Animator; 18import android.animation.AnimatorInflater; 19import android.animation.AnimatorSet; 20import android.animation.ArgbEvaluator; 21import android.animation.ObjectAnimator; 22import android.annotation.TargetApi; 23import android.content.Context; 24import android.content.res.ColorStateList; 25import android.content.res.Resources; 26import android.content.res.Resources.Theme; 27import android.content.res.TypedArray; 28import android.graphics.Canvas; 29import android.graphics.ColorFilter; 30import android.graphics.PorterDuff; 31import android.graphics.Rect; 32import android.graphics.drawable.Animatable; 33import android.graphics.drawable.AnimatedVectorDrawable; 34import android.graphics.drawable.Drawable; 35import android.os.Build; 36import android.support.annotation.DrawableRes; 37import android.support.annotation.NonNull; 38import android.support.annotation.Nullable; 39import android.support.v4.content.res.ResourcesCompat; 40import android.support.v4.graphics.drawable.DrawableCompat; 41import android.support.v4.util.ArrayMap; 42import android.util.AttributeSet; 43import android.util.Log; 44import android.util.Xml; 45import org.xmlpull.v1.XmlPullParser; 46import org.xmlpull.v1.XmlPullParserException; 47 48import java.io.IOException; 49import java.util.ArrayList; 50import java.util.List; 51 52/** 53 * This class uses {@link android.animation.ObjectAnimator} and 54 * {@link android.animation.AnimatorSet} to animate the properties of a 55 * {@link VectorDrawableCompat} to create an animated drawable. 56 * <p> 57 * AnimatedVectorDrawableCompat are normally defined as 3 separate XML files. 58 * </p> 59 * <p> 60 * First is the XML file for {@link VectorDrawableCompat}. Note that we 61 * allow the animation to happen on the group's attributes and path's attributes, which requires they 62 * are uniquely named in this XML file. Groups and paths without animations do not need names. 63 * </p> 64 * <li>Here is a simple VectorDrawable in this vectordrawable.xml file. 65 * <pre> 66 * <vector xmlns:android="http://schemas.android.com/apk/res/android" 67 * android:height="64dp" 68 * android:width="64dp" 69 * android:viewportHeight="600" 70 * android:viewportWidth="600" > 71 * <group 72 * android:name="rotationGroup" 73 * android:pivotX="300.0" 74 * android:pivotY="300.0" 75 * android:rotation="45.0" > 76 * <path 77 * android:name="v" 78 * android:fillColor="#000000" 79 * android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" /> 80 * </group> 81 * </vector> 82 * </pre></li> 83 * <p> 84 * Second is the AnimatedVectorDrawableCompat's XML file, which defines the target 85 * VectorDrawableCompat, the target paths and groups to animate, the properties of the path and 86 * group to animate and the animations defined as the ObjectAnimators or AnimatorSets. 87 * </p> 88 * <li>Here is a simple AnimatedVectorDrawable defined in this avd.xml file. 89 * Note how we use the names to refer to the groups and paths in the vectordrawable.xml. 90 * <pre> 91 * <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" 92 * android:drawable="@drawable/vectordrawable" > 93 * <target 94 * android:name="rotationGroup" 95 * android:animation="@anim/rotation" /> 96 * <target 97 * android:name="v" 98 * android:animation="@anim/path_morph" /> 99 * </animated-vector> 100 * </pre></li> 101 * <p> 102 * Last is the Animator XML file, which is the same as a normal ObjectAnimator or AnimatorSet. To 103 * complete this example, here are the 2 animator files used in avd.xml: rotation.xml and 104 * path_morph.xml. 105 * </p> 106 * <li>Here is the rotation.xml, which will rotate the target group for 360 degrees. 107 * <pre> 108 * <objectAnimator 109 * android:duration="6000" 110 * android:propertyName="rotation" 111 * android:valueFrom="0" 112 * android:valueTo="360" /> 113 * </pre></li> 114 * <li>Here is the path_morph.xml, which will morph the path from one shape to 115 * the other. Note that the paths must be compatible for morphing. 116 * In more details, the paths should have exact same length of commands, and 117 * exact same length of parameters for each commands. 118 * Note that the path strings are better stored in strings.xml for reusing. 119 * <pre> 120 * <set xmlns:android="http://schemas.android.com/apk/res/android"> 121 * <objectAnimator 122 * android:duration="3000" 123 * android:propertyName="pathData" 124 * android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z" 125 * android:valueTo="M300,70 l 0,-70 70,0 0,140 -70,0 z" 126 * android:valueType="pathType"/> 127 * </set> 128 * </pre></li> 129 * 130 * @attr ref android.R.styleable#AnimatedVectorDrawableCompat_drawable 131 * @attr ref android.R.styleable#AnimatedVectorDrawableCompatTarget_name 132 * @attr ref android.R.styleable#AnimatedVectorDrawableCompatTarget_animation 133 */ 134@TargetApi(Build.VERSION_CODES.LOLLIPOP) 135public class AnimatedVectorDrawableCompat extends VectorDrawableCommon implements Animatable { 136 private static final String LOGTAG = "AnimatedVDCompat"; 137 138 private static final String ANIMATED_VECTOR = "animated-vector"; 139 private static final String TARGET = "target"; 140 141 private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false; 142 143 private AnimatedVectorDrawableCompatState mAnimatedVectorState; 144 145 private Context mContext; 146 147 private ArgbEvaluator mArgbEvaluator = null; 148 149 AnimatedVectorDrawableDelegateState mCachedConstantStateDelegate; 150 151 private AnimatedVectorDrawableCompat() { 152 this(null, null, null); 153 } 154 155 private AnimatedVectorDrawableCompat(@Nullable Context context) { 156 this(context, null, null); 157 } 158 159 private AnimatedVectorDrawableCompat(@Nullable Context context, 160 @Nullable AnimatedVectorDrawableCompatState state, 161 @Nullable Resources res) { 162 mContext = context; 163 if (state != null) { 164 mAnimatedVectorState = state; 165 } else { 166 mAnimatedVectorState = new AnimatedVectorDrawableCompatState(context, state, mCallback, 167 res); 168 } 169 } 170 171 @Override 172 public Drawable mutate() { 173 if (mDelegateDrawable != null) { 174 mDelegateDrawable.mutate(); 175 return this; 176 } 177 throw new IllegalStateException("Mutate() is not supported for older platform"); 178 } 179 180 181 /** 182 * Create a AnimatedVectorDrawableCompat object. 183 * 184 * @param context the context for creating the animators. 185 * @param resId the resource ID for AnimatedVectorDrawableCompat object. 186 * @return a new AnimatedVectorDrawableCompat or null if parsing error is found. 187 */ 188 @Nullable 189 public static AnimatedVectorDrawableCompat create(@NonNull Context context, 190 @DrawableRes int resId) { 191 if (Build.VERSION.SDK_INT >= 21) { 192 final AnimatedVectorDrawableCompat drawable = new AnimatedVectorDrawableCompat(context); 193 drawable.mDelegateDrawable = ResourcesCompat.getDrawable(context.getResources(), resId, 194 context.getTheme()); 195 drawable.mDelegateDrawable.setCallback(drawable.mCallback); 196 drawable.mCachedConstantStateDelegate = new AnimatedVectorDrawableDelegateState( 197 drawable.mDelegateDrawable.getConstantState()); 198 return drawable; 199 } 200 Resources resources = context.getResources(); 201 try { 202 final XmlPullParser parser = resources.getXml(resId); 203 final AttributeSet attrs = Xml.asAttributeSet(parser); 204 int type; 205 while ((type = parser.next()) != XmlPullParser.START_TAG 206 && type != XmlPullParser.END_DOCUMENT) { 207 // Empty loop 208 } 209 if (type != XmlPullParser.START_TAG) { 210 throw new XmlPullParserException("No start tag found"); 211 } 212 return createFromXmlInner(context, context.getResources(), parser, attrs, 213 context.getTheme()); 214 } catch (XmlPullParserException e) { 215 Log.e(LOGTAG, "parser error", e); 216 } catch (IOException e) { 217 Log.e(LOGTAG, "parser error", e); 218 } 219 return null; 220 } 221 222 /** 223 * Create a AnimatedVectorDrawableCompat from inside an XML document using an optional 224 * {@link Theme}. Called on a parser positioned at a tag in an XML 225 * document, tries to create a Drawable from that tag. Returns {@code null} 226 * if the tag is not a valid drawable. 227 */ 228 public static AnimatedVectorDrawableCompat createFromXmlInner(Context context, Resources r, 229 XmlPullParser parser, AttributeSet attrs, Theme theme) 230 throws XmlPullParserException, IOException { 231 final AnimatedVectorDrawableCompat drawable = new AnimatedVectorDrawableCompat(context); 232 drawable.inflate(r, parser, attrs, theme); 233 return drawable; 234 } 235 236 /** 237 * {@inheritDoc} 238 * <strong>Note</strong> that we don't support constant state when SDK < 21. 239 * Make sure you check the return value before using it. 240 */ 241 @Override 242 public ConstantState getConstantState() { 243 if (mDelegateDrawable != null) { 244 return new AnimatedVectorDrawableDelegateState(mDelegateDrawable.getConstantState()); 245 } 246 // We can't support constant state in older platform. 247 // We need Context to create the animator, and we can't save the context in the constant 248 // state. 249 return null; 250 } 251 252 @Override 253 public int getChangingConfigurations() { 254 if (mDelegateDrawable != null) { 255 return mDelegateDrawable.getChangingConfigurations(); 256 } 257 return super.getChangingConfigurations() | mAnimatedVectorState.mChangingConfigurations; 258 } 259 260 @Override 261 public void draw(Canvas canvas) { 262 if (mDelegateDrawable != null) { 263 mDelegateDrawable.draw(canvas); 264 return; 265 } 266 mAnimatedVectorState.mVectorDrawable.draw(canvas); 267 if (isStarted()) { 268 invalidateSelf(); 269 } 270 } 271 272 @Override 273 protected void onBoundsChange(Rect bounds) { 274 if (mDelegateDrawable != null) { 275 mDelegateDrawable.setBounds(bounds); 276 return; 277 } 278 mAnimatedVectorState.mVectorDrawable.setBounds(bounds); 279 } 280 281 @Override 282 protected boolean onStateChange(int[] state) { 283 if (mDelegateDrawable != null) { 284 return mDelegateDrawable.setState(state); 285 } 286 return mAnimatedVectorState.mVectorDrawable.setState(state); 287 } 288 289 @Override 290 protected boolean onLevelChange(int level) { 291 if (mDelegateDrawable != null) { 292 return mDelegateDrawable.setLevel(level); 293 } 294 return mAnimatedVectorState.mVectorDrawable.setLevel(level); 295 } 296 297 @Override 298 public int getAlpha() { 299 if (mDelegateDrawable != null) { 300 return DrawableCompat.getAlpha(mDelegateDrawable); 301 } 302 return mAnimatedVectorState.mVectorDrawable.getAlpha(); 303 } 304 305 @Override 306 public void setAlpha(int alpha) { 307 if (mDelegateDrawable != null) { 308 mDelegateDrawable.setAlpha(alpha); 309 return; 310 } 311 mAnimatedVectorState.mVectorDrawable.setAlpha(alpha); 312 } 313 314 @Override 315 public void setColorFilter(ColorFilter colorFilter) { 316 if (mDelegateDrawable != null) { 317 mDelegateDrawable.setColorFilter(colorFilter); 318 return; 319 } 320 mAnimatedVectorState.mVectorDrawable.setColorFilter(colorFilter); 321 } 322 323 public void setTint(int tint) { 324 if (mDelegateDrawable != null) { 325 DrawableCompat.setTint(mDelegateDrawable, tint); 326 return; 327 } 328 329 mAnimatedVectorState.mVectorDrawable.setTint(tint); 330 } 331 332 public void setTintList(ColorStateList tint) { 333 if (mDelegateDrawable != null) { 334 DrawableCompat.setTintList(mDelegateDrawable, tint); 335 return; 336 } 337 338 mAnimatedVectorState.mVectorDrawable.setTintList(tint); 339 } 340 341 public void setTintMode(PorterDuff.Mode tintMode) { 342 if (mDelegateDrawable != null) { 343 DrawableCompat.setTintMode(mDelegateDrawable, tintMode); 344 return; 345 } 346 347 mAnimatedVectorState.mVectorDrawable.setTintMode(tintMode); 348 } 349 350 @Override 351 public boolean setVisible(boolean visible, boolean restart) { 352 if (mDelegateDrawable != null) { 353 return mDelegateDrawable.setVisible(visible, restart); 354 } 355 mAnimatedVectorState.mVectorDrawable.setVisible(visible, restart); 356 return super.setVisible(visible, restart); 357 } 358 359 @Override 360 public boolean isStateful() { 361 if (mDelegateDrawable != null) { 362 return mDelegateDrawable.isStateful(); 363 } 364 return mAnimatedVectorState.mVectorDrawable.isStateful(); 365 } 366 367 @Override 368 public int getOpacity() { 369 if (mDelegateDrawable != null) { 370 return mDelegateDrawable.getOpacity(); 371 } 372 return mAnimatedVectorState.mVectorDrawable.getOpacity(); 373 } 374 375 public int getIntrinsicWidth() { 376 if (mDelegateDrawable != null) { 377 return mDelegateDrawable.getIntrinsicWidth(); 378 } 379 return mAnimatedVectorState.mVectorDrawable.getIntrinsicWidth(); 380 } 381 382 public int getIntrinsicHeight() { 383 if (mDelegateDrawable != null) { 384 return mDelegateDrawable.getIntrinsicHeight(); 385 } 386 return mAnimatedVectorState.mVectorDrawable.getIntrinsicHeight(); 387 } 388 389 /** 390 * Obtains styled attributes from the theme, if available, or unstyled 391 * resources if the theme is null. 392 */ 393 static TypedArray obtainAttributes( 394 Resources res, Theme theme, AttributeSet set, int[] attrs) { 395 if (theme == null) { 396 return res.obtainAttributes(set, attrs); 397 } 398 return theme.obtainStyledAttributes(set, attrs, 0, 0); 399 } 400 401 @Override 402 public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme) 403 throws XmlPullParserException, IOException { 404 if (mDelegateDrawable != null) { 405 DrawableCompat.inflate(mDelegateDrawable, res, parser, attrs, theme); 406 return; 407 } 408 int eventType = parser.getEventType(); 409 while (eventType != XmlPullParser.END_DOCUMENT) { 410 if (eventType == XmlPullParser.START_TAG) { 411 final String tagName = parser.getName(); 412 if (DBG_ANIMATION_VECTOR_DRAWABLE) { 413 Log.v(LOGTAG, "tagName is " + tagName); 414 } 415 if (ANIMATED_VECTOR.equals(tagName)) { 416 final TypedArray a = 417 obtainAttributes(res, theme, attrs, 418 AndroidResources.styleable_AnimatedVectorDrawable); 419 420 int drawableRes = a.getResourceId( 421 AndroidResources.styleable_AnimatedVectorDrawable_drawable, 0); 422 if (DBG_ANIMATION_VECTOR_DRAWABLE) { 423 Log.v(LOGTAG, "drawableRes is " + drawableRes); 424 } 425 if (drawableRes != 0) { 426 VectorDrawableCompat vectorDrawable = VectorDrawableCompat.create(res, 427 drawableRes, theme); 428 vectorDrawable.setAllowCaching(false); 429 vectorDrawable.setCallback(mCallback); 430 if (mAnimatedVectorState.mVectorDrawable != null) { 431 mAnimatedVectorState.mVectorDrawable.setCallback(null); 432 } 433 mAnimatedVectorState.mVectorDrawable = vectorDrawable; 434 } 435 a.recycle(); 436 } else if (TARGET.equals(tagName)) { 437 final TypedArray a = 438 res.obtainAttributes(attrs, 439 AndroidResources.styleable_AnimatedVectorDrawableTarget); 440 final String target = a.getString( 441 AndroidResources.styleable_AnimatedVectorDrawableTarget_name); 442 443 int id = a.getResourceId( 444 AndroidResources.styleable_AnimatedVectorDrawableTarget_animation, 0); 445 if (id != 0) { 446 if (mContext != null) { 447 Animator objectAnimator = AnimatorInflater.loadAnimator(mContext, id); 448 setupAnimatorsForTarget(target, objectAnimator); 449 } else { 450 throw new IllegalStateException("Context can't be null when inflating" + 451 " animators"); 452 } 453 } 454 a.recycle(); 455 } 456 } 457 458 eventType = parser.next(); 459 } 460 } 461 462 @Override 463 public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs) 464 throws XmlPullParserException, IOException { 465 inflate(res, parser, attrs, null); 466 } 467 468 @Override 469 public void applyTheme(Theme t) { 470 if (mDelegateDrawable != null) { 471 DrawableCompat.applyTheme(mDelegateDrawable, t); 472 return; 473 } 474 // TODO: support theming in older platform. 475 return; 476 } 477 478 public boolean canApplyTheme() { 479 if (mDelegateDrawable != null) { 480 return DrawableCompat.canApplyTheme(mDelegateDrawable); 481 } 482 // TODO: support theming in older platform. 483 return false; 484 } 485 486 /** 487 * Constant state for delegating the creating drawable job. 488 * Instead of creating a VectorDrawable, create a VectorDrawableCompat instance which contains 489 * a delegated VectorDrawable instance. 490 */ 491 private static class AnimatedVectorDrawableDelegateState extends ConstantState { 492 private final ConstantState mDelegateState; 493 494 public AnimatedVectorDrawableDelegateState(ConstantState state) { 495 mDelegateState = state; 496 } 497 498 @Override 499 public Drawable newDrawable() { 500 AnimatedVectorDrawableCompat drawableCompat = 501 new AnimatedVectorDrawableCompat(); 502 drawableCompat.mDelegateDrawable = mDelegateState.newDrawable(); 503 drawableCompat.mDelegateDrawable.setCallback(drawableCompat.mCallback); 504 return drawableCompat; 505 } 506 507 @Override 508 public Drawable newDrawable(Resources res) { 509 AnimatedVectorDrawableCompat drawableCompat = 510 new AnimatedVectorDrawableCompat(); 511 drawableCompat.mDelegateDrawable = mDelegateState.newDrawable(res); 512 drawableCompat.mDelegateDrawable.setCallback(drawableCompat.mCallback); 513 return drawableCompat; 514 } 515 516 @Override 517 public Drawable newDrawable(Resources res, Theme theme) { 518 AnimatedVectorDrawableCompat drawableCompat = 519 new AnimatedVectorDrawableCompat(); 520 drawableCompat.mDelegateDrawable = mDelegateState.newDrawable(res, theme); 521 drawableCompat.mDelegateDrawable.setCallback(drawableCompat.mCallback); 522 return drawableCompat; 523 } 524 525 @Override 526 public boolean canApplyTheme() { 527 return mDelegateState.canApplyTheme(); 528 } 529 530 @Override 531 public int getChangingConfigurations() { 532 return mDelegateState.getChangingConfigurations(); 533 } 534 } 535 536 private static class AnimatedVectorDrawableCompatState extends ConstantState { 537 int mChangingConfigurations; 538 VectorDrawableCompat mVectorDrawable; 539 ArrayList<Animator> mAnimators; 540 ArrayMap<Animator, String> mTargetNameMap; 541 542 public AnimatedVectorDrawableCompatState(Context context, 543 AnimatedVectorDrawableCompatState copy, Callback owner, Resources res) { 544 if (copy != null) { 545 mChangingConfigurations = copy.mChangingConfigurations; 546 if (copy.mVectorDrawable != null) { 547 final ConstantState cs = copy.mVectorDrawable.getConstantState(); 548 if (res != null) { 549 mVectorDrawable = (VectorDrawableCompat) cs.newDrawable(res); 550 } else { 551 mVectorDrawable = (VectorDrawableCompat) cs.newDrawable(); 552 } 553 mVectorDrawable = (VectorDrawableCompat) mVectorDrawable.mutate(); 554 mVectorDrawable.setCallback(owner); 555 mVectorDrawable.setBounds(copy.mVectorDrawable.getBounds()); 556 mVectorDrawable.setAllowCaching(false); 557 } 558 if (copy.mAnimators != null) { 559 final int numAnimators = copy.mAnimators.size(); 560 mAnimators = new ArrayList<Animator>(numAnimators); 561 mTargetNameMap = new ArrayMap<Animator, String>(numAnimators); 562 for (int i = 0; i < numAnimators; ++i) { 563 Animator anim = copy.mAnimators.get(i); 564 Animator animClone = anim.clone(); 565 String targetName = copy.mTargetNameMap.get(anim); 566 Object targetObject = mVectorDrawable.getTargetByName(targetName); 567 animClone.setTarget(targetObject); 568 mAnimators.add(animClone); 569 mTargetNameMap.put(animClone, targetName); 570 } 571 } 572 } 573 } 574 575 @Override 576 public Drawable newDrawable() { 577 throw new IllegalStateException("No constant state support for SDK < 21."); 578 } 579 580 @Override 581 public Drawable newDrawable(Resources res) { 582 throw new IllegalStateException("No constant state support for SDK < 21."); 583 } 584 585 @Override 586 public int getChangingConfigurations() { 587 return mChangingConfigurations; 588 } 589 } 590 591 /** 592 * Utility function to fix color interpolation prior to Lollipop. Without this fix, colors 593 * are evaluated as raw integers instead of as colors, which leads to artifacts during 594 * fillColor animations. 595 */ 596 private void setupColorAnimator(Animator animator) { 597 if (animator instanceof AnimatorSet) { 598 List<Animator> childAnimators = ((AnimatorSet) animator).getChildAnimations(); 599 if (childAnimators != null) { 600 for (int i = 0; i < childAnimators.size(); ++i) { 601 setupColorAnimator(childAnimators.get(i)); 602 } 603 } 604 } 605 if (animator instanceof ObjectAnimator) { 606 ObjectAnimator objectAnim = (ObjectAnimator) animator; 607 final String propertyName = objectAnim.getPropertyName(); 608 if ("fillColor".equals(propertyName) || "strokeColor".equals(propertyName)) { 609 if (mArgbEvaluator == null) { 610 mArgbEvaluator = new ArgbEvaluator(); 611 } 612 objectAnim.setEvaluator(mArgbEvaluator); 613 } 614 } 615 } 616 617 private void setupAnimatorsForTarget(String name, Animator animator) { 618 Object target = mAnimatedVectorState.mVectorDrawable.getTargetByName(name); 619 animator.setTarget(target); 620 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { 621 setupColorAnimator(animator); 622 } 623 if (mAnimatedVectorState.mAnimators == null) { 624 mAnimatedVectorState.mAnimators = new ArrayList<Animator>(); 625 mAnimatedVectorState.mTargetNameMap = new ArrayMap<Animator, String>(); 626 } 627 mAnimatedVectorState.mAnimators.add(animator); 628 mAnimatedVectorState.mTargetNameMap.put(animator, name); 629 if (DBG_ANIMATION_VECTOR_DRAWABLE) { 630 Log.v(LOGTAG, "add animator for target " + name + " " + animator); 631 } 632 } 633 634 @Override 635 public boolean isRunning() { 636 if (mDelegateDrawable != null) { 637 return ((AnimatedVectorDrawable) mDelegateDrawable).isRunning(); 638 } 639 final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators; 640 final int size = animators.size(); 641 for (int i = 0; i < size; i++) { 642 final Animator animator = animators.get(i); 643 if (animator.isRunning()) { 644 return true; 645 } 646 } 647 return false; 648 } 649 650 private boolean isStarted() { 651 final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators; 652 if (animators == null) { 653 return false; 654 } 655 final int size = animators.size(); 656 for (int i = 0; i < size; i++) { 657 final Animator animator = animators.get(i); 658 if (animator.isRunning()) { 659 return true; 660 } 661 } 662 return false; 663 } 664 665 @Override 666 public void start() { 667 if (mDelegateDrawable != null) { 668 ((AnimatedVectorDrawable) mDelegateDrawable).start(); 669 return; 670 } 671 // If any one of the animator has not ended, do nothing. 672 if (isStarted()) { 673 return; 674 } 675 // Otherwise, kick off every animator. 676 final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators; 677 final int size = animators.size(); 678 for (int i = 0; i < size; i++) { 679 final Animator animator = animators.get(i); 680 animator.start(); 681 } 682 invalidateSelf(); 683 } 684 685 @Override 686 public void stop() { 687 if (mDelegateDrawable != null) { 688 ((AnimatedVectorDrawable) mDelegateDrawable).stop(); 689 return; 690 } 691 final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators; 692 final int size = animators.size(); 693 for (int i = 0; i < size; i++) { 694 final Animator animator = animators.get(i); 695 animator.end(); 696 } 697 } 698 699 private final Callback mCallback = new Callback() { 700 @Override 701 public void invalidateDrawable(Drawable who) { 702 invalidateSelf(); 703 } 704 705 @Override 706 public void scheduleDrawable(Drawable who, Runnable what, long when) { 707 scheduleSelf(what, when); 708 } 709 710 @Override 711 public void unscheduleDrawable(Drawable who, Runnable what) { 712 unscheduleSelf(what); 713 } 714 }; 715} 716