summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Chet Haase <chet@google.com> 2010-12-07 10:51:28 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2010-12-07 10:51:28 -0800
commit5151b325a4d7e28d4739fb7113a2575991f1eb97 (patch)
treeae84df7500a3b94ff29137fb20b19fcf35269c95
parent9315c9547ae13c88f61fe42a1cda089bbc4b035b (diff)
parente2ab7ccd385cdb6517955c719e1d2b49771bedb6 (diff)
Merge "Change cancel/end behavior of animations to be synchronous"
-rw-r--r--api/current.xml50
-rw-r--r--core/java/android/animation/Animator.java20
-rw-r--r--core/java/android/animation/AnimatorSet.java58
-rw-r--r--core/java/android/animation/ObjectAnimator.java17
-rw-r--r--core/java/android/animation/PropertyValuesHolder.java61
-rwxr-xr-xcore/java/android/animation/ValueAnimator.java72
-rw-r--r--core/java/com/android/internal/widget/DrawableHolder.java7
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);
}
/**