diff options
| author | 2010-12-07 10:51:28 -0800 | |
|---|---|---|
| committer | 2010-12-07 10:51:28 -0800 | |
| commit | 5151b325a4d7e28d4739fb7113a2575991f1eb97 (patch) | |
| tree | ae84df7500a3b94ff29137fb20b19fcf35269c95 | |
| parent | 9315c9547ae13c88f61fe42a1cda089bbc4b035b (diff) | |
| parent | e2ab7ccd385cdb6517955c719e1d2b49771bedb6 (diff) | |
Merge "Change cancel/end behavior of animations to be synchronous"
| -rw-r--r-- | api/current.xml | 50 | ||||
| -rw-r--r-- | core/java/android/animation/Animator.java | 20 | ||||
| -rw-r--r-- | core/java/android/animation/AnimatorSet.java | 58 | ||||
| -rw-r--r-- | core/java/android/animation/ObjectAnimator.java | 17 | ||||
| -rw-r--r-- | core/java/android/animation/PropertyValuesHolder.java | 61 | ||||
| -rwxr-xr-x | core/java/android/animation/ValueAnimator.java | 72 | ||||
| -rw-r--r-- | core/java/com/android/internal/widget/DrawableHolder.java | 7 |
7 files changed, 117 insertions, 168 deletions
diff --git a/api/current.xml b/api/current.xml index f02ec65baa3c..708a852884b1 100644 --- a/api/current.xml +++ b/api/current.xml @@ -20161,17 +20161,6 @@ visibility="public" > </method> -<method name="getGetter" - return="java.lang.reflect.Method" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -</method> <method name="getPropertyName" return="java.lang.String" abstract="false" @@ -20183,17 +20172,6 @@ visibility="public" > </method> -<method name="getSetter" - return="java.lang.reflect.Method" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -</method> <method name="ofFloat" return="android.animation.PropertyValuesHolder" abstract="false" @@ -20282,19 +20260,6 @@ <parameter name="values" type="float..."> </parameter> </method> -<method name="setGetter" - return="void" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="getter" type="java.lang.reflect.Method"> -</parameter> -</method> <method name="setIntValues" return="void" abstract="false" @@ -20347,19 +20312,6 @@ <parameter name="propertyName" type="java.lang.String"> </parameter> </method> -<method name="setSetter" - return="void" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="setter" type="java.lang.reflect.Method"> -</parameter> -</method> </class> <class name="RGBEvaluator" extends="java.lang.Object" @@ -250407,7 +250359,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="arg0" type="T"> +<parameter name="t" type="T"> </parameter> </method> </interface> diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java index 22e04a7ba489..5fe3644f1297 100644 --- a/core/java/android/animation/Animator.java +++ b/core/java/android/animation/Animator.java @@ -36,22 +36,36 @@ public abstract class Animator implements Cloneable { * this call, because all animation events are posted to a central timing loop so that animation * times are all synchronized on a single timing pulse on the UI thread. So the animation will * start the next time that event handler processes events. + * + * <p>The animation started by calling this method will be run on the thread that called + * this method. This thread should have a Looper on it (a runtime exception will be thrown if + * this is not the case). Also, if the animation will animate + * properties of objects in the view hierarchy, then the calling thread should be the UI + * thread for that view hierarchy.</p> + * */ public void start() { } /** * Cancels the animation. Unlike {@link #end()}, <code>cancel()</code> causes the animation to - * stop in its tracks, sending an {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to - * its listeners, followed by an {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message. + * stop in its tracks, sending an + * {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to + * its listeners, followed by an + * {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message. + * + * <p>This method must be called on the thread that is running the animation.</p> */ public void cancel() { } /** * Ends the animation. This causes the animation to assign the end value of the property being - * animated, then calling the {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on + * animated, then calling the + * {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on * its listeners. + * + * <p>This method must be called on the thread that is running the animation.</p> */ public void end() { } diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index 154e084ec591..d77dbdccef17 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -96,6 +96,9 @@ public final class AnimatorSet extends Animator { // The amount of time in ms to delay starting the animation after start() is called private long mStartDelay = 0; + // Animator used for a nonzero startDelay + private ValueAnimator mDelayAnim = null; + // How long the child animations should last in ms. The default value is negative, which // simply means that there is no duration set on the AnimatorSet. When a real duration is @@ -276,6 +279,19 @@ public final class AnimatorSet extends Animator { listener.onAnimationCancel(this); } } + if (mDelayAnim != null && mDelayAnim.isRunning()) { + // If we're currently in the startDelay period, just cancel that animator and + // send out the end event to all listeners + mDelayAnim.cancel(); + if (mListeners != null) { + ArrayList<AnimatorListener> tmpListeners = + (ArrayList<AnimatorListener>) mListeners.clone(); + for (AnimatorListener listener : tmpListeners) { + listener.onAnimationEnd(this); + } + } + return; + } if (mSortedNodes.size() > 0) { for (Node node : mSortedNodes) { node.animation.cancel(); @@ -302,6 +318,9 @@ public final class AnimatorSet extends Animator { node.animation.addListener(mSetListener); } } + if (mDelayAnim != null) { + mDelayAnim.cancel(); + } if (mSortedNodes.size() > 0) { for (Node node : mSortedNodes) { node.animation.end(); @@ -411,12 +430,25 @@ public final class AnimatorSet extends Animator { // contains the animation nodes in the correct order. sortNodes(); + int numSortedNodes = mSortedNodes.size(); + for (int i = 0; i < numSortedNodes; ++i) { + Node node = mSortedNodes.get(i); + // First, clear out the old listeners + ArrayList<AnimatorListener> oldListeners = node.animation.getListeners(); + if (oldListeners != null && oldListeners.size() > 0) { + for (AnimatorListener listener : oldListeners) { + if (listener instanceof DependencyListener) { + node.animation.removeListener(listener); + } + } + } + } + // nodesToStart holds the list of nodes to be started immediately. We don't want to // start the animations in the loop directly because we first need to set up // dependencies on all of the nodes. For example, we don't want to start an animation // when some other animation also wants to start when the first animation begins. final ArrayList<Node> nodesToStart = new ArrayList<Node>(); - int numSortedNodes = mSortedNodes.size(); for (int i = 0; i < numSortedNodes; ++i) { Node node = mSortedNodes.get(i); if (mSetListener == null) { @@ -443,19 +475,25 @@ public final class AnimatorSet extends Animator { } } else { // TODO: Need to cancel out of the delay appropriately - ValueAnimator delayAnim = ValueAnimator.ofFloat(0f, 1f); - delayAnim.setDuration(mStartDelay); - delayAnim.addListener(new AnimatorListenerAdapter() { + mDelayAnim = ValueAnimator.ofFloat(0f, 1f); + mDelayAnim.setDuration(mStartDelay); + mDelayAnim.addListener(new AnimatorListenerAdapter() { + boolean canceled = false; + public void onAnimationCancel(Animator anim) { + canceled = true; + } public void onAnimationEnd(Animator anim) { - int numNodes = nodesToStart.size(); - for (int i = 0; i < numNodes; ++i) { - Node node = nodesToStart.get(i); - node.animation.start(); - mPlayingSet.add(node.animation); + if (!canceled) { + int numNodes = nodesToStart.size(); + for (int i = 0; i < numNodes; ++i) { + Node node = nodesToStart.get(i); + node.animation.start(); + mPlayingSet.add(node.animation); + } } } }); - delayAnim.start(); + mDelayAnim.start(); } if (mListeners != null) { ArrayList<AnimatorListener> tmpListeners = diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java index 7c2e70d717b0..7f11871b0f74 100644 --- a/core/java/android/animation/ObjectAnimator.java +++ b/core/java/android/animation/ObjectAnimator.java @@ -19,6 +19,7 @@ package android.animation; import android.util.Log; import java.lang.reflect.Method; +import java.util.ArrayList; /** * This subclass of {@link ValueAnimator} provides support for animating properties on target objects. @@ -31,6 +32,7 @@ import java.lang.reflect.Method; * */ public final class ObjectAnimator extends ValueAnimator { + private static final boolean DBG = false; // The target object on which the property exists, set in the constructor private Object mTarget; @@ -265,6 +267,21 @@ public final class ObjectAnimator extends ValueAnimator { } } + @Override + public void start() { + if (DBG) { + Log.d("ObjectAnimator", "Anim target, duration" + mTarget + ", " + getDuration()); + for (int i = 0; i < mValues.length; ++i) { + PropertyValuesHolder pvh = mValues[i]; + ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes; + Log.d("ObjectAnimator", " Values[" + i + "]: " + + pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " + + keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue()); + } + } + super.start(); + } + /** * This function is called immediately before processing the first animation * frame of an animation. If there is a nonzero <code>startDelay</code>, the diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java index 97aa5a1a84e2..286c03bd5d1e 100644 --- a/core/java/android/animation/PropertyValuesHolder.java +++ b/core/java/android/animation/PropertyValuesHolder.java @@ -544,67 +544,6 @@ public class PropertyValuesHolder implements Cloneable { } /** - * Sets the <code>Method</code> that is called with the animated values calculated - * during the animation. Setting the setter method is an alternative to supplying a - * {@link #setPropertyName(String) propertyName} from which the method is derived. This - * approach is more direct, and is especially useful when a function must be called that does - * not correspond to the convention of <code>setName()</code>. For example, if a function - * called <code>offset()</code> is to be called with the animated values, there is no way - * to tell <code>ObjectAnimator</code> how to call that function simply through a property - * name, so a setter method should be supplied instead. - * - * <p>Note that the setter function must take the same parameter type as the - * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to - * the setter function will fail.</p> - * - * @param setter The setter method that should be called with the animated values. - */ - public void setSetter(Method setter) { - mSetter = setter; - } - - /** - * Gets the <code>Method</code> that is called with the animated values calculated - * during the animation. - */ - public Method getSetter() { - return mSetter; - } - - /** - * Sets the <code>Method</code> that is called to get unsupplied <code>valueFrom</code> or - * <code>valueTo</code> properties. Setting the getter method is an alternative to supplying a - * {@link #setPropertyName(String) propertyName} from which the method is derived. This - * approach is more direct, and is especially useful when a function must be called that does - * not correspond to the convention of <code>setName()</code>. For example, if a function - * called <code>offset()</code> is to be called to get an initial value, there is no way - * to tell <code>ObjectAnimator</code> how to call that function simply through a property - * name, so a getter method should be supplied instead. - * - * <p>Note that the getter method is only called whether supplied here or derived - * from the property name, if one of <code>valueFrom</code> or <code>valueTo</code> are - * null. If both of those values are non-null, then there is no need to get one of the - * values and the getter is not called. - * - * <p>Note that the getter function must return the same parameter type as the - * <code>valueFrom</code> and <code>valueTo</code> properties (whichever of them are - * non-null), otherwise the call to the getter function will fail.</p> - * - * @param getter The getter method that should be called to get initial animation values. - */ - public void setGetter(Method getter) { - mGetter = getter; - } - - /** - * Gets the <code>Method</code> that is called to get unsupplied <code>valueFrom</code> or - * <code>valueTo</code> properties. - */ - public Method getGetter() { - return mGetter; - } - - /** * Sets the name of the property that will be animated. This name is used to derive * a setter function that will be called to set animated values. * For example, a property name of <code>foo</code> will result diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index 8e541c202b57..1542c49d23a9 100755 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -62,9 +62,7 @@ public class ValueAnimator extends Animator { */ private static final int STOPPED = 0; // Not yet playing private static final int RUNNING = 1; // Playing normally - private static final int CANCELED = 2; // cancel() called - need to end it - private static final int ENDED = 3; // end() called - need to end it - private static final int SEEKED = 4; // Seeked to some time value + private static final int SEEKED = 2; // Seeked to some time value /** * Internal variables @@ -178,7 +176,7 @@ public class ValueAnimator extends Animator { * Flag that represents the current state of the animation. Used to figure out when to start * an animation (if state == STOPPED). Also used to end an animation that * has been cancel()'d or end()'d since the last animation frame. Possible values are - * STOPPED, RUNNING, ENDED, CANCELED. + * STOPPED, RUNNING, SEEKED. */ private int mPlayingState = STOPPED; @@ -581,8 +579,7 @@ public class ValueAnimator extends Animator { 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.mPlayingState == ENDED || - anim.mPlayingState == CANCELED) { + if (anim.mStartDelay == 0) { anim.startAnimation(); } else { delayedAnims.add(anim); @@ -619,14 +616,28 @@ public class ValueAnimator extends Animator { // Now process all active animations. The return value from animationFrame() // tells the handler whether it should now be ended int numAnims = animations.size(); - for (int i = 0; i < numAnims; ++i) { + 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 (int i = 0; i < endingAnims.size(); ++i) { + for (i = 0; i < endingAnims.size(); ++i) { endingAnims.get(i).endAnimation(); } endingAnims.clear(); @@ -918,9 +929,7 @@ public class ValueAnimator extends Animator { // to run if (mPlayingState != STOPPED || sPendingAnimations.get().contains(this) || sDelayedAnims.get().contains(this)) { - // Just set the CANCELED flag - this causes the animation to end the next time a frame - // is processed. - mPlayingState = CANCELED; + endAnimation(); } } @@ -929,23 +938,21 @@ public class ValueAnimator extends Animator { if (!sAnimations.get().contains(this) && !sPendingAnimations.get().contains(this)) { // Special case if the animation has not yet started; get it ready for ending mStartedDelay = false; - sPendingAnimations.get().add(this); - AnimationHandler animationHandler = sAnimationHandler.get(); - if (animationHandler == null) { - animationHandler = new AnimationHandler(); - sAnimationHandler.set(animationHandler); - } - animationHandler.sendEmptyMessage(ANIMATION_START); + startAnimation(); + } + // The final value set on the target varies, depending on whether the animation + // was supposed to repeat an odd number of times + if (mRepeatCount > 0 && (mRepeatCount & 0x01) == 1) { + animateValue(0f); + } else { + animateValue(1f); } - // Just set the ENDED flag - this causes the animation to end the next time a frame - // is processed. - mPlayingState = ENDED; + endAnimation(); } @Override public boolean isRunning() { - // ENDED or CANCELED indicate that it has been ended or canceled, but not processed yet - return (mPlayingState == RUNNING || mPlayingState == ENDED || mPlayingState == CANCELED); + return (mPlayingState == RUNNING); } /** @@ -973,6 +980,8 @@ public class ValueAnimator extends Animator { */ private void endAnimation() { sAnimations.get().remove(this); + sPendingAnimations.get().remove(this); + sDelayedAnims.get().remove(this); mPlayingState = STOPPED; if (mListeners != null) { ArrayList<AnimatorListener> tmpListeners = @@ -1014,10 +1023,6 @@ public class ValueAnimator extends Animator { * should be added to the set of active animations. */ private boolean delayedAnimationFrame(long currentTime) { - if (mPlayingState == CANCELED || mPlayingState == ENDED) { - // end the delay, process an animation frame to actually cancel it - return true; - } if (!mStartedDelay) { mStartedDelay = true; mDelayStartTime = currentTime; @@ -1088,19 +1093,6 @@ public class ValueAnimator extends Animator { } animateValue(fraction); break; - case ENDED: - // The final value set on the target varies, depending on whether the animation - // was supposed to repeat an odd number of times - if (mRepeatCount > 0 && (mRepeatCount & 0x01) == 1) { - animateValue(0f); - } else { - animateValue(1f); - } - // Fall through to set done flag - case CANCELED: - done = true; - mPlayingState = STOPPED; - break; } return done; diff --git a/core/java/com/android/internal/widget/DrawableHolder.java b/core/java/com/android/internal/widget/DrawableHolder.java index a528aeb8023f..947e0f3c01ed 100644 --- a/core/java/com/android/internal/widget/DrawableHolder.java +++ b/core/java/com/android/internal/widget/DrawableHolder.java @@ -87,15 +87,12 @@ public class DrawableHolder implements AnimatorListener { * @param property */ public void removeAnimationFor(String property) { - ArrayList<ObjectAnimator> removalList = new ArrayList<ObjectAnimator>(); - for (ObjectAnimator currentAnim : mAnimators) { + ArrayList<ObjectAnimator> removalList = (ArrayList<ObjectAnimator>)mAnimators.clone(); + for (ObjectAnimator currentAnim : removalList) { if (property.equals(currentAnim.getPropertyName())) { currentAnim.cancel(); - removalList.add(currentAnim); } } - if (DBG) Log.v(TAG, "Remove list size: " + removalList.size()); - mAnimators.removeAll(removalList); } /** |