@@ -55,11 +55,6 @@ public class ValueAnimator extends Animator {
5555 */
5656 private static float sDurationScale = 1.0f ;
5757
58- /**
59- * Messages sent to timing handler: START is sent when an animation first begins.
60- */
61- static final int ANIMATION_START = 0 ;
62-
6358 /**
6459 * Values used with internal variable mPlayingState to indicate the current state of an
6560 * animation.
@@ -504,7 +499,7 @@ public void setCurrentPlayTime(long playTime) {
504499 mPlayingState = SEEKED ;
505500 }
506501 mStartTime = currentTime - playTime ;
507- animationFrame (currentTime );
502+ doAnimationFrame (currentTime );
508503 }
509504
510505 /**
@@ -528,8 +523,9 @@ public long getCurrentPlayTime() {
528523 * the same times for calculating their values, which makes synchronizing
529524 * animations possible.
530525 *
526+ * The handler uses the Choreographer for executing periodic callbacks.
531527 */
532- private static class AnimationHandler extends Handler implements Runnable {
528+ private static class AnimationHandler implements Runnable {
533529 // The per-thread list of all active animations
534530 private final ArrayList <ValueAnimator > mAnimations = new ArrayList <ValueAnimator >();
535531
@@ -552,34 +548,13 @@ private AnimationHandler() {
552548 }
553549
554550 /**
555- * The START message is sent when an animation's start() method is called.
556- * It cannot start synchronously when start() is called
557- * because the call may be on the wrong thread, and it would also not be
558- * synchronized with other animations because it would not start on a common
559- * timing pulse. So each animation sends a START message to the handler, which
560- * causes the handler to place the animation on the active animations queue and
561- * start processing frames for that animation.
551+ * Start animating on the next frame.
562552 */
563- @ Override
564- public void handleMessage (Message msg ) {
565- switch (msg .what ) {
566- case ANIMATION_START :
567- // If there are already active animations, or if another ANIMATION_START
568- // message was processed during this frame, then the pending list may already
569- // have been cleared. If that's the case, we've already processed the
570- // active animations for this frame - don't do it again.
571- if (mPendingAnimations .size () > 0 ) {
572- doAnimationFrame ();
573- }
574- break ;
575- }
553+ public void start () {
554+ scheduleAnimation ();
576555 }
577556
578- private void doAnimationFrame () {
579- // currentTime holds the common time for all animations processed
580- // during this frame
581- long currentTime = AnimationUtils .currentAnimationTimeMillis ();
582-
557+ private void doAnimationFrame (long frameTime ) {
583558 // mPendingAnimations holds any animations that have requested to be started
584559 // We're going to clear mPendingAnimations, but starting animation may
585560 // cause more to be added to the pending list (for example, if one animation
@@ -605,7 +580,7 @@ private void doAnimationFrame() {
605580 int numDelayedAnims = mDelayedAnims .size ();
606581 for (int i = 0 ; i < numDelayedAnims ; ++i ) {
607582 ValueAnimator anim = mDelayedAnims .get (i );
608- if (anim .delayedAnimationFrame (currentTime )) {
583+ if (anim .delayedAnimationFrame (frameTime )) {
609584 mReadyAnims .add (anim );
610585 }
611586 }
@@ -626,7 +601,7 @@ private void doAnimationFrame() {
626601 int i = 0 ;
627602 while (i < numAnims ) {
628603 ValueAnimator anim = mAnimations .get (i );
629- if (anim .animationFrame ( currentTime )) {
604+ if (anim .doAnimationFrame ( frameTime )) {
630605 mEndingAnims .add (anim );
631606 }
632607 if (mAnimations .size () == numAnims ) {
@@ -652,18 +627,23 @@ private void doAnimationFrame() {
652627
653628 // If there are still active or delayed animations, schedule a future call to
654629 // onAnimate to process the next frame of the animations.
655- if (!mAnimationScheduled
656- && (!mAnimations .isEmpty () || !mDelayedAnims .isEmpty ())) {
657- mChoreographer .postCallback (Choreographer .CALLBACK_ANIMATION , this , null );
658- mAnimationScheduled = true ;
630+ if (!mAnimations .isEmpty () || !mDelayedAnims .isEmpty ()) {
631+ scheduleAnimation ();
659632 }
660633 }
661634
662635 // Called by the Choreographer.
663636 @ Override
664637 public void run () {
665638 mAnimationScheduled = false ;
666- doAnimationFrame ();
639+ doAnimationFrame (mChoreographer .getFrameTime ());
640+ }
641+
642+ private void scheduleAnimation () {
643+ if (!mAnimationScheduled ) {
644+ mChoreographer .postCallback (Choreographer .CALLBACK_ANIMATION , this , null );
645+ mAnimationScheduled = true ;
646+ }
667647 }
668648 }
669649
@@ -935,7 +915,7 @@ private void start(boolean playBackwards) {
935915 mRunning = true ;
936916 notifyStartListeners ();
937917 }
938- animationHandler .sendEmptyMessage ( ANIMATION_START );
918+ animationHandler .start ( );
939919 }
940920
941921 @ Override
@@ -1098,17 +1078,6 @@ private boolean delayedAnimationFrame(long currentTime) {
10981078 */
10991079 boolean animationFrame (long currentTime ) {
11001080 boolean done = false ;
1101-
1102- if (mPlayingState == STOPPED ) {
1103- mPlayingState = RUNNING ;
1104- if (mSeekTime < 0 ) {
1105- mStartTime = currentTime ;
1106- } else {
1107- mStartTime = currentTime - mSeekTime ;
1108- // Now that we're playing, reset the seek time
1109- mSeekTime = -1 ;
1110- }
1111- }
11121081 switch (mPlayingState ) {
11131082 case RUNNING :
11141083 case SEEKED :
@@ -1143,6 +1112,31 @@ boolean animationFrame(long currentTime) {
11431112 return done ;
11441113 }
11451114
1115+ /**
1116+ * Processes a frame of the animation, adjusting the start time if needed.
1117+ *
1118+ * @param frameTime The frame time.
1119+ * @return true if the animation has ended.
1120+ */
1121+ final boolean doAnimationFrame (long frameTime ) {
1122+ if (mPlayingState == STOPPED ) {
1123+ mPlayingState = RUNNING ;
1124+ if (mSeekTime < 0 ) {
1125+ mStartTime = frameTime ;
1126+ } else {
1127+ mStartTime = frameTime - mSeekTime ;
1128+ // Now that we're playing, reset the seek time
1129+ mSeekTime = -1 ;
1130+ }
1131+ }
1132+ // The frame time might be before the start time during the first frame of
1133+ // an animation. The "current time" must always be on or after the start
1134+ // time to avoid animating frames at negative time intervals. In practice, this
1135+ // is very rare and only happens when seeking backwards.
1136+ final long currentTime = Math .max (frameTime , mStartTime );
1137+ return animationFrame (currentTime );
1138+ }
1139+
11461140 /**
11471141 * Returns the current animation fraction, which is the elapsed/interpolated fraction used in
11481142 * the most recent frame update on the animation.
0 commit comments