ValueAnimator.java revision 4365e5a13755ffbdb977586c90b8b515add599aa
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, unless the animation has has its play time set via 629 * {@link #setCurrentPlayTime(long)} or {@link #setCurrentFraction(float)}, in which case 630 * it will return the time that was set. 631 * 632 * @return The current position in time of the animation. 633 */ 634 public long getCurrentPlayTime() { 635 if (!mInitialized || (!mStarted && mSeekFraction < 0)) { 636 return 0; 637 } 638 if (mSeekFraction >= 0) { 639 return (long) (mUnscaledDuration * mSeekFraction); 640 } 641 return AnimationUtils.currentAnimationTimeMillis() - mStartTime; 642 } 643 644 /** 645 * The amount of time, in milliseconds, to delay starting the animation after 646 * {@link #start()} is called. 647 * 648 * @return the number of milliseconds to delay running the animation 649 */ 650 @Override 651 public long getStartDelay() { 652 return mUnscaledStartDelay; 653 } 654 655 /** 656 * The amount of time, in milliseconds, to delay starting the animation after 657 * {@link #start()} is called. 658 659 * @param startDelay The amount of the delay, in milliseconds 660 */ 661 @Override 662 public void setStartDelay(long startDelay) { 663 this.mStartDelay = (long)(startDelay * sDurationScale); 664 mUnscaledStartDelay = startDelay; 665 } 666 667 /** 668 * The amount of time, in milliseconds, between each frame of the animation. This is a 669 * requested time that the animation will attempt to honor, but the actual delay between 670 * frames may be different, depending on system load and capabilities. This is a static 671 * function because the same delay will be applied to all animations, since they are all 672 * run off of a single timing loop. 673 * 674 * The frame delay may be ignored when the animation system uses an external timing 675 * source, such as the display refresh rate (vsync), to govern animations. 676 * 677 * Note that this method should be called from the same thread that {@link #start()} is 678 * called in order to check the frame delay for that animation. A runtime exception will be 679 * thrown if the calling thread does not have a Looper. 680 * 681 * @return the requested time between frames, in milliseconds 682 */ 683 public static long getFrameDelay() { 684 return AnimationHandler.getInstance().getFrameDelay(); 685 } 686 687 /** 688 * The amount of time, in milliseconds, between each frame of the animation. This is a 689 * requested time that the animation will attempt to honor, but the actual delay between 690 * frames may be different, depending on system load and capabilities. This is a static 691 * function because the same delay will be applied to all animations, since they are all 692 * run off of a single timing loop. 693 * 694 * The frame delay may be ignored when the animation system uses an external timing 695 * source, such as the display refresh rate (vsync), to govern animations. 696 * 697 * Note that this method should be called from the same thread that {@link #start()} is 698 * called in order to have the new frame delay take effect on that animation. A runtime 699 * exception will be thrown if the calling thread does not have a Looper. 700 * 701 * @param frameDelay the requested time between frames, in milliseconds 702 */ 703 public static void setFrameDelay(long frameDelay) { 704 AnimationHandler.getInstance().setFrameDelay(frameDelay); 705 } 706 707 /** 708 * The most recent value calculated by this <code>ValueAnimator</code> when there is just one 709 * property being animated. This value is only sensible while the animation is running. The main 710 * purpose for this read-only property is to retrieve the value from the <code>ValueAnimator</code> 711 * during a call to {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which 712 * is called during each animation frame, immediately after the value is calculated. 713 * 714 * @return animatedValue The value most recently calculated by this <code>ValueAnimator</code> for 715 * the single property being animated. If there are several properties being animated 716 * (specified by several PropertyValuesHolder objects in the constructor), this function 717 * returns the animated value for the first of those objects. 718 */ 719 public Object getAnimatedValue() { 720 if (mValues != null && mValues.length > 0) { 721 return mValues[0].getAnimatedValue(); 722 } 723 // Shouldn't get here; should always have values unless ValueAnimator was set up wrong 724 return null; 725 } 726 727 /** 728 * The most recent value calculated by this <code>ValueAnimator</code> for <code>propertyName</code>. 729 * The main purpose for this read-only property is to retrieve the value from the 730 * <code>ValueAnimator</code> during a call to 731 * {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which 732 * is called during each animation frame, immediately after the value is calculated. 733 * 734 * @return animatedValue The value most recently calculated for the named property 735 * by this <code>ValueAnimator</code>. 736 */ 737 public Object getAnimatedValue(String propertyName) { 738 PropertyValuesHolder valuesHolder = mValuesMap.get(propertyName); 739 if (valuesHolder != null) { 740 return valuesHolder.getAnimatedValue(); 741 } else { 742 // At least avoid crashing if called with bogus propertyName 743 return null; 744 } 745 } 746 747 /** 748 * Sets how many times the animation should be repeated. If the repeat 749 * count is 0, the animation is never repeated. If the repeat count is 750 * greater than 0 or {@link #INFINITE}, the repeat mode will be taken 751 * into account. The repeat count is 0 by default. 752 * 753 * @param value the number of times the animation should be repeated 754 */ 755 public void setRepeatCount(int value) { 756 mRepeatCount = value; 757 } 758 /** 759 * Defines how many times the animation should repeat. The default value 760 * is 0. 761 * 762 * @return the number of times the animation should repeat, or {@link #INFINITE} 763 */ 764 public int getRepeatCount() { 765 return mRepeatCount; 766 } 767 768 /** 769 * Defines what this animation should do when it reaches the end. This 770 * setting is applied only when the repeat count is either greater than 771 * 0 or {@link #INFINITE}. Defaults to {@link #RESTART}. 772 * 773 * @param value {@link #RESTART} or {@link #REVERSE} 774 */ 775 public void setRepeatMode(int value) { 776 mRepeatMode = value; 777 } 778 779 /** 780 * Defines what this animation should do when it reaches the end. 781 * 782 * @return either one of {@link #REVERSE} or {@link #RESTART} 783 */ 784 public int getRepeatMode() { 785 return mRepeatMode; 786 } 787 788 /** 789 * Adds a listener to the set of listeners that are sent update events through the life of 790 * an animation. This method is called on all listeners for every frame of the animation, 791 * after the values for the animation have been calculated. 792 * 793 * @param listener the listener to be added to the current set of listeners for this animation. 794 */ 795 public void addUpdateListener(AnimatorUpdateListener listener) { 796 if (mUpdateListeners == null) { 797 mUpdateListeners = new ArrayList<AnimatorUpdateListener>(); 798 } 799 mUpdateListeners.add(listener); 800 } 801 802 /** 803 * Removes all listeners from the set listening to frame updates for this animation. 804 */ 805 public void removeAllUpdateListeners() { 806 if (mUpdateListeners == null) { 807 return; 808 } 809 mUpdateListeners.clear(); 810 mUpdateListeners = null; 811 } 812 813 /** 814 * Removes a listener from the set listening to frame updates for this animation. 815 * 816 * @param listener the listener to be removed from the current set of update listeners 817 * for this animation. 818 */ 819 public void removeUpdateListener(AnimatorUpdateListener listener) { 820 if (mUpdateListeners == null) { 821 return; 822 } 823 mUpdateListeners.remove(listener); 824 if (mUpdateListeners.size() == 0) { 825 mUpdateListeners = null; 826 } 827 } 828 829 830 /** 831 * The time interpolator used in calculating the elapsed fraction of this animation. The 832 * interpolator determines whether the animation runs with linear or non-linear motion, 833 * such as acceleration and deceleration. The default value is 834 * {@link android.view.animation.AccelerateDecelerateInterpolator} 835 * 836 * @param value the interpolator to be used by this animation. A value of <code>null</code> 837 * will result in linear interpolation. 838 */ 839 @Override 840 public void setInterpolator(TimeInterpolator value) { 841 if (value != null) { 842 mInterpolator = value; 843 } else { 844 mInterpolator = new LinearInterpolator(); 845 } 846 } 847 848 /** 849 * Returns the timing interpolator that this ValueAnimator uses. 850 * 851 * @return The timing interpolator for this ValueAnimator. 852 */ 853 @Override 854 public TimeInterpolator getInterpolator() { 855 return mInterpolator; 856 } 857 858 /** 859 * The type evaluator to be used when calculating the animated values of this animation. 860 * The system will automatically assign a float or int evaluator based on the type 861 * of <code>startValue</code> and <code>endValue</code> in the constructor. But if these values 862 * are not one of these primitive types, or if different evaluation is desired (such as is 863 * necessary with int values that represent colors), a custom evaluator needs to be assigned. 864 * For example, when running an animation on color values, the {@link ArgbEvaluator} 865 * should be used to get correct RGB color interpolation. 866 * 867 * <p>If this ValueAnimator has only one set of values being animated between, this evaluator 868 * will be used for that set. If there are several sets of values being animated, which is 869 * the case if PropertyValuesHolder objects were set on the ValueAnimator, then the evaluator 870 * is assigned just to the first PropertyValuesHolder object.</p> 871 * 872 * @param value the evaluator to be used this animation 873 */ 874 public void setEvaluator(TypeEvaluator value) { 875 if (value != null && mValues != null && mValues.length > 0) { 876 mValues[0].setEvaluator(value); 877 } 878 } 879 880 private void notifyStartListeners() { 881 if (mListeners != null && !mStartListenersCalled) { 882 ArrayList<AnimatorListener> tmpListeners = 883 (ArrayList<AnimatorListener>) mListeners.clone(); 884 int numListeners = tmpListeners.size(); 885 for (int i = 0; i < numListeners; ++i) { 886 tmpListeners.get(i).onAnimationStart(this); 887 } 888 } 889 mStartListenersCalled = true; 890 } 891 892 /** 893 * Start the animation playing. This version of start() takes a boolean flag that indicates 894 * whether the animation should play in reverse. The flag is usually false, but may be set 895 * to true if called from the reverse() method. 896 * 897 * <p>The animation started by calling this method will be run on the thread that called 898 * this method. This thread should have a Looper on it (a runtime exception will be thrown if 899 * this is not the case). Also, if the animation will animate 900 * properties of objects in the view hierarchy, then the calling thread should be the UI 901 * thread for that view hierarchy.</p> 902 * 903 * @param playBackwards Whether the ValueAnimator should start playing in reverse. 904 */ 905 private void start(boolean playBackwards) { 906 if (Looper.myLooper() == null) { 907 throw new AndroidRuntimeException("Animators may only be run on Looper threads"); 908 } 909 mReversing = playBackwards; 910 mPlayingBackwards = playBackwards; 911 if (playBackwards && mSeekFraction != -1) { 912 if (mSeekFraction == 0 && mCurrentIteration == 0) { 913 // special case: reversing from seek-to-0 should act as if not seeked at all 914 mSeekFraction = 0; 915 } else if (mRepeatCount == INFINITE) { 916 mSeekFraction = 1 - (mSeekFraction % 1); 917 } else { 918 mSeekFraction = 1 + mRepeatCount - (mCurrentIteration + mSeekFraction); 919 } 920 mCurrentIteration = (int) mSeekFraction; 921 mSeekFraction = mSeekFraction % 1; 922 } 923 if (mCurrentIteration > 0 && mRepeatMode == REVERSE && 924 (mCurrentIteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) { 925 // if we were seeked to some other iteration in a reversing animator, 926 // figure out the correct direction to start playing based on the iteration 927 if (playBackwards) { 928 mPlayingBackwards = (mCurrentIteration % 2) == 0; 929 } else { 930 mPlayingBackwards = (mCurrentIteration % 2) != 0; 931 } 932 } 933 mStarted = true; 934 mPaused = false; 935 mRunning = false; 936 mAnimationEndRequested = false; 937 updateScaledDuration(); // in case the scale factor has changed since creation time 938 AnimationHandler animationHandler = AnimationHandler.getInstance(); 939 animationHandler.addAnimationFrameCallback(this, mStartDelay); 940 941 if (mStartDelay == 0) { 942 // If there's no start delay, init the animation and notify start listeners right away 943 // Otherwise, postpone this until the first frame after the start delay. 944 startAnimation(); 945 setCurrentFraction(mSeekFraction == -1 ? 0 : mSeekFraction); 946 } 947 } 948 949 @Override 950 public void start() { 951 start(false); 952 } 953 954 @Override 955 public void cancel() { 956 if (Looper.myLooper() == null) { 957 throw new AndroidRuntimeException("Animators may only be run on Looper threads"); 958 } 959 960 // If end has already been requested, through a previous end() or cancel() call, no-op 961 // until animation starts again. 962 if (mAnimationEndRequested) { 963 return; 964 } 965 966 // Only cancel if the animation is actually running or has been started and is about 967 // to run 968 // Only notify listeners if the animator has actually started 969 if ((mStarted || mRunning) && mListeners != null) { 970 if (!mRunning) { 971 // If it's not yet running, then start listeners weren't called. Call them now. 972 notifyStartListeners(); 973 } 974 ArrayList<AnimatorListener> tmpListeners = 975 (ArrayList<AnimatorListener>) mListeners.clone(); 976 for (AnimatorListener listener : tmpListeners) { 977 listener.onAnimationCancel(this); 978 } 979 } 980 endAnimation(); 981 982 } 983 984 @Override 985 public void end() { 986 if (Looper.myLooper() == null) { 987 throw new AndroidRuntimeException("Animators may only be run on Looper threads"); 988 } 989 if (!mRunning) { 990 // Special case if the animation has not yet started; get it ready for ending 991 startAnimation(); 992 mStarted = true; 993 } else if (!mInitialized) { 994 initAnimation(); 995 } 996 animateValue(mPlayingBackwards ? 0f : 1f); 997 endAnimation(); 998 } 999 1000 @Override 1001 public void resume() { 1002 if (mPaused) { 1003 mResumed = true; 1004 } 1005 super.resume(); 1006 } 1007 1008 @Override 1009 public void pause() { 1010 boolean previouslyPaused = mPaused; 1011 super.pause(); 1012 if (!previouslyPaused && mPaused) { 1013 mPauseTime = -1; 1014 mResumed = false; 1015 } 1016 } 1017 1018 @Override 1019 public boolean isRunning() { 1020 return mRunning; 1021 } 1022 1023 @Override 1024 public boolean isStarted() { 1025 return mStarted; 1026 } 1027 1028 /** 1029 * Plays the ValueAnimator in reverse. If the animation is already running, 1030 * it will stop itself and play backwards from the point reached when reverse was called. 1031 * If the animation is not currently running, then it will start from the end and 1032 * play backwards. This behavior is only set for the current animation; future playing 1033 * of the animation will use the default behavior of playing forward. 1034 */ 1035 @Override 1036 public void reverse() { 1037 mPlayingBackwards = !mPlayingBackwards; 1038 if (mRunning) { 1039 long currentTime = AnimationUtils.currentAnimationTimeMillis(); 1040 long currentPlayTime = currentTime - mStartTime; 1041 long timeLeft = mDuration - currentPlayTime; 1042 mStartTime = currentTime - timeLeft; 1043 mStartTimeCommitted = true; // do not allow start time to be compensated for jank 1044 mReversing = !mReversing; 1045 } else if (mStarted) { 1046 end(); 1047 } else { 1048 start(true); 1049 } 1050 } 1051 1052 /** 1053 * @hide 1054 */ 1055 @Override 1056 public boolean canReverse() { 1057 return true; 1058 } 1059 1060 /** 1061 * Called internally to end an animation by removing it from the animations list. Must be 1062 * called on the UI thread. 1063 */ 1064 private void endAnimation() { 1065 if (mAnimationEndRequested) { 1066 return; 1067 } 1068 AnimationHandler handler = AnimationHandler.getInstance(); 1069 handler.removeCallback(this); 1070 1071 mAnimationEndRequested = true; 1072 mPaused = false; 1073 if ((mStarted || mRunning) && mListeners != null) { 1074 if (!mRunning) { 1075 // If it's not yet running, then start listeners weren't called. Call them now. 1076 notifyStartListeners(); 1077 } 1078 ArrayList<AnimatorListener> tmpListeners = 1079 (ArrayList<AnimatorListener>) mListeners.clone(); 1080 int numListeners = tmpListeners.size(); 1081 for (int i = 0; i < numListeners; ++i) { 1082 tmpListeners.get(i).onAnimationEnd(this); 1083 } 1084 } 1085 mRunning = false; 1086 mStarted = false; 1087 mStartListenersCalled = false; 1088 mPlayingBackwards = false; 1089 mReversing = false; 1090 mLastFrameTime = 0; 1091 mCurrentIteration = 0; 1092 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { 1093 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(), 1094 System.identityHashCode(this)); 1095 } 1096 } 1097 1098 /** 1099 * Called internally to start an animation by adding it to the active animations list. Must be 1100 * called on the UI thread. 1101 */ 1102 private void startAnimation() { 1103 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { 1104 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(), 1105 System.identityHashCode(this)); 1106 } 1107 initAnimation(); 1108 mRunning = true; 1109 if (mListeners != null) { 1110 notifyStartListeners(); 1111 } 1112 } 1113 1114 /** 1115 * Returns the name of this animator for debugging purposes. 1116 */ 1117 String getNameForTrace() { 1118 return "animator"; 1119 } 1120 1121 /** 1122 * Applies an adjustment to the animation to compensate for jank between when 1123 * the animation first ran and when the frame was drawn. 1124 * @hide 1125 */ 1126 public void commitAnimationFrame(long frameTime) { 1127 if (!mStartTimeCommitted) { 1128 mStartTimeCommitted = true; 1129 long adjustment = frameTime - mLastFrameTime; 1130 if (adjustment > 0) { 1131 mStartTime += adjustment; 1132 if (DEBUG) { 1133 Log.d(TAG, "Adjusted start time by " + adjustment + " ms: " + toString()); 1134 } 1135 } 1136 } 1137 } 1138 1139 /** 1140 * This internal function processes a single animation frame for a given animation. The 1141 * currentTime parameter is the timing pulse sent by the handler, used to calculate the 1142 * elapsed duration, and therefore 1143 * the elapsed fraction, of the animation. The return value indicates whether the animation 1144 * should be ended (which happens when the elapsed time of the animation exceeds the 1145 * animation's duration, including the repeatCount). 1146 * 1147 * @param currentTime The current time, as tracked by the static timing handler 1148 * @return true if the animation's duration, including any repetitions due to 1149 * <code>repeatCount</code> has been exceeded and the animation should be ended. 1150 */ 1151 boolean animateBasedOnTime(long currentTime) { 1152 boolean done = false; 1153 if (mRunning) { 1154 float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f; 1155 if (mDuration == 0 && mRepeatCount != INFINITE) { 1156 // Skip to the end 1157 mCurrentIteration = mRepeatCount; 1158 if (!mReversing) { 1159 mPlayingBackwards = false; 1160 } 1161 } 1162 if (fraction >= 1f) { 1163 if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) { 1164 // Time to repeat 1165 if (mListeners != null) { 1166 int numListeners = mListeners.size(); 1167 for (int i = 0; i < numListeners; ++i) { 1168 mListeners.get(i).onAnimationRepeat(this); 1169 } 1170 } 1171 if (mRepeatMode == REVERSE) { 1172 mPlayingBackwards = !mPlayingBackwards; 1173 } 1174 mCurrentIteration += (int) fraction; 1175 fraction = fraction % 1f; 1176 mStartTime += mDuration; 1177 // Note: We do not need to update the value of mStartTimeCommitted here 1178 // since we just added a duration offset. 1179 } else { 1180 done = true; 1181 fraction = Math.min(fraction, 1.0f); 1182 } 1183 } 1184 if (mPlayingBackwards) { 1185 fraction = 1f - fraction; 1186 } 1187 animateValue(fraction); 1188 } 1189 return done; 1190 } 1191 1192 /** 1193 * Processes a frame of the animation, adjusting the start time if needed. 1194 * 1195 * @param frameTime The frame time. 1196 * @return true if the animation has ended. 1197 * @hide 1198 */ 1199 public final void doAnimationFrame(long frameTime) { 1200 AnimationHandler handler = AnimationHandler.getInstance(); 1201 if (mLastFrameTime == 0) { 1202 // First frame 1203 handler.addOneShotCommitCallback(this); 1204 if (mStartDelay > 0) { 1205 startAnimation(); 1206 } 1207 if (mSeekFraction < 0) { 1208 mStartTime = frameTime; 1209 } else { 1210 long seekTime = (long) (mDuration * mSeekFraction); 1211 mStartTime = frameTime - seekTime; 1212 mSeekFraction = -1; 1213 } 1214 mStartTimeCommitted = false; // allow start time to be compensated for jank 1215 } 1216 mLastFrameTime = frameTime; 1217 if (mPaused) { 1218 if (mPauseTime < 0) { 1219 mPauseTime = frameTime; 1220 } 1221 return; 1222 } else if (mResumed) { 1223 mResumed = false; 1224 if (mPauseTime > 0) { 1225 // Offset by the duration that the animation was paused 1226 mStartTime += (frameTime - mPauseTime); 1227 mStartTimeCommitted = false; // allow start time to be compensated for jank 1228 } 1229 handler.addOneShotCommitCallback(this); 1230 } 1231 // The frame time might be before the start time during the first frame of 1232 // an animation. The "current time" must always be on or after the start 1233 // time to avoid animating frames at negative time intervals. In practice, this 1234 // is very rare and only happens when seeking backwards. 1235 final long currentTime = Math.max(frameTime, mStartTime); 1236 boolean finished = animateBasedOnTime(currentTime); 1237 1238 if (finished) { 1239 endAnimation(); 1240 } 1241 } 1242 1243 /** 1244 * Returns the current animation fraction, which is the elapsed/interpolated fraction used in 1245 * the most recent frame update on the animation. 1246 * 1247 * @return Elapsed/interpolated fraction of the animation. 1248 */ 1249 public float getAnimatedFraction() { 1250 return mCurrentFraction; 1251 } 1252 1253 /** 1254 * This method is called with the elapsed fraction of the animation during every 1255 * animation frame. This function turns the elapsed fraction into an interpolated fraction 1256 * and then into an animated value (from the evaluator. The function is called mostly during 1257 * animation updates, but it is also called when the <code>end()</code> 1258 * function is called, to set the final value on the property. 1259 * 1260 * <p>Overrides of this method must call the superclass to perform the calculation 1261 * of the animated value.</p> 1262 * 1263 * @param fraction The elapsed fraction of the animation. 1264 */ 1265 @CallSuper 1266 void animateValue(float fraction) { 1267 fraction = mInterpolator.getInterpolation(fraction); 1268 mCurrentFraction = fraction; 1269 int numValues = mValues.length; 1270 for (int i = 0; i < numValues; ++i) { 1271 mValues[i].calculateValue(fraction); 1272 } 1273 if (mUpdateListeners != null) { 1274 int numListeners = mUpdateListeners.size(); 1275 for (int i = 0; i < numListeners; ++i) { 1276 mUpdateListeners.get(i).onAnimationUpdate(this); 1277 } 1278 } 1279 } 1280 1281 @Override 1282 public ValueAnimator clone() { 1283 final ValueAnimator anim = (ValueAnimator) super.clone(); 1284 if (mUpdateListeners != null) { 1285 anim.mUpdateListeners = new ArrayList<AnimatorUpdateListener>(mUpdateListeners); 1286 } 1287 anim.mSeekFraction = -1; 1288 anim.mPlayingBackwards = false; 1289 anim.mReversing = false; 1290 anim.mCurrentIteration = 0; 1291 anim.mInitialized = false; 1292 anim.mStarted = false; 1293 anim.mRunning = false; 1294 anim.mPaused = false; 1295 anim.mResumed = false; 1296 anim.mStartListenersCalled = false; 1297 anim.mStartTime = 0; 1298 anim.mStartTimeCommitted = false; 1299 anim.mAnimationEndRequested = false; 1300 anim.mPauseTime = 0; 1301 anim.mLastFrameTime = 0; 1302 anim.mCurrentFraction = 0; 1303 1304 PropertyValuesHolder[] oldValues = mValues; 1305 if (oldValues != null) { 1306 int numValues = oldValues.length; 1307 anim.mValues = new PropertyValuesHolder[numValues]; 1308 anim.mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues); 1309 for (int i = 0; i < numValues; ++i) { 1310 PropertyValuesHolder newValuesHolder = oldValues[i].clone(); 1311 anim.mValues[i] = newValuesHolder; 1312 anim.mValuesMap.put(newValuesHolder.getPropertyName(), newValuesHolder); 1313 } 1314 } 1315 return anim; 1316 } 1317 1318 /** 1319 * Implementors of this interface can add themselves as update listeners 1320 * to an <code>ValueAnimator</code> instance to receive callbacks on every animation 1321 * frame, after the current frame's values have been calculated for that 1322 * <code>ValueAnimator</code>. 1323 */ 1324 public static interface AnimatorUpdateListener { 1325 /** 1326 * <p>Notifies the occurrence of another frame of the animation.</p> 1327 * 1328 * @param animation The animation which was repeated. 1329 */ 1330 void onAnimationUpdate(ValueAnimator animation); 1331 1332 } 1333 1334 /** 1335 * Return the number of animations currently running. 1336 * 1337 * Used by StrictMode internally to annotate violations. 1338 * May be called on arbitrary threads! 1339 * 1340 * @hide 1341 */ 1342 public static int getCurrentAnimationsCount() { 1343 return AnimationHandler.getAnimationCount(); 1344 } 1345 1346 @Override 1347 public String toString() { 1348 String returnVal = "ValueAnimator@" + Integer.toHexString(hashCode()); 1349 if (mValues != null) { 1350 for (int i = 0; i < mValues.length; ++i) { 1351 returnVal += "\n " + mValues[i].toString(); 1352 } 1353 } 1354 return returnVal; 1355 } 1356 1357 /** 1358 * <p>Whether or not the ValueAnimator is allowed to run asynchronously off of 1359 * the UI thread. This is a hint that informs the ValueAnimator that it is 1360 * OK to run the animation off-thread, however ValueAnimator may decide 1361 * that it must run the animation on the UI thread anyway. For example if there 1362 * is an {@link AnimatorUpdateListener} the animation will run on the UI thread, 1363 * regardless of the value of this hint.</p> 1364 * 1365 * <p>Regardless of whether or not the animation runs asynchronously, all 1366 * listener callbacks will be called on the UI thread.</p> 1367 * 1368 * <p>To be able to use this hint the following must be true:</p> 1369 * <ol> 1370 * <li>{@link #getAnimatedFraction()} is not needed (it will return undefined values).</li> 1371 * <li>The animator is immutable while {@link #isStarted()} is true. Requests 1372 * to change values, duration, delay, etc... may be ignored.</li> 1373 * <li>Lifecycle callback events may be asynchronous. Events such as 1374 * {@link Animator.AnimatorListener#onAnimationEnd(Animator)} or 1375 * {@link Animator.AnimatorListener#onAnimationRepeat(Animator)} may end up delayed 1376 * as they must be posted back to the UI thread, and any actions performed 1377 * by those callbacks (such as starting new animations) will not happen 1378 * in the same frame.</li> 1379 * <li>State change requests ({@link #cancel()}, {@link #end()}, {@link #reverse()}, etc...) 1380 * may be asynchronous. It is guaranteed that all state changes that are 1381 * performed on the UI thread in the same frame will be applied as a single 1382 * atomic update, however that frame may be the current frame, 1383 * the next frame, or some future frame. This will also impact the observed 1384 * state of the Animator. For example, {@link #isStarted()} may still return true 1385 * after a call to {@link #end()}. Using the lifecycle callbacks is preferred over 1386 * queries to {@link #isStarted()}, {@link #isRunning()}, and {@link #isPaused()} 1387 * for this reason.</li> 1388 * </ol> 1389 * @hide 1390 */ 1391 @Override 1392 public void setAllowRunningAsynchronously(boolean mayRunAsync) { 1393 // It is up to subclasses to support this, if they can. 1394 } 1395} 1396