/** * Sets the position of the animation to the specified point in time. This time should be between * 0 and the total duration of the animation, including any repetition. If the animation has not * yet been started, then it will not advance forward after it is set to this time; it will simply * set the time to this value and perform any appropriate actions based on that time. If the * animation is already running, then setCurrentPlayTime() will set the current playing time to * this value and continue playing from that point. * * @param playTime The time, in milliseconds, to which the animation is advanced or rewound. */ public void setCurrentPlayTime(long playTime) { initAnimation(); long currentTime = AnimationUtils.currentAnimationTimeMillis(); if (mPlayingState != RUNNING) { mSeekTime = playTime; mPlayingState = SEEKED; } mStartTime = currentTime - playTime; animationFrame(currentTime); }
/** * There are only two messages that we care about: ANIMATION_START and ANIMATION_FRAME. The * START message is sent when an animation's start() method is called. It cannot start * synchronously when start() is called because the call may be on the wrong thread, and it * would also not be synchronized with other animations because it would not start on a common * timing pulse. So each animation sends a START message to the handler, which causes the * handler to place the animation on the active animations queue and start processing frames for * that animation. The FRAME message is the one that is sent over and over while there are any * active animations to process. */ public void handleMessage(Message msg) { boolean callAgain = true; ArrayList<ValueAnimator> animations = sAnimations.get(); ArrayList<ValueAnimator> delayedAnims = sDelayedAnims.get(); switch (msg.what) { // TODO: should we avoid sending frame message when starting if we // were already running? case ANIMATION_START: ArrayList<ValueAnimator> pendingAnimations = sPendingAnimations.get(); if (animations.size() > 0 || delayedAnims.size() > 0) { callAgain = false; } // pendingAnims holds any animations that have requested to be started // We're going to clear sPendingAnimations, but starting animation may // cause more to be added to the pending list (for example, if one animation // starting triggers another starting). So we loop until sPendingAnimations // is empty. while (pendingAnimations.size() > 0) { ArrayList<ValueAnimator> pendingCopy = (ArrayList<ValueAnimator>) pendingAnimations.clone(); pendingAnimations.clear(); int count = pendingCopy.size(); for (int i = 0; i < count; ++i) { ValueAnimator anim = pendingCopy.get(i); // If the animation has a startDelay, place it on the delayed list if (anim.mStartDelay == 0) { anim.startAnimation(); } else { delayedAnims.add(anim); } } } // fall through to process first frame of new animations case ANIMATION_FRAME: // currentTime holds the common time for all animations processed // during this frame long currentTime = AnimationUtils.currentAnimationTimeMillis(); ArrayList<ValueAnimator> readyAnims = sReadyAnims.get(); ArrayList<ValueAnimator> endingAnims = sEndingAnims.get(); // First, process animations currently sitting on the delayed queue, adding // them to the active animations if they are ready int numDelayedAnims = delayedAnims.size(); for (int i = 0; i < numDelayedAnims; ++i) { ValueAnimator anim = delayedAnims.get(i); if (anim.delayedAnimationFrame(currentTime)) { readyAnims.add(anim); } } int numReadyAnims = readyAnims.size(); if (numReadyAnims > 0) { for (int i = 0; i < numReadyAnims; ++i) { ValueAnimator anim = readyAnims.get(i); anim.startAnimation(); anim.mRunning = true; delayedAnims.remove(anim); } readyAnims.clear(); } // Now process all active animations. The return value from animationFrame() // tells the handler whether it should now be ended int numAnims = animations.size(); int i = 0; while (i < numAnims) { ValueAnimator anim = animations.get(i); if (anim.animationFrame(currentTime)) { endingAnims.add(anim); } if (animations.size() == numAnims) { ++i; } else { // An animation might be canceled or ended by client code // during the animation frame. Check to see if this happened by // seeing whether the current index is the same as it was before // calling animationFrame(). Another approach would be to copy // animations to a temporary list and process that list instead, // but that entails garbage and processing overhead that would // be nice to avoid. --numAnims; endingAnims.remove(anim); } } if (endingAnims.size() > 0) { for (i = 0; i < endingAnims.size(); ++i) { endingAnims.get(i).endAnimation(); } endingAnims.clear(); } // If there are still active or delayed animations, call the handler again // after the frameDelay if (callAgain && (!animations.isEmpty() || !delayedAnims.isEmpty())) { sendEmptyMessageDelayed( ANIMATION_FRAME, Math.max( 0, sFrameDelay - (AnimationUtils.currentAnimationTimeMillis() - currentTime))); } break; } }