ValueAnimator.java revision 2b2e2c8d252b33fa25ccba1e37322256cd44b3d5
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.animation; 18 19import android.annotation.CallSuper; 20import android.os.Looper; 21import android.os.Trace; 22import android.util.AndroidRuntimeException; 23import android.util.Log; 24import android.view.animation.AccelerateDecelerateInterpolator; 25import android.view.animation.AnimationUtils; 26import android.view.animation.LinearInterpolator; 27 28import java.util.ArrayList; 29import java.util.HashMap; 30 31/** 32 * This class provides a simple timing engine for running animations 33 * which calculate animated values and set them on target objects. 34 * 35 * <p>There is a single timing pulse that all animations use. It runs in a 36 * custom handler to ensure that property changes happen on the UI thread.</p> 37 * 38 * <p>By default, ValueAnimator uses non-linear time interpolation, via the 39 * {@link AccelerateDecelerateInterpolator} class, which accelerates into and decelerates 40 * out of an animation. This behavior can be changed by calling 41 * {@link ValueAnimator#setInterpolator(TimeInterpolator)}.</p> 42 * 43 * <p>Animators can be created from either code or resource files. Here is an example 44 * of a ValueAnimator resource file:</p> 45 * 46 * {@sample development/samples/ApiDemos/res/anim/animator.xml ValueAnimatorResources} 47 * 48 * <p>It is also possible to use a combination of {@link PropertyValuesHolder} and 49 * {@link Keyframe} resource tags to create a multi-step animation. 50 * Note that you can specify explicit fractional values (from 0 to 1) for 51 * each keyframe to determine when, in the overall duration, the animation should arrive at that 52 * value. Alternatively, you can leave the fractions off and the keyframes will be equally 53 * distributed within the total duration:</p> 54 * 55 * {@sample development/samples/ApiDemos/res/anim/value_animator_pvh_kf.xml 56 * ValueAnimatorKeyframeResources} 57 * 58 * <div class="special reference"> 59 * <h3>Developer Guides</h3> 60 * <p>For more information about animating with {@code ValueAnimator}, read the 61 * <a href="{@docRoot}guide/topics/graphics/prop-animation.html#value-animator">Property 62 * Animation</a> developer guide.</p> 63 * </div> 64 */ 65@SuppressWarnings("unchecked") 66public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback { 67 private static final String TAG = "ValueAnimator"; 68 private static final boolean DEBUG = false; 69 70 /** 71 * Internal constants 72 */ 73 private static float sDurationScale = 1.0f; 74 75 /** 76 * Internal variables 77 * NOTE: This object implements the clone() method, making a deep copy of any referenced 78 * objects. As other non-trivial fields are added to this class, make sure to add logic 79 * to clone() to make deep copies of them. 80 */ 81 82 /** 83 * The first time that the animation's animateFrame() method is called. This time is used to 84 * determine elapsed time (and therefore the elapsed fraction) in subsequent calls 85 * to animateFrame(). 86 * 87 * Whenever mStartTime is set, you must also update mStartTimeCommitted. 88 */ 89 long mStartTime; 90 91 /** 92 * When true, the start time has been firmly committed as a chosen reference point in 93 * time by which the progress of the animation will be evaluated. When false, the 94 * start time may be updated when the first animation frame is committed so as 95 * to compensate for jank that may have occurred between when the start time was 96 * initialized and when the frame was actually drawn. 97 * 98 * This flag is generally set to false during the first frame of the animation 99 * when the animation playing state transitions from STOPPED to RUNNING or 100 * resumes after having been paused. This flag is set to true when the start time 101 * is firmly committed and should not be further compensated for jank. 102 */ 103 boolean mStartTimeCommitted; 104 105 /** 106 * Set when setCurrentPlayTime() is called. If negative, animation is not currently seeked 107 * to a value. 108 */ 109 float mSeekFraction = -1; 110 111 /** 112 * Set on the next frame after pause() is called, used to calculate a new startTime 113 * or delayStartTime which allows the animator to continue from the point at which 114 * it was paused. If negative, has not yet been set. 115 */ 116 private long mPauseTime; 117 118 /** 119 * Set when an animator is resumed. This triggers logic in the next frame which 120 * actually resumes the animator. 121 */ 122 private boolean mResumed = false; 123 124 // The time interpolator to be used if none is set on the animation 125 private static final TimeInterpolator sDefaultInterpolator = 126 new AccelerateDecelerateInterpolator(); 127 128 /** 129 * Used to indicate whether the animation is currently playing in reverse. This causes the 130 * elapsed fraction to be inverted to calculate the appropriate values. 131 */ 132 private boolean mPlayingBackwards = false; 133 134 /** 135 * Flag to indicate whether this animator is playing in reverse mode, specifically 136 * by being started or interrupted by a call to reverse(). This flag is different than 137 * mPlayingBackwards, which indicates merely whether the current iteration of the 138 * animator is playing in reverse. It is used in corner cases to determine proper end 139 * behavior. 140 */ 141 private boolean mReversing; 142 143 /** 144 * This variable tracks the current iteration that is playing. When mCurrentIteration exceeds the 145 * repeatCount (if repeatCount!=INFINITE), the animation ends 146 */ 147 private int mCurrentIteration = 0; 148 149 /** 150 * Tracks current elapsed/eased fraction, for querying in getAnimatedFraction(). 151 */ 152 private float mCurrentFraction = 0f; 153 154 /** 155 * Tracks the time (in milliseconds) when the last frame arrived. 156 */ 157 private long mLastFrameTime = 0; 158 159 /** 160 * Additional playing state to indicate whether an animator has been start()'d. There is 161 * some lag between a call to start() and the first animation frame. We should still note 162 * that the animation has been started, even if it's first animation frame has not yet 163 * happened, and reflect that state in isRunning(). 164 * Note that delayed animations are different: they are not started until their first 165 * animation frame, which occurs after their delay elapses. 166 */ 167 private boolean mRunning = false; 168 169 /** 170 * Additional playing state to indicate whether an animator has been start()'d, whether or 171 * not there is a nonzero startDelay. 172 */ 173 private boolean mStarted = false; 174 175 /** 176 * Tracks whether we've notified listeners of the onAnimationStart() event. This can be 177 * complex to keep track of since we notify listeners at different times depending on 178 * startDelay and whether start() was called before end(). 179 */ 180 private boolean mStartListenersCalled = false; 181 182 /** 183 * Flag that denotes whether the animation is set up and ready to go. Used to 184 * set up animation that has not yet been started. 185 */ 186 boolean mInitialized = false; 187 188 /** 189 * Flag that tracks whether animation has been requested to end. 190 */ 191 private boolean mAnimationEndRequested = false; 192 193 // 194 // Backing variables 195 // 196 197 // How long the animation should last in ms 198 private long mUnscaledDuration = 300; 199 private long mDuration = (long)(mUnscaledDuration * sDurationScale); 200 201 // The amount of time in ms to delay starting the animation after start() is called 202 long mStartDelay = 0; 203 private long mUnscaledStartDelay = 0; 204 205 // The number of times the animation will repeat. The default is 0, which means the animation 206 // will play only once 207 private int mRepeatCount = 0; 208 209 /** 210 * The type of repetition that will occur when repeatMode is nonzero. RESTART means the 211 * animation will start from the beginning on every new cycle. REVERSE means the animation 212 * will reverse directions on each iteration. 213 */ 214 private int mRepeatMode = RESTART; 215 216 /** 217 * The time interpolator to be used. The elapsed fraction of the animation will be passed 218 * through this interpolator to calculate the interpolated fraction, which is then used to 219 * calculate the animated values. 220 */ 221 private TimeInterpolator mInterpolator = sDefaultInterpolator; 222 223 /** 224 * The set of listeners to be sent events through the life of an animation. 225 */ 226 ArrayList<AnimatorUpdateListener> mUpdateListeners = null; 227 228 /** 229 * The property/value sets being animated. 230 */ 231 PropertyValuesHolder[] mValues; 232 233 /** 234 * A hashmap of the PropertyValuesHolder objects. This map is used to lookup animated values 235 * by property name during calls to getAnimatedValue(String). 236 */ 237 HashMap<String, PropertyValuesHolder> mValuesMap; 238 239 /** 240 * Public constants 241 */ 242 243 /** 244 * When the animation reaches the end and <code>repeatCount</code> is INFINITE 245 * or a positive value, the animation restarts from the beginning. 246 */ 247 public static final int RESTART = 1; 248 /** 249 * When the animation reaches the end and <code>repeatCount</code> is INFINITE 250 * or a positive value, the animation reverses direction on every iteration. 251 */ 252 public static final int REVERSE = 2; 253 /** 254 * This value used used with the {@link #setRepeatCount(int)} property to repeat 255 * the animation indefinitely. 256 */ 257 public static final int INFINITE = -1; 258 259 /** 260 * @hide 261 */ 262 public static void setDurationScale(float durationScale) { 263 sDurationScale = durationScale; 264 } 265 266 /** 267 * @hide 268 */ 269 public static float getDurationScale() { 270 return sDurationScale; 271 } 272 273 /** 274 * Creates a new ValueAnimator object. This default constructor is primarily for 275 * use internally; the factory methods which take parameters are more generally 276 * useful. 277 */ 278 public ValueAnimator() { 279 } 280 281 /** 282 * Constructs and returns a ValueAnimator that animates between int values. A single 283 * value implies that that value is the one being animated to. However, this is not typically 284 * useful in a ValueAnimator object because there is no way for the object to determine the 285 * starting value for the animation (unlike ObjectAnimator, which can derive that value 286 * from the target object and property being animated). Therefore, there should typically 287 * be two or more values. 288 * 289 * @param values A set of values that the animation will animate between over time. 290 * @return A ValueAnimator object that is set up to animate between the given values. 291 */ 292 public static ValueAnimator ofInt(int... values) { 293 ValueAnimator anim = new ValueAnimator(); 294 anim.setIntValues(values); 295 return anim; 296 } 297 298 /** 299 * Constructs and returns a ValueAnimator that animates between color values. A single 300 * value implies that that value is the one being animated to. However, this is not typically 301 * useful in a ValueAnimator object because there is no way for the object to determine the 302 * starting value for the animation (unlike ObjectAnimator, which can derive that value 303 * from the target object and property being animated). Therefore, there should typically 304 * be two or more values. 305 * 306 * @param values A set of values that the animation will animate between over time. 307 * @return A ValueAnimator object that is set up to animate between the given values. 308 */ 309 public static ValueAnimator ofArgb(int... values) { 310 ValueAnimator anim = new ValueAnimator(); 311 anim.setIntValues(values); 312 anim.setEvaluator(ArgbEvaluator.getInstance()); 313 return anim; 314 } 315 316 /** 317 * Constructs and returns a ValueAnimator that animates between float values. A single 318 * value implies that that value is the one being animated to. However, this is not typically 319 * useful in a ValueAnimator object because there is no way for the object to determine the 320 * starting value for the animation (unlike ObjectAnimator, which can derive that value 321 * from the target object and property being animated). Therefore, there should typically 322 * be two or more values. 323 * 324 * @param values A set of values that the animation will animate between over time. 325 * @return A ValueAnimator object that is set up to animate between the given values. 326 */ 327 public static ValueAnimator ofFloat(float... values) { 328 ValueAnimator anim = new ValueAnimator(); 329 anim.setFloatValues(values); 330 return anim; 331 } 332 333 /** 334 * Constructs and returns a ValueAnimator that animates between the values 335 * specified in the PropertyValuesHolder objects. 336 * 337 * @param values A set of PropertyValuesHolder objects whose values will be animated 338 * between over time. 339 * @return A ValueAnimator object that is set up to animate between the given values. 340 */ 341 public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values) { 342 ValueAnimator anim = new ValueAnimator(); 343 anim.setValues(values); 344 return anim; 345 } 346 /** 347 * Constructs and returns a ValueAnimator that animates between Object values. A single 348 * value implies that that value is the one being animated to. However, this is not typically 349 * useful in a ValueAnimator object because there is no way for the object to determine the 350 * starting value for the animation (unlike ObjectAnimator, which can derive that value 351 * from the target object and property being animated). Therefore, there should typically 352 * be two or more values. 353 * 354 * <p>Since ValueAnimator does not know how to animate between arbitrary Objects, this 355 * factory method also takes a TypeEvaluator object that the ValueAnimator will use 356 * to perform that interpolation. 357 * 358 * @param evaluator A TypeEvaluator that will be called on each animation frame to 359 * provide the ncessry interpolation between the Object values to derive the animated 360 * value. 361 * @param values A set of values that the animation will animate between over time. 362 * @return A ValueAnimator object that is set up to animate between the given values. 363 */ 364 public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) { 365 ValueAnimator anim = new ValueAnimator(); 366 anim.setObjectValues(values); 367 anim.setEvaluator(evaluator); 368 return anim; 369 } 370 371 /** 372 * Sets int values that will be animated between. A single 373 * value implies that that value is the one being animated to. However, this is not typically 374 * useful in a ValueAnimator object because there is no way for the object to determine the 375 * starting value for the animation (unlike ObjectAnimator, which can derive that value 376 * from the target object and property being animated). Therefore, there should typically 377 * be two or more values. 378 * 379 * <p>If there are already multiple sets of values defined for this ValueAnimator via more 380 * than one PropertyValuesHolder object, this method will set the values for the first 381 * of those objects.</p> 382 * 383 * @param values A set of values that the animation will animate between over time. 384 */ 385 public void setIntValues(int... values) { 386 if (values == null || values.length == 0) { 387 return; 388 } 389 if (mValues == null || mValues.length == 0) { 390 setValues(PropertyValuesHolder.ofInt("", values)); 391 } else { 392 PropertyValuesHolder valuesHolder = mValues[0]; 393 valuesHolder.setIntValues(values); 394 } 395 // New property/values/target should cause re-initialization prior to starting 396 mInitialized = false; 397 } 398 399 /** 400 * Sets float values that will be animated between. A single 401 * value implies that that value is the one being animated to. However, this is not typically 402 * useful in a ValueAnimator object because there is no way for the object to determine the 403 * starting value for the animation (unlike ObjectAnimator, which can derive that value 404 * from the target object and property being animated). Therefore, there should typically 405 * be two or more values. 406 * 407 * <p>If there are already multiple sets of values defined for this ValueAnimator via more 408 * than one PropertyValuesHolder object, this method will set the values for the first 409 * of those objects.</p> 410 * 411 * @param values A set of values that the animation will animate between over time. 412 */ 413 public void setFloatValues(float... values) { 414 if (values == null || values.length == 0) { 415 return; 416 } 417 if (mValues == null || mValues.length == 0) { 418 setValues(PropertyValuesHolder.ofFloat("", values)); 419 } else { 420 PropertyValuesHolder valuesHolder = mValues[0]; 421 valuesHolder.setFloatValues(values); 422 } 423 // New property/values/target should cause re-initialization prior to starting 424 mInitialized = false; 425 } 426 427 /** 428 * Sets the values to animate between for this animation. A single 429 * value implies that that value is the one being animated to. However, this is not typically 430 * useful in a ValueAnimator object because there is no way for the object to determine the 431 * starting value for the animation (unlike ObjectAnimator, which can derive that value 432 * from the target object and property being animated). Therefore, there should typically 433 * be two or more values. 434 * 435 * <p>If there are already multiple sets of values defined for this ValueAnimator via more 436 * than one PropertyValuesHolder object, this method will set the values for the first 437 * of those objects.</p> 438 * 439 * <p>There should be a TypeEvaluator set on the ValueAnimator that knows how to interpolate 440 * between these value objects. ValueAnimator only knows how to interpolate between the 441 * primitive types specified in the other setValues() methods.</p> 442 * 443 * @param values The set of values to animate between. 444 */ 445 public void setObjectValues(Object... values) { 446 if (values == null || values.length == 0) { 447 return; 448 } 449 if (mValues == null || mValues.length == 0) { 450 setValues(PropertyValuesHolder.ofObject("", null, values)); 451 } else { 452 PropertyValuesHolder valuesHolder = mValues[0]; 453 valuesHolder.setObjectValues(values); 454 } 455 // New property/values/target should cause re-initialization prior to starting 456 mInitialized = false; 457 } 458 459 /** 460 * Sets the values, per property, being animated between. This function is called internally 461 * by the constructors of ValueAnimator that take a list of values. But a ValueAnimator can 462 * be constructed without values and this method can be called to set the values manually 463 * instead. 464 * 465 * @param values The set of values, per property, being animated between. 466 */ 467 public void setValues(PropertyValuesHolder... values) { 468 int numValues = values.length; 469 mValues = values; 470 mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues); 471 for (int i = 0; i < numValues; ++i) { 472 PropertyValuesHolder valuesHolder = values[i]; 473 mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder); 474 } 475 // New property/values/target should cause re-initialization prior to starting 476 mInitialized = false; 477 } 478 479 /** 480 * Returns the values that this ValueAnimator animates between. These values are stored in 481 * PropertyValuesHolder objects, even if the ValueAnimator was created with a simple list 482 * of value objects instead. 483 * 484 * @return PropertyValuesHolder[] An array of PropertyValuesHolder objects which hold the 485 * values, per property, that define the animation. 486 */ 487 public PropertyValuesHolder[] getValues() { 488 return mValues; 489 } 490 491 /** 492 * This function is called immediately before processing the first animation 493 * frame of an animation. If there is a nonzero <code>startDelay</code>, the 494 * function is called after that delay ends. 495 * It takes care of the final initialization steps for the 496 * animation. 497 * 498 * <p>Overrides of this method should call the superclass method to ensure 499 * that internal mechanisms for the animation are set up correctly.</p> 500 */ 501 @CallSuper 502 void initAnimation() { 503 if (!mInitialized) { 504 int numValues = mValues.length; 505 for (int i = 0; i < numValues; ++i) { 506 mValues[i].init(); 507 } 508 mInitialized = true; 509 } 510 } 511 512 /** 513 * Sets the length of the animation. The default duration is 300 milliseconds. 514 * 515 * @param duration The length of the animation, in milliseconds. This value cannot 516 * be negative. 517 * @return ValueAnimator The object called with setDuration(). This return 518 * value makes it easier to compose statements together that construct and then set the 519 * duration, as in <code>ValueAnimator.ofInt(0, 10).setDuration(500).start()</code>. 520 */ 521 @Override 522 public ValueAnimator setDuration(long duration) { 523 if (duration < 0) { 524 throw new IllegalArgumentException("Animators cannot have negative duration: " + 525 duration); 526 } 527 mUnscaledDuration = duration; 528 updateScaledDuration(); 529 return this; 530 } 531 532 private void updateScaledDuration() { 533 mDuration = (long)(mUnscaledDuration * sDurationScale); 534 } 535 536 /** 537 * Gets the length of the animation. The default duration is 300 milliseconds. 538 * 539 * @return The length of the animation, in milliseconds. 540 */ 541 @Override 542 public long getDuration() { 543 return mUnscaledDuration; 544 } 545 546 /** 547 * @hide 548 */ 549 @Override 550 public long getTotalDuration() { 551 if (mRepeatCount == INFINITE) { 552 return DURATION_INFINITE; 553 } else { 554 return mUnscaledStartDelay + (mUnscaledDuration * (mRepeatCount + 1)); 555 } 556 } 557 558 /** 559 * Sets the position of the animation to the specified point in time. This time should 560 * be between 0 and the total duration of the animation, including any repetition. If 561 * the animation has not yet been started, then it will not advance forward after it is 562 * set to this time; it will simply set the time to this value and perform any appropriate 563 * actions based on that time. If the animation is already running, then setCurrentPlayTime() 564 * will set the current playing time to this value and continue playing from that point. 565 * 566 * @param playTime The time, in milliseconds, to which the animation is advanced or rewound. 567 */ 568 public void setCurrentPlayTime(long playTime) { 569 float fraction = mUnscaledDuration > 0 ? (float) playTime / mUnscaledDuration : 1; 570 setCurrentFraction(fraction); 571 } 572 573 /** 574 * Sets the position of the animation to the specified fraction. This fraction should 575 * be between 0 and the total fraction of the animation, including any repetition. That is, 576 * a fraction of 0 will position the animation at the beginning, a value of 1 at the end, 577 * and a value of 2 at the end of a reversing animator that repeats once. If 578 * the animation has not yet been started, then it will not advance forward after it is 579 * set to this fraction; it will simply set the fraction to this value and perform any 580 * appropriate actions based on that fraction. If the animation is already running, then 581 * setCurrentFraction() will set the current fraction to this value and continue 582 * playing from that point. {@link Animator.AnimatorListener} events are not called 583 * due to changing the fraction; those events are only processed while the animation 584 * is running. 585 * 586 * @param fraction The fraction to which the animation is advanced or rewound. Values 587 * outside the range of 0 to the maximum fraction for the animator will be clamped to 588 * the correct range. 589 */ 590 public void setCurrentFraction(float fraction) { 591 initAnimation(); 592 if (fraction < 0) { 593 fraction = 0; 594 } 595 int iteration = (int) fraction; 596 if (fraction == 1) { 597 iteration -= 1; 598 } else if (fraction > 1) { 599 if (iteration < (mRepeatCount + 1) || mRepeatCount == INFINITE) { 600 if (mRepeatMode == REVERSE) { 601 mPlayingBackwards = (iteration % 2) != 0; 602 } 603 fraction = fraction % 1f; 604 } else { 605 fraction = 1; 606 iteration -= 1; 607 } 608 } else { 609 mPlayingBackwards = mReversing; 610 } 611 mCurrentIteration = iteration; 612 long seekTime = (long) (mDuration * fraction); 613 long currentTime = AnimationUtils.currentAnimationTimeMillis(); 614 mStartTime = currentTime - seekTime; 615 mStartTimeCommitted = true; // do not allow start time to be compensated for jank 616 if (!mRunning) { 617 mSeekFraction = fraction; 618 } 619 if (mPlayingBackwards) { 620 fraction = 1f - fraction; 621 } 622 animateValue(fraction); 623 } 624 625 /** 626 * Gets the current position of the animation in time, which is equal to the current 627 * time minus the time that the animation started. An animation that is not yet started will 628 * return a value of zero. 629 * 630 * @return The current position in time of the animation. 631 */ 632 public long getCurrentPlayTime() { 633 if (!mInitialized || !mStarted) { 634 return 0; 635 } 636 return AnimationUtils.currentAnimationTimeMillis() - mStartTime; 637 } 638 639 /** 640 * The amount of time, in milliseconds, to delay starting the animation after 641 * {@link #start()} is called. 642 * 643 * @return the number of milliseconds to delay running the animation 644 */ 645 @Override 646 public long getStartDelay() { 647 return mUnscaledStartDelay; 648 } 649 650 /** 651 * The amount of time, in milliseconds, to delay starting the animation after 652 * {@link #start()} is called. 653 654 * @param startDelay The amount of the delay, in milliseconds 655 */ 656 @Override 657 public void setStartDelay(long startDelay) { 658 this.mStartDelay = (long)(startDelay * sDurationScale); 659 mUnscaledStartDelay = startDelay; 660 } 661 662 /** 663 * The amount of time, in milliseconds, between each frame of the animation. This is a 664 * requested time that the animation will attempt to honor, but the actual delay between 665 * frames may be different, depending on system load and capabilities. This is a static 666 * function because the same delay will be applied to all animations, since they are all 667 * run off of a single timing loop. 668 * 669 * The frame delay may be ignored when the animation system uses an external timing 670 * source, such as the display refresh rate (vsync), to govern animations. 671 * 672 * Note that this method should be called from the same thread that {@link #start()} is 673 * called in order to check the frame delay for that animation. A runtime exception will be 674 * thrown if the calling thread does not have a Looper. 675 * 676 * @return the requested time between frames, in milliseconds 677 */ 678 public static long getFrameDelay() { 679 return AnimationHandler.getInstance().getFrameDelay(); 680 } 681 682 /** 683 * The amount of time, in milliseconds, between each frame of the animation. This is a 684 * requested time that the animation will attempt to honor, but the actual delay between 685 * frames may be different, depending on system load and capabilities. This is a static 686 * function because the same delay will be applied to all animations, since they are all 687 * run off of a single timing loop. 688 * 689 * The frame delay may be ignored when the animation system uses an external timing 690 * source, such as the display refresh rate (vsync), to govern animations. 691 * 692 * Note that this method should be called from the same thread that {@link #start()} is 693 * called in order to have the new frame delay take effect on that animation. A runtime 694 * exception will be thrown if the calling thread does not have a Looper. 695 * 696 * @param frameDelay the requested time between frames, in milliseconds 697 */ 698 public static void setFrameDelay(long frameDelay) { 699 AnimationHandler.getInstance().setFrameDelay(frameDelay); 700 } 701 702 /** 703 * The most recent value calculated by this <code>ValueAnimator</code> when there is just one 704 * property being animated. This value is only sensible while the animation is running. The main 705 * purpose for this read-only property is to retrieve the value from the <code>ValueAnimator</code> 706 * during a call to {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which 707 * is called during each animation frame, immediately after the value is calculated. 708 * 709 * @return animatedValue The value most recently calculated by this <code>ValueAnimator</code> for 710 * the single property being animated. If there are several properties being animated 711 * (specified by several PropertyValuesHolder objects in the constructor), this function 712 * returns the animated value for the first of those objects. 713 */ 714 public Object getAnimatedValue() { 715 if (mValues != null && mValues.length > 0) { 716 return mValues[0].getAnimatedValue(); 717 } 718 // Shouldn't get here; should always have values unless ValueAnimator was set up wrong 719 return null; 720 } 721 722 /** 723 * The most recent value calculated by this <code>ValueAnimator</code> for <code>propertyName</code>. 724 * The main purpose for this read-only property is to retrieve the value from the 725 * <code>ValueAnimator</code> during a call to 726 * {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which 727 * is called during each animation frame, immediately after the value is calculated. 728 * 729 * @return animatedValue The value most recently calculated for the named property 730 * by this <code>ValueAnimator</code>. 731 */ 732 public Object getAnimatedValue(String propertyName) { 733 PropertyValuesHolder valuesHolder = mValuesMap.get(propertyName); 734 if (valuesHolder != null) { 735 return valuesHolder.getAnimatedValue(); 736 } else { 737 // At least avoid crashing if called with bogus propertyName 738 return null; 739 } 740 } 741 742 /** 743 * Sets how many times the animation should be repeated. If the repeat 744 * count is 0, the animation is never repeated. If the repeat count is 745 * greater than 0 or {@link #INFINITE}, the repeat mode will be taken 746 * into account. The repeat count is 0 by default. 747 * 748 * @param value the number of times the animation should be repeated 749 */ 750 public void setRepeatCount(int value) { 751 mRepeatCount = value; 752 } 753 /** 754 * Defines how many times the animation should repeat. The default value 755 * is 0. 756 * 757 * @return the number of times the animation should repeat, or {@link #INFINITE} 758 */ 759 public int getRepeatCount() { 760 return mRepeatCount; 761 } 762 763 /** 764 * Defines what this animation should do when it reaches the end. This 765 * setting is applied only when the repeat count is either greater than 766 * 0 or {@link #INFINITE}. Defaults to {@link #RESTART}. 767 * 768 * @param value {@link #RESTART} or {@link #REVERSE} 769 */ 770 public void setRepeatMode(int value) { 771 mRepeatMode = value; 772 } 773 774 /** 775 * Defines what this animation should do when it reaches the end. 776 * 777 * @return either one of {@link #REVERSE} or {@link #RESTART} 778 */ 779 public int getRepeatMode() { 780 return mRepeatMode; 781 } 782 783 /** 784 * Adds a listener to the set of listeners that are sent update events through the life of 785 * an animation. This method is called on all listeners for every frame of the animation, 786 * after the values for the animation have been calculated. 787 * 788 * @param listener the listener to be added to the current set of listeners for this animation. 789 */ 790 public void addUpdateListener(AnimatorUpdateListener listener) { 791 if (mUpdateListeners == null) { 792 mUpdateListeners = new ArrayList<AnimatorUpdateListener>(); 793 } 794 mUpdateListeners.add(listener); 795 } 796 797 /** 798 * Removes all listeners from the set listening to frame updates for this animation. 799 */ 800 public void removeAllUpdateListeners() { 801 if (mUpdateListeners == null) { 802 return; 803 } 804 mUpdateListeners.clear(); 805 mUpdateListeners = null; 806 } 807 808 /** 809 * Removes a listener from the set listening to frame updates for this animation. 810 * 811 * @param listener the listener to be removed from the current set of update listeners 812 * for this animation. 813 */ 814 public void removeUpdateListener(AnimatorUpdateListener listener) { 815 if (mUpdateListeners == null) { 816 return; 817 } 818 mUpdateListeners.remove(listener); 819 if (mUpdateListeners.size() == 0) { 820 mUpdateListeners = null; 821 } 822 } 823 824 825 /** 826 * The time interpolator used in calculating the elapsed fraction of this animation. The 827 * interpolator determines whether the animation runs with linear or non-linear motion, 828 * such as acceleration and deceleration. The default value is 829 * {@link android.view.animation.AccelerateDecelerateInterpolator} 830 * 831 * @param value the interpolator to be used by this animation. A value of <code>null</code> 832 * will result in linear interpolation. 833 */ 834 @Override 835 public void setInterpolator(TimeInterpolator value) { 836 if (value != null) { 837 mInterpolator = value; 838 } else { 839 mInterpolator = new LinearInterpolator(); 840 } 841 } 842 843 /** 844 * Returns the timing interpolator that this ValueAnimator uses. 845 * 846 * @return The timing interpolator for this ValueAnimator. 847 */ 848 @Override 849 public TimeInterpolator getInterpolator() { 850 return mInterpolator; 851 } 852 853 /** 854 * The type evaluator to be used when calculating the animated values of this animation. 855 * The system will automatically assign a float or int evaluator based on the type 856 * of <code>startValue</code> and <code>endValue</code> in the constructor. But if these values 857 * are not one of these primitive types, or if different evaluation is desired (such as is 858 * necessary with int values that represent colors), a custom evaluator needs to be assigned. 859 * For example, when running an animation on color values, the {@link ArgbEvaluator} 860 * should be used to get correct RGB color interpolation. 861 * 862 * <p>If this ValueAnimator has only one set of values being animated between, this evaluator 863 * will be used for that set. If there are several sets of values being animated, which is 864 * the case if PropertyValuesHolder objects were set on the ValueAnimator, then the evaluator 865 * is assigned just to the first PropertyValuesHolder object.</p> 866 * 867 * @param value the evaluator to be used this animation 868 */ 869 public void setEvaluator(TypeEvaluator value) { 870 if (value != null && mValues != null && mValues.length > 0) { 871 mValues[0].setEvaluator(value); 872 } 873 } 874 875 private void notifyStartListeners() { 876 if (mListeners != null && !mStartListenersCalled) { 877 ArrayList<AnimatorListener> tmpListeners = 878 (ArrayList<AnimatorListener>) mListeners.clone(); 879 int numListeners = tmpListeners.size(); 880 for (int i = 0; i < numListeners; ++i) { 881 tmpListeners.get(i).onAnimationStart(this); 882 } 883 } 884 mStartListenersCalled = true; 885 } 886 887 /** 888 * Start the animation playing. This version of start() takes a boolean flag that indicates 889 * whether the animation should play in reverse. The flag is usually false, but may be set 890 * to true if called from the reverse() method. 891 * 892 * <p>The animation started by calling this method will be run on the thread that called 893 * this method. This thread should have a Looper on it (a runtime exception will be thrown if 894 * this is not the case). Also, if the animation will animate 895 * properties of objects in the view hierarchy, then the calling thread should be the UI 896 * thread for that view hierarchy.</p> 897 * 898 * @param playBackwards Whether the ValueAnimator should start playing in reverse. 899 */ 900 private void start(boolean playBackwards) { 901 if (Looper.myLooper() == null) { 902 throw new AndroidRuntimeException("Animators may only be run on Looper threads"); 903 } 904 mReversing = playBackwards; 905 mPlayingBackwards = playBackwards; 906 if (playBackwards && mSeekFraction != -1) { 907 if (mSeekFraction == 0 && mCurrentIteration == 0) { 908 // special case: reversing from seek-to-0 should act as if not seeked at all 909 mSeekFraction = 0; 910 } else if (mRepeatCount == INFINITE) { 911 mSeekFraction = 1 - (mSeekFraction % 1); 912 } else { 913 mSeekFraction = 1 + mRepeatCount - (mCurrentIteration + mSeekFraction); 914 } 915 mCurrentIteration = (int) mSeekFraction; 916 mSeekFraction = mSeekFraction % 1; 917 } 918 if (mCurrentIteration > 0 && mRepeatMode == REVERSE && 919 (mCurrentIteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) { 920 // if we were seeked to some other iteration in a reversing animator, 921 // figure out the correct direction to start playing based on the iteration 922 if (playBackwards) { 923 mPlayingBackwards = (mCurrentIteration % 2) == 0; 924 } else { 925 mPlayingBackwards = (mCurrentIteration % 2) != 0; 926 } 927 } 928 mStarted = true; 929 mPaused = false; 930 mRunning = false; 931 mAnimationEndRequested = false; 932 updateScaledDuration(); // in case the scale factor has changed since creation time 933 AnimationHandler animationHandler = AnimationHandler.getInstance(); 934 animationHandler.addAnimationFrameCallback(this, mStartDelay); 935 } 936 937 @Override 938 public void start() { 939 start(false); 940 } 941 942 @Override 943 public void cancel() { 944 if (Looper.myLooper() == null) { 945 throw new AndroidRuntimeException("Animators may only be run on Looper threads"); 946 } 947 948 // If end has already been requested, through a previous end() or cancel() call, no-op 949 // until animation starts again. 950 if (mAnimationEndRequested) { 951 return; 952 } 953 954 // Only cancel if the animation is actually running or has been started and is about 955 // to run 956 // Only notify listeners if the animator has actually started 957 if ((mStarted || mRunning) && mListeners != null) { 958 if (!mRunning) { 959 // If it's not yet running, then start listeners weren't called. Call them now. 960 notifyStartListeners(); 961 } 962 ArrayList<AnimatorListener> tmpListeners = 963 (ArrayList<AnimatorListener>) mListeners.clone(); 964 for (AnimatorListener listener : tmpListeners) { 965 listener.onAnimationCancel(this); 966 } 967 } 968 endAnimation(); 969 970 } 971 972 @Override 973 public void end() { 974 if (Looper.myLooper() == null) { 975 throw new AndroidRuntimeException("Animators may only be run on Looper threads"); 976 } 977 if (!mRunning) { 978 // Special case if the animation has not yet started; get it ready for ending 979 startAnimation(); 980 mStarted = true; 981 } else if (!mInitialized) { 982 initAnimation(); 983 } 984 animateValue(mPlayingBackwards ? 0f : 1f); 985 endAnimation(); 986 } 987 988 @Override 989 public void resume() { 990 if (mPaused) { 991 mResumed = true; 992 } 993 super.resume(); 994 } 995 996 @Override 997 public void pause() { 998 boolean previouslyPaused = mPaused; 999 super.pause(); 1000 if (!previouslyPaused && mPaused) { 1001 mPauseTime = -1; 1002 mResumed = false; 1003 } 1004 } 1005 1006 @Override 1007 public boolean isRunning() { 1008 return mRunning; 1009 } 1010 1011 @Override 1012 public boolean isStarted() { 1013 return mStarted; 1014 } 1015 1016 /** 1017 * Plays the ValueAnimator in reverse. If the animation is already running, 1018 * it will stop itself and play backwards from the point reached when reverse was called. 1019 * If the animation is not currently running, then it will start from the end and 1020 * play backwards. This behavior is only set for the current animation; future playing 1021 * of the animation will use the default behavior of playing forward. 1022 */ 1023 @Override 1024 public void reverse() { 1025 mPlayingBackwards = !mPlayingBackwards; 1026 if (mRunning) { 1027 long currentTime = AnimationUtils.currentAnimationTimeMillis(); 1028 long currentPlayTime = currentTime - mStartTime; 1029 long timeLeft = mDuration - currentPlayTime; 1030 mStartTime = currentTime - timeLeft; 1031 mStartTimeCommitted = true; // do not allow start time to be compensated for jank 1032 mReversing = !mReversing; 1033 } else if (mStarted) { 1034 end(); 1035 } else { 1036 start(true); 1037 } 1038 } 1039 1040 /** 1041 * @hide 1042 */ 1043 @Override 1044 public boolean canReverse() { 1045 return true; 1046 } 1047 1048 /** 1049 * Called internally to end an animation by removing it from the animations list. Must be 1050 * called on the UI thread. 1051 */ 1052 private void endAnimation() { 1053 if (mAnimationEndRequested) { 1054 return; 1055 } 1056 AnimationHandler handler = AnimationHandler.getInstance(); 1057 handler.removeCallback(this); 1058 1059 mAnimationEndRequested = true; 1060 mPaused = false; 1061 if ((mStarted || mRunning) && mListeners != null) { 1062 if (!mRunning) { 1063 // If it's not yet running, then start listeners weren't called. Call them now. 1064 notifyStartListeners(); 1065 } 1066 ArrayList<AnimatorListener> tmpListeners = 1067 (ArrayList<AnimatorListener>) mListeners.clone(); 1068 int numListeners = tmpListeners.size(); 1069 for (int i = 0; i < numListeners; ++i) { 1070 tmpListeners.get(i).onAnimationEnd(this); 1071 } 1072 } 1073 mRunning = false; 1074 mStarted = false; 1075 mStartListenersCalled = false; 1076 mPlayingBackwards = false; 1077 mReversing = false; 1078 mCurrentIteration = 0; 1079 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { 1080 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(), 1081 System.identityHashCode(this)); 1082 } 1083 } 1084 1085 /** 1086 * Called internally to start an animation by adding it to the active animations list. Must be 1087 * called on the UI thread. 1088 */ 1089 private void startAnimation() { 1090 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { 1091 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(), 1092 System.identityHashCode(this)); 1093 } 1094 initAnimation(); 1095 mRunning = true; 1096 if (mListeners != null) { 1097 notifyStartListeners(); 1098 } 1099 } 1100 1101 /** 1102 * Returns the name of this animator for debugging purposes. 1103 */ 1104 String getNameForTrace() { 1105 return "animator"; 1106 } 1107 1108 /** 1109 * Applies an adjustment to the animation to compensate for jank between when 1110 * the animation first ran and when the frame was drawn. 1111 * @hide 1112 */ 1113 public void commitAnimationFrame(long frameTime) { 1114 if (!mStartTimeCommitted) { 1115 mStartTimeCommitted = true; 1116 long adjustment = frameTime - mLastFrameTime; 1117 if (adjustment > 0) { 1118 mStartTime += adjustment; 1119 if (DEBUG) { 1120 Log.d(TAG, "Adjusted start time by " + adjustment + " ms: " + toString()); 1121 } 1122 } 1123 } 1124 } 1125 1126 /** 1127 * This internal function processes a single animation frame for a given animation. The 1128 * currentTime parameter is the timing pulse sent by the handler, used to calculate the 1129 * elapsed duration, and therefore 1130 * the elapsed fraction, of the animation. The return value indicates whether the animation 1131 * should be ended (which happens when the elapsed time of the animation exceeds the 1132 * animation's duration, including the repeatCount). 1133 * 1134 * @param currentTime The current time, as tracked by the static timing handler 1135 * @return true if the animation's duration, including any repetitions due to 1136 * <code>repeatCount</code> has been exceeded and the animation should be ended. 1137 */ 1138 boolean animateBasedOnTime(long currentTime) { 1139 boolean done = false; 1140 if (mRunning) { 1141 float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f; 1142 if (mDuration == 0 && mRepeatCount != INFINITE) { 1143 // Skip to the end 1144 mCurrentIteration = mRepeatCount; 1145 if (!mReversing) { 1146 mPlayingBackwards = false; 1147 } 1148 } 1149 if (fraction >= 1f) { 1150 if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) { 1151 // Time to repeat 1152 if (mListeners != null) { 1153 int numListeners = mListeners.size(); 1154 for (int i = 0; i < numListeners; ++i) { 1155 mListeners.get(i).onAnimationRepeat(this); 1156 } 1157 } 1158 if (mRepeatMode == REVERSE) { 1159 mPlayingBackwards = !mPlayingBackwards; 1160 } 1161 mCurrentIteration += (int) fraction; 1162 fraction = fraction % 1f; 1163 mStartTime += mDuration; 1164 // Note: We do not need to update the value of mStartTimeCommitted here 1165 // since we just added a duration offset. 1166 } else { 1167 done = true; 1168 fraction = Math.min(fraction, 1.0f); 1169 } 1170 } 1171 if (mPlayingBackwards) { 1172 fraction = 1f - fraction; 1173 } 1174 animateValue(fraction); 1175 } 1176 return done; 1177 } 1178 1179 /** 1180 * Processes a frame of the animation, adjusting the start time if needed. 1181 * 1182 * @param frameTime The frame time. 1183 * @return true if the animation has ended. 1184 * @hide 1185 */ 1186 public final void doAnimationFrame(long frameTime) { 1187 mLastFrameTime = frameTime; 1188 AnimationHandler handler = AnimationHandler.getInstance(); 1189 if (!mRunning) { 1190 // First frame 1191 handler.addOneShotCommitCallback(this); 1192 startAnimation(); 1193 if (mSeekFraction < 0) { 1194 mStartTime = frameTime; 1195 } else { 1196 long seekTime = (long) (mDuration * mSeekFraction); 1197 mStartTime = frameTime - seekTime; 1198 mSeekFraction = -1; 1199 } 1200 mStartTimeCommitted = false; // allow start time to be compensated for jank 1201 } 1202 if (mPaused) { 1203 if (mPauseTime < 0) { 1204 mPauseTime = frameTime; 1205 } 1206 return; 1207 } else if (mResumed) { 1208 mResumed = false; 1209 if (mPauseTime > 0) { 1210 // Offset by the duration that the animation was paused 1211 mStartTime += (frameTime - mPauseTime); 1212 mStartTimeCommitted = false; // allow start time to be compensated for jank 1213 } 1214 handler.addOneShotCommitCallback(this); 1215 } 1216 // The frame time might be before the start time during the first frame of 1217 // an animation. The "current time" must always be on or after the start 1218 // time to avoid animating frames at negative time intervals. In practice, this 1219 // is very rare and only happens when seeking backwards. 1220 final long currentTime = Math.max(frameTime, mStartTime); 1221 boolean finished = animateBasedOnTime(currentTime); 1222 1223 if (finished) { 1224 endAnimation(); 1225 } 1226 } 1227 1228 /** 1229 * Returns the current animation fraction, which is the elapsed/interpolated fraction used in 1230 * the most recent frame update on the animation. 1231 * 1232 * @return Elapsed/interpolated fraction of the animation. 1233 */ 1234 public float getAnimatedFraction() { 1235 return mCurrentFraction; 1236 } 1237 1238 /** 1239 * This method is called with the elapsed fraction of the animation during every 1240 * animation frame. This function turns the elapsed fraction into an interpolated fraction 1241 * and then into an animated value (from the evaluator. The function is called mostly during 1242 * animation updates, but it is also called when the <code>end()</code> 1243 * function is called, to set the final value on the property. 1244 * 1245 * <p>Overrides of this method must call the superclass to perform the calculation 1246 * of the animated value.</p> 1247 * 1248 * @param fraction The elapsed fraction of the animation. 1249 */ 1250 @CallSuper 1251 void animateValue(float fraction) { 1252 fraction = mInterpolator.getInterpolation(fraction); 1253 mCurrentFraction = fraction; 1254 int numValues = mValues.length; 1255 for (int i = 0; i < numValues; ++i) { 1256 mValues[i].calculateValue(fraction); 1257 } 1258 if (mUpdateListeners != null) { 1259 int numListeners = mUpdateListeners.size(); 1260 for (int i = 0; i < numListeners; ++i) { 1261 mUpdateListeners.get(i).onAnimationUpdate(this); 1262 } 1263 } 1264 } 1265 1266 @Override 1267 public ValueAnimator clone() { 1268 final ValueAnimator anim = (ValueAnimator) super.clone(); 1269 if (mUpdateListeners != null) { 1270 anim.mUpdateListeners = new ArrayList<AnimatorUpdateListener>(mUpdateListeners); 1271 } 1272 anim.mSeekFraction = -1; 1273 anim.mPlayingBackwards = false; 1274 anim.mReversing = false; 1275 anim.mCurrentIteration = 0; 1276 anim.mInitialized = false; 1277 anim.mStarted = false; 1278 anim.mRunning = false; 1279 anim.mPaused = false; 1280 anim.mResumed = false; 1281 anim.mStartListenersCalled = false; 1282 anim.mStartTime = 0; 1283 anim.mStartTimeCommitted = false; 1284 anim.mAnimationEndRequested = false; 1285 anim.mPauseTime = 0; 1286 anim.mLastFrameTime = 0; 1287 anim.mCurrentFraction = 0; 1288 1289 PropertyValuesHolder[] oldValues = mValues; 1290 if (oldValues != null) { 1291 int numValues = oldValues.length; 1292 anim.mValues = new PropertyValuesHolder[numValues]; 1293 anim.mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues); 1294 for (int i = 0; i < numValues; ++i) { 1295 PropertyValuesHolder newValuesHolder = oldValues[i].clone(); 1296 anim.mValues[i] = newValuesHolder; 1297 anim.mValuesMap.put(newValuesHolder.getPropertyName(), newValuesHolder); 1298 } 1299 } 1300 return anim; 1301 } 1302 1303 /** 1304 * Implementors of this interface can add themselves as update listeners 1305 * to an <code>ValueAnimator</code> instance to receive callbacks on every animation 1306 * frame, after the current frame's values have been calculated for that 1307 * <code>ValueAnimator</code>. 1308 */ 1309 public static interface AnimatorUpdateListener { 1310 /** 1311 * <p>Notifies the occurrence of another frame of the animation.</p> 1312 * 1313 * @param animation The animation which was repeated. 1314 */ 1315 void onAnimationUpdate(ValueAnimator animation); 1316 1317 } 1318 1319 /** 1320 * Return the number of animations currently running. 1321 * 1322 * Used by StrictMode internally to annotate violations. 1323 * May be called on arbitrary threads! 1324 * 1325 * @hide 1326 */ 1327 public static int getCurrentAnimationsCount() { 1328 return AnimationHandler.getAnimationCount(); 1329 } 1330 1331 @Override 1332 public String toString() { 1333 String returnVal = "ValueAnimator@" + Integer.toHexString(hashCode()); 1334 if (mValues != null) { 1335 for (int i = 0; i < mValues.length; ++i) { 1336 returnVal += "\n " + mValues[i].toString(); 1337 } 1338 } 1339 return returnVal; 1340 } 1341 1342 /** 1343 * <p>Whether or not the ValueAnimator is allowed to run asynchronously off of 1344 * the UI thread. This is a hint that informs the ValueAnimator that it is 1345 * OK to run the animation off-thread, however ValueAnimator may decide 1346 * that it must run the animation on the UI thread anyway. For example if there 1347 * is an {@link AnimatorUpdateListener} the animation will run on the UI thread, 1348 * regardless of the value of this hint.</p> 1349 * 1350 * <p>Regardless of whether or not the animation runs asynchronously, all 1351 * listener callbacks will be called on the UI thread.</p> 1352 * 1353 * <p>To be able to use this hint the following must be true:</p> 1354 * <ol> 1355 * <li>{@link #getAnimatedFraction()} is not needed (it will return undefined values).</li> 1356 * <li>The animator is immutable while {@link #isStarted()} is true. Requests 1357 * to change values, duration, delay, etc... may be ignored.</li> 1358 * <li>Lifecycle callback events may be asynchronous. Events such as 1359 * {@link Animator.AnimatorListener#onAnimationEnd(Animator)} or 1360 * {@link Animator.AnimatorListener#onAnimationRepeat(Animator)} may end up delayed 1361 * as they must be posted back to the UI thread, and any actions performed 1362 * by those callbacks (such as starting new animations) will not happen 1363 * in the same frame.</li> 1364 * <li>State change requests ({@link #cancel()}, {@link #end()}, {@link #reverse()}, etc...) 1365 * may be asynchronous. It is guaranteed that all state changes that are 1366 * performed on the UI thread in the same frame will be applied as a single 1367 * atomic update, however that frame may be the current frame, 1368 * the next frame, or some future frame. This will also impact the observed 1369 * state of the Animator. For example, {@link #isStarted()} may still return true 1370 * after a call to {@link #end()}. Using the lifecycle callbacks is preferred over 1371 * queries to {@link #isStarted()}, {@link #isRunning()}, and {@link #isPaused()} 1372 * for this reason.</li> 1373 * </ol> 1374 * @hide 1375 */ 1376 @Override 1377 public void setAllowRunningAsynchronously(boolean mayRunAsync) { 1378 // It is up to subclasses to support this, if they can. 1379 } 1380} 1381