summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorChet Haase <chet@google.com>2010-10-22 17:54:04 -0700
committerChet Haase <chet@google.com>2010-11-03 17:42:04 -0700
commit7c608f25d494c8a0a671e7373efbb47ca635367e (patch)
treeb3f4c1d8c429ac6523cd41a10d837b6b2f59efad /core
parent8182cd36cfbf5049f72b4be18f67c40ddef89d8b (diff)
downloadframeworks_base-7c608f25d494c8a0a671e7373efbb47ca635367e.zip
frameworks_base-7c608f25d494c8a0a671e7373efbb47ca635367e.tar.gz
frameworks_base-7c608f25d494c8a0a671e7373efbb47ca635367e.tar.bz2
optimizing for primitive types in animations
The animator classes caused autoboxing by converting primitive types (by far the most typical types used in animations) to be converted to their Object equivalents because of various APIs that required Object (like getValue() to get the animated value). This change creates factory methods on some classes instead of the former constructors so that we can create and return private type-specific subclasses which operate directly on the primitive types instead. In particular, float and int are natively supported by the animators now. Support in the APIs for double and long was removed because it seemed like these less common types did not justify the extra baggage of the added API and code. Change-Id: I6008a3883e3d6dd5225005f45f112af148e5a4ea
Diffstat (limited to 'core')
-rw-r--r--core/java/android/animation/AnimatorInflater.java38
-rw-r--r--core/java/android/animation/AnimatorSet.java56
-rw-r--r--core/java/android/animation/FloatKeyframeSet.java134
-rw-r--r--core/java/android/animation/IntKeyframeSet.java133
-rw-r--r--core/java/android/animation/Keyframe.java244
-rw-r--r--core/java/android/animation/KeyframeSet.java127
-rw-r--r--core/java/android/animation/ObjectAnimator.java64
-rw-r--r--core/java/android/animation/PropertyValuesHolder.java252
-rwxr-xr-xcore/java/android/animation/ValueAnimator.java110
-rw-r--r--core/java/android/view/View.java2
10 files changed, 731 insertions, 429 deletions
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index 4a6c460..b96391a 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -51,8 +51,6 @@ public class AnimatorInflater {
*/
private static final int VALUE_TYPE_FLOAT = 0;
private static final int VALUE_TYPE_INT = 1;
- private static final int VALUE_TYPE_DOUBLE = 2;
- private static final int VALUE_TYPE_LONG = 3;
private static final int VALUE_TYPE_COLOR = 4;
private static final int VALUE_TYPE_CUSTOM = 5;
@@ -242,42 +240,6 @@ public class AnimatorInflater {
}
break;
- case VALUE_TYPE_LONG: {
- int valueFrom;
- int valueTo;
- if (hasFrom) {
- valueFrom = a.getInteger(com.android.internal.R.styleable.Animator_valueFrom, 0);
- if (hasTo) {
- valueTo = a.getInteger(com.android.internal.R.styleable.Animator_valueTo, 0);
- anim.setLongValues(valueFrom, valueTo);
- } else {
- anim.setLongValues(valueFrom);
- }
- } else {
- valueTo = a.getInteger(com.android.internal.R.styleable.Animator_valueTo, 0);
- anim.setLongValues(valueTo);
- }
- }
- break;
-
- case VALUE_TYPE_DOUBLE: {
- double valueFrom;
- double valueTo;
- if (hasFrom) {
- valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
- if (hasTo) {
- valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
- anim.setDoubleValues(valueFrom, valueTo);
- } else {
- anim.setDoubleValues(valueFrom);
- }
- } else {
- valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
- anim.setDoubleValues(valueTo);
- }
- }
- break;
-
case VALUE_TYPE_CUSTOM: {
// TODO: How to get an 'Object' value?
float valueFrom;
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 8fc45f4..9ba9388 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -362,14 +362,18 @@ public final class AnimatorSet extends Animator {
// 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>();
- for (Node node : mSortedNodes) {
+ int numSortedNodes = mSortedNodes.size();
+ for (int i = 0; i < numSortedNodes; ++i) {
+ Node node = mSortedNodes.get(i);
if (mSetListener == null) {
mSetListener = new AnimatorSetListener(this);
}
if (node.dependencies == null || node.dependencies.size() == 0) {
nodesToStart.add(node);
} else {
- for (Dependency dependency : node.dependencies) {
+ int numDependencies = node.dependencies.size();
+ for (int j = 0; j < numDependencies; ++j) {
+ Dependency dependency = node.dependencies.get(j);
dependency.node.animation.addListener(
new DependencyListener(this, node, dependency.rule));
}
@@ -389,7 +393,9 @@ public final class AnimatorSet extends Animator {
delayAnim.setDuration(mStartDelay);
delayAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator anim) {
- for (Node node : nodesToStart) {
+ int numNodes = nodesToStart.size();
+ for (int i = 0; i < numNodes; ++i) {
+ Node node = nodesToStart.get(i);
node.animation.start();
mPlayingSet.add(node.animation);
}
@@ -399,8 +405,9 @@ public final class AnimatorSet extends Animator {
if (mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
- for (AnimatorListener listener : tmpListeners) {
- listener.onAnimationStart(this);
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationStart(this);
}
}
}
@@ -540,7 +547,9 @@ public final class AnimatorSet extends Animator {
return;
}
Dependency dependencyToRemove = null;
- for (Dependency dependency : mNode.tmpDependencies) {
+ int numDependencies = mNode.tmpDependencies.size();
+ for (int i = 0; i < numDependencies; ++i) {
+ Dependency dependency = mNode.tmpDependencies.get(i);
if (dependency.rule == mRule &&
dependency.node.animation == dependencyAnimation) {
// rule fired - remove the dependency and listener and check to
@@ -571,8 +580,9 @@ public final class AnimatorSet extends Animator {
public void onAnimationCancel(Animator animation) {
if (mPlayingSet.size() == 0) {
if (mListeners != null) {
- for (AnimatorListener listener : mListeners) {
- listener.onAnimationCancel(mAnimatorSet);
+ int numListeners = mListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ mListeners.get(i).onAnimationCancel(mAnimatorSet);
}
}
}
@@ -586,8 +596,9 @@ public final class AnimatorSet extends Animator {
animNode.done = true;
ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes;
boolean allDone = true;
- for (Node node : sortedNodes) {
- if (!node.done) {
+ int numSortedNodes = sortedNodes.size();
+ for (int i = 0; i < numSortedNodes; ++i) {
+ if (!sortedNodes.get(i).done) {
allDone = false;
break;
}
@@ -598,8 +609,9 @@ public final class AnimatorSet extends Animator {
if (mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
- for (AnimatorListener listener : tmpListeners) {
- listener.onAnimationEnd(mAnimatorSet);
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
}
}
}
@@ -629,17 +641,23 @@ public final class AnimatorSet extends Animator {
if (mNeedsSort) {
mSortedNodes.clear();
ArrayList<Node> roots = new ArrayList<Node>();
- for (Node node : mNodes) {
+ int numNodes = mNodes.size();
+ for (int i = 0; i < numNodes; ++i) {
+ Node node = mNodes.get(i);
if (node.dependencies == null || node.dependencies.size() == 0) {
roots.add(node);
}
}
ArrayList<Node> tmpRoots = new ArrayList<Node>();
while (roots.size() > 0) {
- for (Node root : roots) {
+ int numRoots = roots.size();
+ for (int i = 0; i < numRoots; ++i) {
+ Node root = roots.get(i);
mSortedNodes.add(root);
if (root.nodeDependents != null) {
- for (Node node : root.nodeDependents) {
+ int numDependents = root.nodeDependents.size();
+ for (int j = 0; j < numDependents; ++j) {
+ Node node = root.nodeDependents.get(j);
node.nodeDependencies.remove(root);
if (node.nodeDependencies.size() == 0) {
tmpRoots.add(node);
@@ -660,9 +678,13 @@ public final class AnimatorSet extends Animator {
// Doesn't need sorting, but still need to add in the nodeDependencies list
// because these get removed as the event listeners fire and the dependencies
// are satisfied
- for (Node node : mNodes) {
+ int numNodes = mNodes.size();
+ for (int i = 0; i < numNodes; ++i) {
+ Node node = mNodes.get(i);
if (node.dependencies != null && node.dependencies.size() > 0) {
- for (Dependency dependency : node.dependencies) {
+ int numDependencies = node.dependencies.size();
+ for (int j = 0; j < numDependencies; ++j) {
+ Dependency dependency = node.dependencies.get(j);
if (node.nodeDependencies == null) {
node.nodeDependencies = new ArrayList<Node>();
}
diff --git a/core/java/android/animation/FloatKeyframeSet.java b/core/java/android/animation/FloatKeyframeSet.java
new file mode 100644
index 0000000..6fad4a68
--- /dev/null
+++ b/core/java/android/animation/FloatKeyframeSet.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+import android.animation.Keyframe.FloatKeyframe;
+
+import java.util.ArrayList;
+
+/**
+ * This class holds a collection of FloatKeyframe objects and is called by ValueAnimator to calculate
+ * values between those keyframes for a given animation. The class internal to the animation
+ * package because it is an implementation detail of how Keyframes are stored and used.
+ *
+ * <p>This type-specific subclass of KeyframeSet, along with the other type-specific subclasses for
+ * int, long, and double, exists to speed up the getValue() method when there is no custom
+ * TypeEvaluator set for the animation, so that values can be calculated without autoboxing to the
+ * Object equivalents of these primitive types.</p>
+ */
+class FloatKeyframeSet extends KeyframeSet {
+ private float firstValue;
+ private float lastValue;
+ private float deltaValue;
+ private boolean firstTime = true;
+
+ public FloatKeyframeSet(FloatKeyframe... keyframes) {
+ super(keyframes);
+ }
+
+ @Override
+ public Object getValue(float fraction) {
+ return getFloatValue(fraction);
+ }
+
+ @Override
+ public FloatKeyframeSet clone() {
+ ArrayList<Keyframe> keyframes = mKeyframes;
+ int numKeyframes = mKeyframes.size();
+ FloatKeyframe[] newKeyframes = new FloatKeyframe[numKeyframes];
+ for (int i = 0; i < numKeyframes; ++i) {
+ newKeyframes[i] = (FloatKeyframe) keyframes.get(i).clone();
+ }
+ FloatKeyframeSet newSet = new FloatKeyframeSet(newKeyframes);
+ return newSet;
+ }
+
+ public float getFloatValue(float fraction) {
+ if (mNumKeyframes == 2) {
+ if (firstTime) {
+ firstTime = false;
+ firstValue = ((FloatKeyframe) mKeyframes.get(0)).getFloatValue();
+ lastValue = ((FloatKeyframe) mKeyframes.get(1)).getFloatValue();
+ deltaValue = lastValue - firstValue;
+ }
+ if (mInterpolator != null) {
+ fraction = mInterpolator.getInterpolation(fraction);
+ }
+ if (mEvaluator == null) {
+ return firstValue + fraction * deltaValue;
+ } else {
+ return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).floatValue();
+ }
+ }
+ if (fraction <= 0f) {
+ final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
+ final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(1);
+ float prevValue = prevKeyframe.getFloatValue();
+ float nextValue = nextKeyframe.getFloatValue();
+ float prevFraction = prevKeyframe.getFraction();
+ float nextFraction = nextKeyframe.getFraction();
+ final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
+ if (interpolator != null) {
+ fraction = interpolator.getInterpolation(fraction);
+ }
+ float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
+ return mEvaluator == null ?
+ prevValue + fraction * (nextValue - prevValue) :
+ ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
+ floatValue();
+ } else if (fraction >= 1f) {
+ final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 2);
+ final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 1);
+ float prevValue = prevKeyframe.getFloatValue();
+ float nextValue = nextKeyframe.getFloatValue();
+ float prevFraction = prevKeyframe.getFraction();
+ float nextFraction = nextKeyframe.getFraction();
+ final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
+ if (interpolator != null) {
+ fraction = interpolator.getInterpolation(fraction);
+ }
+ float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
+ return mEvaluator == null ?
+ prevValue + fraction * (nextValue - prevValue) :
+ ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
+ floatValue();
+ }
+ FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
+ for (int i = 1; i < mNumKeyframes; ++i) {
+ FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);
+ if (fraction < nextKeyframe.getFraction()) {
+ final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
+ if (interpolator != null) {
+ fraction = interpolator.getInterpolation(fraction);
+ }
+ float intervalFraction = (fraction - prevKeyframe.getFraction()) /
+ (nextKeyframe.getFraction() - prevKeyframe.getFraction());
+ float prevValue = prevKeyframe.getFloatValue();
+ float nextValue = nextKeyframe.getFloatValue();
+ return mEvaluator == null ?
+ prevValue + fraction * (nextValue - prevValue) :
+ ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
+ floatValue();
+ }
+ prevKeyframe = nextKeyframe;
+ }
+ // shouldn't get here
+ return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
+ }
+
+}
+
diff --git a/core/java/android/animation/IntKeyframeSet.java b/core/java/android/animation/IntKeyframeSet.java
new file mode 100644
index 0000000..14a4e3a
--- /dev/null
+++ b/core/java/android/animation/IntKeyframeSet.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+import android.animation.Keyframe.IntKeyframe;
+
+import java.util.ArrayList;
+
+/**
+ * This class holds a collection of IntKeyframe objects and is called by ValueAnimator to calculate
+ * values between those keyframes for a given animation. The class internal to the animation
+ * package because it is an implementation detail of how Keyframes are stored and used.
+ *
+ * <p>This type-specific subclass of KeyframeSet, along with the other type-specific subclasses for
+ * float, long, and double, exists to speed up the getValue() method when there is no custom
+ * TypeEvaluator set for the animation, so that values can be calculated without autoboxing to the
+ * Object equivalents of these primitive types.</p>
+ */
+class IntKeyframeSet extends KeyframeSet {
+ private int firstValue;
+ private int lastValue;
+ private int deltaValue;
+ private boolean firstTime = true;
+
+ public IntKeyframeSet(IntKeyframe... keyframes) {
+ super(keyframes);
+ }
+
+ @Override
+ public Object getValue(float fraction) {
+ return getIntValue(fraction);
+ }
+
+ @Override
+ public IntKeyframeSet clone() {
+ ArrayList<Keyframe> keyframes = mKeyframes;
+ int numKeyframes = mKeyframes.size();
+ IntKeyframe[] newKeyframes = new IntKeyframe[numKeyframes];
+ for (int i = 0; i < numKeyframes; ++i) {
+ newKeyframes[i] = (IntKeyframe) keyframes.get(i).clone();
+ }
+ IntKeyframeSet newSet = new IntKeyframeSet(newKeyframes);
+ return newSet;
+ }
+
+ public int getIntValue(float fraction) {
+ if (mNumKeyframes == 2) {
+ if (firstTime) {
+ firstTime = false;
+ firstValue = ((IntKeyframe) mKeyframes.get(0)).getIntValue();
+ lastValue = ((IntKeyframe) mKeyframes.get(1)).getIntValue();
+ deltaValue = lastValue - firstValue;
+ }
+ if (mInterpolator != null) {
+ fraction = mInterpolator.getInterpolation(fraction);
+ }
+ if (mEvaluator == null) {
+ return firstValue + (int)(fraction * deltaValue);
+ } else {
+ return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue();
+ }
+ }
+ if (fraction <= 0f) {
+ final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
+ final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(1);
+ int prevValue = prevKeyframe.getIntValue();
+ int nextValue = nextKeyframe.getIntValue();
+ float prevFraction = prevKeyframe.getFraction();
+ float nextFraction = nextKeyframe.getFraction();
+ final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
+ if (interpolator != null) {
+ fraction = interpolator.getInterpolation(fraction);
+ }
+ float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
+ return mEvaluator == null ?
+ prevValue + (int)(fraction * (nextValue - prevValue)) :
+ ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
+ intValue();
+ } else if (fraction >= 1f) {
+ final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 2);
+ final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 1);
+ int prevValue = prevKeyframe.getIntValue();
+ int nextValue = nextKeyframe.getIntValue();
+ float prevFraction = prevKeyframe.getFraction();
+ float nextFraction = nextKeyframe.getFraction();
+ final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
+ if (interpolator != null) {
+ fraction = interpolator.getInterpolation(fraction);
+ }
+ float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
+ return mEvaluator == null ?
+ prevValue + (int)(fraction * (nextValue - prevValue)) :
+ ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).intValue();
+ }
+ IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
+ for (int i = 1; i < mNumKeyframes; ++i) {
+ IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(i);
+ if (fraction < nextKeyframe.getFraction()) {
+ final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
+ if (interpolator != null) {
+ fraction = interpolator.getInterpolation(fraction);
+ }
+ float intervalFraction = (fraction - prevKeyframe.getFraction()) /
+ (nextKeyframe.getFraction() - prevKeyframe.getFraction());
+ int prevValue = prevKeyframe.getIntValue();
+ int nextValue = nextKeyframe.getIntValue();
+ return mEvaluator == null ?
+ prevValue + (int)(fraction * (nextValue - prevValue)) :
+ ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
+ intValue();
+ }
+ prevKeyframe = nextKeyframe;
+ }
+ // shouldn't get here
+ return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).intValue();
+ }
+
+}
+
diff --git a/core/java/android/animation/Keyframe.java b/core/java/android/animation/Keyframe.java
index f9a4f3c..e98719a 100644
--- a/core/java/android/animation/Keyframe.java
+++ b/core/java/android/animation/Keyframe.java
@@ -23,23 +23,27 @@ package android.animation;
* target object will animate between the value at the previous keyframe and the value at the
* next keyframe. Each keyframe also holds an optional {@link TimeInterpolator}
* object, which defines the time interpolation over the intervalue preceding the keyframe.
+ *
+ * <p>The Keyframe class itself is abstract. The type-specific factory methods will return
+ * a subclass of Keyframe specific to the type of value being stored. This is done to improve
+ * performance when dealing with the most common cases (e.g., <code>float</code> and
+ * <code>int</code> values). Other types will fall into a more general Keyframe class that
+ * treats its values as Objects. Unless your animation requires dealing with a custom type
+ * or a data structure that needs to be animated directly (and evaluated using an implementation
+ * of {@link TypeEvaluator}), you should stick to using float and int as animations using those
+ * types have lower runtime overhead than other types.</p>
*/
-public class Keyframe implements Cloneable {
+public abstract class Keyframe implements Cloneable {
/**
* The time at which mValue will hold true.
*/
- private float mFraction;
-
- /**
- * The value of the animation at the time mFraction.
- */
- private Object mValue;
+ float mFraction;
/**
* The type of the value in this Keyframe. This type is determined at construction time,
* based on the type of the <code>value</code> object passed into the constructor.
*/
- private Class mValueType;
+ Class mValueType;
/**
* The optional time interpolator for the interval preceding this keyframe. A null interpolator
@@ -48,23 +52,11 @@ public class Keyframe implements Cloneable {
private TimeInterpolator mInterpolator = null;
/**
- * Private constructor, called from the public constructors with the additional
- * <code>valueType</code> parameter.
- *
- * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
- * of time elapsed of the overall animation duration.
- * @param value The value that the object will animate to as the animation time approaches
- * the time in this keyframe, and the the value animated from as the time passes the time in
- * this keyframe.
- * @param valueType The type of the <code>value</code> object. This is used by the
- * {@link #getValue()} functionm, which is queried by {@link ValueAnimator} to determine
- * the type of {@link TypeEvaluator} to use to interpolate between values.
+ * Flag to indicate whether this keyframe has a valid value. This flag is used when an
+ * animation first starts, to populate placeholder keyframes with real values derived
+ * from the target object.
*/
- private Keyframe(float fraction, Object value, Class valueType) {
- mFraction = fraction;
- mValue = value;
- mValueType = valueType;
- }
+ boolean mHasValue = false;
/**
* Constructs a Keyframe object with the given time and value. The time defines the
@@ -78,28 +70,28 @@ public class Keyframe implements Cloneable {
* the time in this keyframe, and the the value animated from as the time passes the time in
* this keyframe.
*/
- public Keyframe(float fraction, Object value) {
- this(fraction, value, (value != null) ? value.getClass() : Object.class);
+ public static Keyframe ofInt(float fraction, int value) {
+ return new IntKeyframe(fraction, value);
}
/**
- * Constructs a Keyframe object with the given time and float value. The time defines the
+ * Constructs a Keyframe object with the given time. The value at this time will be derived
+ * from the target object when the animation first starts (note that this implies that keyframes
+ * with no initial value must be used as part of an {@link ObjectAnimator}).
+ * The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
*
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
* of time elapsed of the overall animation duration.
- * @param value The value that the object will animate to as the animation time approaches
- * the time in this keyframe, and the the value animated from as the time passes the time in
- * this keyframe.
*/
- public Keyframe(float fraction, Float value) {
- this(fraction, value, Float.class);
+ public static Keyframe ofInt(float fraction) {
+ return new IntKeyframe(fraction);
}
/**
- * Constructs a Keyframe object with the given time and integer value. The time defines the
+ * Constructs a Keyframe object with the given time and value. The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
@@ -110,28 +102,28 @@ public class Keyframe implements Cloneable {
* the time in this keyframe, and the the value animated from as the time passes the time in
* this keyframe.
*/
- public Keyframe(float fraction, Integer value) {
- this(fraction, value, Integer.class);
+ public static Keyframe ofFloat(float fraction, float value) {
+ return new FloatKeyframe(fraction, value);
}
/**
- * Constructs a Keyframe object with the given time and double value. The time defines the
+ * Constructs a Keyframe object with the given time. The value at this time will be derived
+ * from the target object when the animation first starts (note that this implies that keyframes
+ * with no initial value must be used as part of an {@link ObjectAnimator}).
+ * The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
*
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
* of time elapsed of the overall animation duration.
- * @param value The value that the object will animate to as the animation time approaches
- * the time in this keyframe, and the the value animated from as the time passes the time in
- * this keyframe.
*/
- public Keyframe(float fraction, Double value) {
- this(fraction, value, Double.class);
+ public static Keyframe ofFloat(float fraction) {
+ return new FloatKeyframe(fraction);
}
/**
- * Constructs a Keyframe object with the given time and integer value. The time defines the
+ * Constructs a Keyframe object with the given time and value. The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
@@ -142,40 +134,35 @@ public class Keyframe implements Cloneable {
* the time in this keyframe, and the the value animated from as the time passes the time in
* this keyframe.
*/
- public Keyframe(float fraction, int value) {
- this(fraction, value, int.class);
+ public static Keyframe ofObject(float fraction, Object value) {
+ return new ObjectKeyframe(fraction, value);
}
/**
- * Constructs a Keyframe object with the given time and float value. The time defines the
+ * Constructs a Keyframe object with the given time. The value at this time will be derived
+ * from the target object when the animation first starts (note that this implies that keyframes
+ * with no initial value must be used as part of an {@link ObjectAnimator}).
+ * The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
*
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
* of time elapsed of the overall animation duration.
- * @param value The value that the object will animate to as the animation time approaches
- * the time in this keyframe, and the the value animated from as the time passes the time in
- * this keyframe.
*/
- public Keyframe(float fraction, float value) {
- this(fraction, value, float.class);
+ public static Keyframe ofObject(float fraction) {
+ return new ObjectKeyframe(fraction, null);
}
/**
- * Constructs a Keyframe object with the given time and double value. The time defines the
- * time, as a proportion of an overall animation's duration, at which the value will hold true
- * for the animation. The value for the animation between keyframes will be calculated as
- * an interpolation between the values at those keyframes.
+ * Indicates whether this keyframe has a valid value. This method is called internally when
+ * an {@link ObjectAnimator} first starts; keyframes without values are assigned values at
+ * that time by deriving the value for the property from the target object.
*
- * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
- * of time elapsed of the overall animation duration.
- * @param value The value that the object will animate to as the animation time approaches
- * the time in this keyframe, and the the value animated from as the time passes the time in
- * this keyframe.
+ * @return boolean Whether this object has a value assigned.
*/
- public Keyframe(float fraction, double value) {
- this(fraction, value, double.class);
+ public boolean hasValue() {
+ return mHasValue;
}
/**
@@ -183,18 +170,14 @@ public class Keyframe implements Cloneable {
*
* @return The value for this Keyframe.
*/
- public Object getValue() {
- return mValue;
- }
+ public abstract Object getValue();
/**
* Sets the value for this Keyframe.
*
* @param value value for this Keyframe.
*/
- public void setValue(Object value) {
- mValue = value;
- }
+ public abstract void setValue(Object value);
/**
* Gets the time for this keyframe, as a fraction of the overall animation duration.
@@ -248,9 +231,128 @@ public class Keyframe implements Cloneable {
}
@Override
- public Keyframe clone() {
- Keyframe kfClone = new Keyframe(mFraction, mValue, mValueType);
- kfClone.setInterpolator(mInterpolator);
- return kfClone;
+ public abstract Keyframe clone();
+
+ /**
+ * This internal subclass is used for all types which are not int or float.
+ */
+ static class ObjectKeyframe extends Keyframe {
+
+ /**
+ * The value of the animation at the time mFraction.
+ */
+ Object mValue;
+
+ ObjectKeyframe(float fraction, Object value) {
+ mFraction = fraction;
+ mValue = value;
+ mHasValue = (value != null);
+ mValueType = mHasValue ? value.getClass() : Object.class;
+ }
+
+ public Object getValue() {
+ return mValue;
+ }
+
+ public void setValue(Object value) {
+ mValue = value;
+ mHasValue = (value != null);
+ }
+
+ @Override
+ public ObjectKeyframe clone() {
+ ObjectKeyframe kfClone = new ObjectKeyframe(getFraction(), mValue);
+ kfClone.setInterpolator(getInterpolator());
+ return kfClone;
+ }
+ }
+
+ /**
+ * Internal subclass used when the keyframe value is of type int.
+ */
+ static class IntKeyframe extends Keyframe {
+
+ /**
+ * The value of the animation at the time mFraction.
+ */
+ int mValue;
+
+ IntKeyframe(float fraction, int value) {
+ mFraction = fraction;
+ mValue = value;
+ mValueType = int.class;
+ mHasValue = true;
+ }
+
+ IntKeyframe(float fraction) {
+ mFraction = fraction;
+ mValueType = int.class;
+ }
+
+ public int getIntValue() {
+ return mValue;
+ }
+
+ public Object getValue() {
+ return mValue;
+ }
+
+ public void setValue(Object value) {
+ if (value != null && value.getClass() == Integer.class) {
+ mValue = ((Integer)value).intValue();
+ mHasValue = true;
+ }
+ }
+
+ @Override
+ public IntKeyframe clone() {
+ IntKeyframe kfClone = new IntKeyframe(getFraction(), mValue);
+ kfClone.setInterpolator(getInterpolator());
+ return kfClone;
+ }
+ }
+
+ /**
+ * Internal subclass used when the keyframe value is of type float.
+ */
+ static class FloatKeyframe extends Keyframe {
+ /**
+ * The value of the animation at the time mFraction.
+ */
+ float mValue;
+
+ FloatKeyframe(float fraction, float value) {
+ mFraction = fraction;
+ mValue = value;
+ mValueType = float.class;
+ mHasValue = true;
+ }
+
+ FloatKeyframe(float fraction) {
+ mFraction = fraction;
+ mValueType = float.class;
+ }
+
+ public float getFloatValue() {
+ return mValue;
+ }
+
+ public Object getValue() {
+ return mValue;
+ }
+
+ public void setValue(Object value) {
+ if (value != null && value.getClass() == Float.class) {
+ mValue = ((Float)value).floatValue();
+ mHasValue = true;
+ }
+ }
+
+ @Override
+ public FloatKeyframe clone() {
+ FloatKeyframe kfClone = new FloatKeyframe(getFraction(), mValue);
+ kfClone.setInterpolator(getInterpolator());
+ return kfClone;
+ }
}
}
diff --git a/core/java/android/animation/KeyframeSet.java b/core/java/android/animation/KeyframeSet.java
index 2242462..fa61b71 100644
--- a/core/java/android/animation/KeyframeSet.java
+++ b/core/java/android/animation/KeyframeSet.java
@@ -18,6 +18,9 @@ package android.animation;
import java.util.ArrayList;
import java.util.Arrays;
+import android.animation.Keyframe.IntKeyframe;
+import android.animation.Keyframe.FloatKeyframe;
+import android.animation.Keyframe.ObjectKeyframe;
/**
* This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate
@@ -26,12 +29,14 @@ import java.util.Arrays;
*/
class KeyframeSet {
- private int mNumKeyframes;
+ int mNumKeyframes;
Keyframe mFirstKeyframe;
Keyframe mLastKeyframe;
TimeInterpolator mInterpolator; // only used in the 2-keyframe case
ArrayList<Keyframe> mKeyframes; // only used when there are not 2 keyframes
+ TypeEvaluator mEvaluator;
+
public KeyframeSet(Keyframe... keyframes) {
mNumKeyframes = keyframes.length;
@@ -44,80 +49,106 @@ class KeyframeSet {
public static KeyframeSet ofInt(int... values) {
int numKeyframes = values.length;
- Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
+ IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
- keyframes[0] = new Keyframe(0f, (Object) null);
- keyframes[1] = new Keyframe(1f, values[0]);
+ keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
+ keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
} else {
- keyframes[0] = new Keyframe(0f, values[0]);
+ keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i) {
- keyframes[i] = new Keyframe((float) i / (numKeyframes - 1), values[i]);
+ keyframes[i] = (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
}
}
- return new KeyframeSet(keyframes);
+ return new IntKeyframeSet(keyframes);
}
public static KeyframeSet ofFloat(float... values) {
int numKeyframes = values.length;
- Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
+ FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
- keyframes[0] = new Keyframe(0f, (Object) null);
- keyframes[1] = new Keyframe(1f, values[0]);
+ keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
+ keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
} else {
- keyframes[0] = new Keyframe(0f, values[0]);
+ keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i) {
- keyframes[i] = new Keyframe((float) i / (numKeyframes - 1), values[i]);
+ keyframes[i] = (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
}
}
- return new KeyframeSet(keyframes);
+ return new FloatKeyframeSet(keyframes);
}
- public static KeyframeSet ofDouble(double... values) {
- int numKeyframes = values.length;
- Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
- if (numKeyframes == 1) {
- keyframes[0] = new Keyframe(0f, (Object) null);
- keyframes[1] = new Keyframe(1f, values[0]);
- } else {
- keyframes[0] = new Keyframe(0f, values[0]);
- for (int i = 1; i < numKeyframes; ++i) {
- keyframes[i] = new Keyframe((float) i / (numKeyframes - 1), values[i]);
+ public static KeyframeSet ofKeyframe(Keyframe... keyframes) {
+ // if all keyframes of same primitive type, create the appropriate KeyframeSet
+ int numKeyframes = keyframes.length;
+ boolean hasFloat = false;
+ boolean hasInt = false;
+ boolean hasOther = false;
+ for (int i = 0; i < numKeyframes; ++i) {
+ if (keyframes[i] instanceof FloatKeyframe) {
+ hasFloat = true;
+ } else if (keyframes[i] instanceof IntKeyframe) {
+ hasInt = true;
+ } else {
+ hasOther = true;
}
}
- return new KeyframeSet(keyframes);
- }
-
- public static KeyframeSet ofLong(long... values) {
- int numKeyframes = values.length;
- Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
- if (numKeyframes == 1) {
- keyframes[0] = new Keyframe(0f, (Object) null);
- keyframes[1] = new Keyframe(1f, values[0]);
- } else {
- keyframes[0] = new Keyframe(0f, values[0]);
- for (int i = 1; i < numKeyframes; ++i) {
- keyframes[i] = new Keyframe((float) i / (numKeyframes - 1), values[i]);
+ if (hasFloat && !hasInt && !hasOther) {
+ FloatKeyframe floatKeyframes[] = new FloatKeyframe[numKeyframes];
+ for (int i = 0; i < numKeyframes; ++i) {
+ floatKeyframes[i] = (FloatKeyframe) keyframes[i];
+ }
+ return new FloatKeyframeSet(floatKeyframes);
+ } else if (hasInt && !hasFloat && !hasOther) {
+ IntKeyframe intKeyframes[] = new IntKeyframe[numKeyframes];
+ for (int i = 0; i < numKeyframes; ++i) {
+ intKeyframes[i] = (IntKeyframe) keyframes[i];
}
+ return new IntKeyframeSet(intKeyframes);
+ } else {
+ return new KeyframeSet(keyframes);
}
- return new KeyframeSet(keyframes);
}
public static KeyframeSet ofObject(Object... values) {
int numKeyframes = values.length;
- Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
+ ObjectKeyframe keyframes[] = new ObjectKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
- keyframes[0] = new Keyframe(0f, (Object) null);
- keyframes[1] = new Keyframe(1f, values[0]);
+ keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f);
+ keyframes[1] = (ObjectKeyframe) Keyframe.ofObject(1f, values[0]);
} else {
- keyframes[0] = new Keyframe(0f, values[0]);
+ keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i) {
- keyframes[i] = new Keyframe((float) i / (numKeyframes - 1), values[i]);
+ keyframes[i] = (ObjectKeyframe) Keyframe.ofObject((float) i / (numKeyframes - 1), values[i]);
}
}
return new KeyframeSet(keyframes);
}
/**
+ * Sets the TypeEvaluator to be used when calculating animated values. This object
+ * is required only for KeyframeSets that are not either IntKeyframeSet or FloatKeyframeSet,
+ * both of which assume their own evaluator to speed up calculations with those primitive
+ * types.
+ *
+ * @param evaluator The TypeEvaluator to be used to calculate animated values.
+ */
+ public void setEvaluator(TypeEvaluator evaluator) {
+ mEvaluator = evaluator;
+ }
+
+ @Override
+ public KeyframeSet clone() {
+ ArrayList<Keyframe> keyframes = mKeyframes;
+ int numKeyframes = mKeyframes.size();
+ Keyframe[] newKeyframes = new Keyframe[numKeyframes];
+ for (int i = 0; i < numKeyframes; ++i) {
+ newKeyframes[i] = keyframes.get(i).clone();
+ }
+ KeyframeSet newSet = new KeyframeSet(newKeyframes);
+ return newSet;
+ }
+
+ /**
* Gets the animated value, given the elapsed fraction of the animation (interpolated by the
* animation's interpolator) and the evaluator used to calculate in-between values. This
* function maps the input fraction to the appropriate keyframe interval and a fraction
@@ -127,20 +158,18 @@ class KeyframeSet {
* just using the two keyframes at the appropriate end when the value is outside those bounds.
*
* @param fraction The elapsed fraction of the animation
- * @param evaluator The type evaluator to use when calculating the interpolated values.
* @return The animated value.
*/
- public Object getValue(float fraction, TypeEvaluator evaluator) {
+ public Object getValue(float fraction) {
// Special-case optimization for the common case of only two keyframes
if (mNumKeyframes == 2) {
if (mInterpolator != null) {
fraction = mInterpolator.getInterpolation(fraction);
}
- return evaluator.evaluate(fraction, mFirstKeyframe.getValue(),
+ return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
mLastKeyframe.getValue());
}
-
if (fraction <= 0f) {
final Keyframe nextKeyframe = mKeyframes.get(1);
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
@@ -150,7 +179,7 @@ class KeyframeSet {
final float prevFraction = mFirstKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(nextKeyframe.getFraction() - prevFraction);
- return evaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
+ return mEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
nextKeyframe.getValue());
} else if (fraction >= 1f) {
final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
@@ -161,7 +190,7 @@ class KeyframeSet {
final float prevFraction = prevKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(mLastKeyframe.getFraction() - prevFraction);
- return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
+ return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
mLastKeyframe.getValue());
}
Keyframe prevKeyframe = mFirstKeyframe;
@@ -175,7 +204,7 @@ class KeyframeSet {
final float prevFraction = prevKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(nextKeyframe.getFraction() - prevFraction);
- return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
+ return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
nextKeyframe.getValue());
}
prevKeyframe = nextKeyframe;
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index 5b8c91d..c898ae3 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -175,48 +175,6 @@ public final class ObjectAnimator extends ValueAnimator {
}
/**
- * Constructs and returns an ObjectAnimator that animates between long values. A single
- * value implies that that value is the one being animated to. However, this is not typically
- * useful in a ValueAnimator object because there is no way for the object to determine the
- * starting value for the animation (unlike ObjectAnimator, which can derive that value
- * from the target object and property being animated). Therefore, there should typically
- * be two or more values.
- *
- * @param target The object whose property is to be animated. This object should
- * have a public method on it called <code>setName()</code>, where <code>name</code> is
- * the value of the <code>propertyName</code> parameter.
- * @param propertyName The name of the property being animated.
- * @param values A set of values that the animation will animate between over time.
- * @return A ValueAnimator object that is set up to animate between the given values.
- */
- public static ObjectAnimator ofLong(Object target, String propertyName, long... values) {
- ObjectAnimator anim = new ObjectAnimator(target, propertyName);
- anim.setLongValues(values);
- return anim;
- }
-
- /**
- * Constructs and returns an ObjectAnimator that animates between double values. A single
- * value implies that that value is the one being animated to. However, this is not typically
- * useful in a ValueAnimator object because there is no way for the object to determine the
- * starting value for the animation (unlike ObjectAnimator, which can derive that value
- * from the target object and property being animated). Therefore, there should typically
- * be two or more values.
- *
- * @param target The object whose property is to be animated. This object should
- * have a public method on it called <code>setName()</code>, where <code>name</code> is
- * the value of the <code>propertyName</code> parameter.
- * @param propertyName The name of the property being animated.
- * @param values A set of values that the animation will animate between over time.
- * @return A ValueAnimator object that is set up to animate between the given values.
- */
- public static ObjectAnimator ofDouble(Object target, String propertyName, double... values) {
- ObjectAnimator anim = new ObjectAnimator(target, propertyName);
- anim.setDoubleValues(values);
- return anim;
- }
-
- /**
* A constructor that takes <code>PropertyValueHolder</code> values. This constructor should
* be used when animating several properties at once with the same ObjectAnimator, since
* PropertyValuesHolder allows you to associate a set of animation values with a property
@@ -287,28 +245,6 @@ public final class ObjectAnimator extends ValueAnimator {
}
@Override
- public void setDoubleValues(double... values) {
- if (mValues == null || mValues.length == 0) {
- // No values yet - this animator is being constructed piecemeal. Init the values with
- // whatever the current propertyName is
- setValues(PropertyValuesHolder.ofDouble(mPropertyName, values));
- } else {
- super.setDoubleValues(values);
- }
- }
-
- @Override
- public void setLongValues(long... values) {
- if (mValues == null || mValues.length == 0) {
- // No values yet - this animator is being constructed piecemeal. Init the values with
- // whatever the current propertyName is
- setValues(PropertyValuesHolder.ofLong(mPropertyName, values));
- } else {
- super.setLongValues(values);
- }
- }
-
- @Override
public void setObjectValues(Object... values) {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index 0f759f1..39754b0 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -17,6 +17,8 @@
package android.animation;
import android.util.Log;
+import android.animation.Keyframe.IntKeyframe;
+import android.animation.Keyframe.FloatKeyframe;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -45,7 +47,7 @@ public class PropertyValuesHolder implements Cloneable {
* property can be manually set via setSetter(). Otherwise, it is automatically
* derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
*/
- private Method mSetter = null;
+ Method mSetter = null;
/**
* The getter function, if needed. ObjectAnimator hands off this functionality to
@@ -60,12 +62,12 @@ public class PropertyValuesHolder implements Cloneable {
* The type of values supplied. This information is used both in deriving the setter/getter
* functions and in deriving the type of TypeEvaluator.
*/
- private Class mValueType;
+ Class mValueType;
/**
* The set of keyframes (time/value pairs) that define this animation.
*/
- private KeyframeSet mKeyframeSet = null;
+ KeyframeSet mKeyframeSet = null;
// type evaluators for the three primitive types handled by this implementation
@@ -100,7 +102,7 @@ public class PropertyValuesHolder implements Cloneable {
private ReentrantReadWriteLock propertyMapLock = new ReentrantReadWriteLock();
// Used to pass single value to varargs parameter in setter invocation
- private Object[] mTmpValueArray = new Object[1];
+ Object[] mTmpValueArray = new Object[1];
/**
* The type evaluator used to calculate the animated values. This evaluator is determined
@@ -133,8 +135,7 @@ public class PropertyValuesHolder implements Cloneable {
* @return PropertyValuesHolder The constructed PropertyValuesHolder object.
*/
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
- PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
- pvh.setIntValues(values);
+ PropertyValuesHolder pvh = new IntPropertyValuesHolder(propertyName, values);
return pvh;
}
@@ -146,34 +147,7 @@ public class PropertyValuesHolder implements Cloneable {
* @return PropertyValuesHolder The constructed PropertyValuesHolder object.
*/
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
- PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
- pvh.setFloatValues(values);
- return pvh;
- }
-
- /**
- * Constructs and returns a PropertyValuesHolder with a given property name and
- * set of double values.
- * @param propertyName The name of the property being animated.
- * @param values The values that the named property will animate between.
- * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
- */
- public static PropertyValuesHolder ofDouble(String propertyName, double... values) {
- PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
- pvh.setDoubleValues(values);
- return pvh;
- }
-
- /**
- * Constructs and returns a PropertyValuesHolder with a given property name and
- * set of long values.
- * @param propertyName The name of the property being animated.
- * @param values The values that the named property will animate between.
- * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
- */
- public static PropertyValuesHolder ofLong(String propertyName, long... values) {
- PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
- pvh.setLongValues(values);
+ PropertyValuesHolder pvh = new FloatPropertyValuesHolder(propertyName, values);
return pvh;
}
@@ -218,9 +192,18 @@ public class PropertyValuesHolder implements Cloneable {
* @param values The set of values to animate between.
*/
public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
- PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
- pvh.setKeyframes(values);
- return pvh;
+ KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
+ if (keyframeSet instanceof IntKeyframeSet) {
+ return new IntPropertyValuesHolder(propertyName, (IntKeyframeSet) keyframeSet);
+ } else if (keyframeSet instanceof FloatKeyframeSet) {
+ return new FloatPropertyValuesHolder(propertyName, (FloatKeyframeSet) keyframeSet);
+ }
+ else {
+ PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
+ pvh.mKeyframeSet = keyframeSet;
+ pvh.mValueType = ((Keyframe)values[0]).getType();
+ return pvh;
+ }
}
/**
@@ -262,44 +245,6 @@ public class PropertyValuesHolder implements Cloneable {
}
/**
- * Set the animated values for this object to this set of doubles.
- * If there is only one value, it is assumed to be the end value of an animation,
- * and an initial value will be derived, if possible, by calling a getter function
- * on the object. Also, if any value is null, the value will be filled in when the animation
- * starts in the same way. This mechanism of automatically getting null values only works
- * if the PropertyValuesHolder object is used in conjunction
- * {@link ObjectAnimator}, and with a getter function either
- * derived automatically from <code>propertyName</code> or set explicitly via
- * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
- * no way of determining what the value should be.
- *
- * @param values One or more values that the animation will animate between.
- */
- public void setDoubleValues(double... values) {
- mValueType = double.class;
- mKeyframeSet = KeyframeSet.ofDouble(values);
- }
-
- /**
- * Set the animated values for this object to this set of longs.
- * If there is only one value, it is assumed to be the end value of an animation,
- * and an initial value will be derived, if possible, by calling a getter function
- * on the object. Also, if any value is null, the value will be filled in when the animation
- * starts in the same way. This mechanism of automatically getting null values only works
- * if the PropertyValuesHolder object is used in conjunction
- * {@link ObjectAnimator}, and with a getter function either
- * derived automatically from <code>propertyName</code> or set explicitly via
- * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
- * no way of determining what the value should be.
- *
- * @param values One or more values that the animation will animate between.
- */
- public void setLongValues(long... values) {
- mValueType = long.class;
- mKeyframeSet = KeyframeSet.ofLong(values);
- }
-
- /**
* Set the animated values for this object to this set of Keyframes.
*
* @param values One or more values that the animation will animate between.
@@ -466,7 +411,7 @@ public class PropertyValuesHolder implements Cloneable {
setupSetter(targetClass);
}
for (Keyframe kf : mKeyframeSet.mKeyframes) {
- if (kf.getValue() == null) {
+ if (!kf.hasValue()) {
if (mGetter == null) {
setupGetter(targetClass);
}
@@ -528,14 +473,18 @@ public class PropertyValuesHolder implements Cloneable {
@Override
public PropertyValuesHolder clone() {
- ArrayList<Keyframe> keyframes = mKeyframeSet.mKeyframes;
- int numKeyframes = mKeyframeSet.mKeyframes.size();
- Keyframe[] newKeyframes = new Keyframe[numKeyframes];
- for (int i = 0; i < numKeyframes; ++i) {
- newKeyframes[i] = keyframes.get(i).clone();
+ try {
+ PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
+ newPVH.mPropertyName = mPropertyName;
+ newPVH.mKeyframeSet = mKeyframeSet.clone();
+ newPVH.mEvaluator = mEvaluator;
+ return newPVH;
+ } catch (CloneNotSupportedException e) {
+ // won't reach here
+ return null;
}
- return PropertyValuesHolder.ofKeyframe(mPropertyName, newKeyframes);
}
+
/**
* Internal function to set the value on the target object, using the setter set up
* earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
@@ -546,7 +495,7 @@ public class PropertyValuesHolder implements Cloneable {
void setAnimatedValue(Object target) {
if (mSetter != null) {
try {
- mTmpValueArray[0] = mAnimatedValue;
+ mTmpValueArray[0] = getAnimatedValue();
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
@@ -562,9 +511,16 @@ public class PropertyValuesHolder implements Cloneable {
*/
void init() {
if (mEvaluator == null) {
- mEvaluator = (mValueType == int.class || mValueType == Integer.class) ? sIntEvaluator :
- (mValueType == double.class || mValueType == Double.class) ? sDoubleEvaluator :
- sFloatEvaluator;
+ // We already handle int, float, long, double automatically, but not their Object
+ // equivalents
+ mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
+ (mValueType == Float.class) ? sFloatEvaluator :
+ null;
+ }
+ if (mEvaluator != null) {
+ // KeyframeSet knows how to evaluate the common types - only give it a custom
+ // evaulator if one has been set on this class
+ mKeyframeSet.setEvaluator(mEvaluator);
}
}
@@ -578,8 +534,9 @@ public class PropertyValuesHolder implements Cloneable {
* and Integer) are correctly interpolated; all other types require setting a TypeEvaluator.
* @param evaluator
*/
- public void setEvaluator(TypeEvaluator evaluator) {
+ public void setEvaluator(TypeEvaluator evaluator) {
mEvaluator = evaluator;
+ mKeyframeSet.setEvaluator(evaluator);
}
/**
@@ -587,11 +544,9 @@ public class PropertyValuesHolder implements Cloneable {
* this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
*
* @param fraction The elapsed, interpolated fraction of the animation.
- * @return The calculated value at this point in the animation.
*/
- Object calculateValue(float fraction) {
- mAnimatedValue = mKeyframeSet.getValue(fraction, mEvaluator);
- return mAnimatedValue;
+ void calculateValue(float fraction) {
+ mAnimatedValue = mKeyframeSet.getValue(fraction);
}
/**
@@ -694,4 +649,119 @@ public class PropertyValuesHolder implements Cloneable {
Object getAnimatedValue() {
return mAnimatedValue;
}
+
+ static class IntPropertyValuesHolder extends PropertyValuesHolder {
+
+ IntKeyframeSet mIntKeyframeSet;
+ int mIntAnimatedValue;
+
+ public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) {
+ super(propertyName);
+ mValueType = int.class;
+ mKeyframeSet = keyframeSet;
+ mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
+ }
+
+ public IntPropertyValuesHolder(String propertyName, int... values) {
+ super(propertyName);
+ setIntValues(values);
+ mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
+ }
+
+ @Override
+ void calculateValue(float fraction) {
+ mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction);
+ }
+
+ @Override
+ Object getAnimatedValue() {
+ return mIntAnimatedValue;
+ }
+
+ @Override
+ public IntPropertyValuesHolder clone() {
+ IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
+ newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet;
+ return newPVH;
+ }
+
+ /**
+ * Internal function to set the value on the target object, using the setter set up
+ * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
+ * to handle turning the value calculated by ValueAnimator into a value set on the object
+ * according to the name of the property.
+ * @param target The target object on which the value is set
+ */
+ @Override
+ void setAnimatedValue(Object target) {
+ if (mSetter != null) {
+ try {
+ mTmpValueArray[0] = mIntAnimatedValue;
+ mSetter.invoke(target, mTmpValueArray);
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+ }
+ }
+
+ static class FloatPropertyValuesHolder extends PropertyValuesHolder {
+
+ FloatKeyframeSet mFloatKeyframeSet;
+ float mFloatAnimatedValue;
+
+ public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
+ super(propertyName);
+ mValueType = float.class;
+ mKeyframeSet = keyframeSet;
+ mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
+ }
+
+ public FloatPropertyValuesHolder(String propertyName, float... values) {
+ super(propertyName);
+ setFloatValues(values);
+ mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
+ }
+
+ @Override
+ void calculateValue(float fraction) {
+ mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
+ }
+
+ @Override
+ Object getAnimatedValue() {
+ return mFloatAnimatedValue;
+ }
+
+ @Override
+ public FloatPropertyValuesHolder clone() {
+ FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
+ newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet;
+ return newPVH;
+ }
+
+ /**
+ * Internal function to set the value on the target object, using the setter set up
+ * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
+ * to handle turning the value calculated by ValueAnimator into a value set on the object
+ * according to the name of the property.
+ * @param target The target object on which the value is set
+ */
+ @Override
+ void setAnimatedValue(Object target) {
+ if (mSetter != null) {
+ try {
+ mTmpValueArray[0] = mFloatAnimatedValue;
+ mSetter.invoke(target, mTmpValueArray);
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+ }
+
+ }
} \ No newline at end of file
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index a0b70b5..b021e75 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -297,40 +297,6 @@ public class ValueAnimator extends Animator {
}
/**
- * Constructs and returns a ValueAnimator that animates between double values. A single
- * value implies that that value is the one being animated to. However, this is not typically
- * useful in a ValueAnimator object because there is no way for the object to determine the
- * starting value for the animation (unlike ObjectAnimator, which can derive that value
- * from the target object and property being animated). Therefore, there should typically
- * be two or more values.
- *
- * @param values A set of values that the animation will animate between over time.
- * @return A ValueAnimator object that is set up to animate between the given values.
- */
- public static ValueAnimator ofDouble(double... values) {
- ValueAnimator anim = new ValueAnimator();
- anim.setDoubleValues(values);
- return anim;
- }
-
- /**
- * Constructs and returns a ValueAnimator that animates between long values. A single
- * value implies that that value is the one being animated to. However, this is not typically
- * useful in a ValueAnimator object because there is no way for the object to determine the
- * starting value for the animation (unlike ObjectAnimator, which can derive that value
- * from the target object and property being animated). Therefore, there should typically
- * be two or more values.
- *
- * @param values A set of values that the animation will animate between over time.
- * @return A ValueAnimator object that is set up to animate between the given values.
- */
- public static ValueAnimator ofLong(long... values) {
- ValueAnimator anim = new ValueAnimator();
- anim.setLongValues(values);
- return anim;
- }
-
- /**
* Constructs and returns a ValueAnimator that animates between the values
* specified in the PropertyValuesHolder objects.
*
@@ -425,62 +391,6 @@ public class ValueAnimator extends Animator {
}
/**
- * Sets long values that will be animated between. A single
- * value implies that that value is the one being animated to. However, this is not typically
- * useful in a ValueAnimator object because there is no way for the object to determine the
- * starting value for the animation (unlike ObjectAnimator, which can derive that value
- * from the target object and property being animated). Therefore, there should typically
- * be two or more values.
- *
- * <p>If there are already multiple sets of values defined for this ValueAnimator via more
- * than one PropertyValuesHolder object, this method will set the values for the first
- * of those objects.</p>
- *
- * @param values A set of values that the animation will animate between over time.
- */
- public void setLongValues(long... values) {
- if (values == null || values.length == 0) {
- return;
- }
- if (mValues == null || mValues.length == 0) {
- setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofLong("", values)});
- } else {
- PropertyValuesHolder valuesHolder = mValues[0];
- valuesHolder.setLongValues(values);
- }
- // New property/values/target should cause re-initialization prior to starting
- mInitialized = false;
- }
-
- /**
- * Sets double values that will be animated between. A single
- * value implies that that value is the one being animated to. However, this is not typically
- * useful in a ValueAnimator object because there is no way for the object to determine the
- * starting value for the animation (unlike ObjectAnimator, which can derive that value
- * from the target object and property being animated). Therefore, there should typically
- * be two or more values.
- *
- * <p>If there are already multiple sets of values defined for this ValueAnimator via more
- * than one PropertyValuesHolder object, this method will set the values for the first
- * of those objects.</p>
- *
- * @param values A set of values that the animation will animate between over time.
- */
- public void setDoubleValues(double... values) {
- if (values == null || values.length == 0) {
- return;
- }
- if (mValues == null || mValues.length == 0) {
- setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofDouble("", values)});
- } else {
- PropertyValuesHolder valuesHolder = mValues[0];
- valuesHolder.setDoubleValues(values);
- }
- // New property/values/target should cause re-initialization prior to starting
- mInitialized = false;
- }
-
- /**
* Sets the values to animate between for this animation. A single
* value implies that that value is the one being animated to. However, this is not typically
* useful in a ValueAnimator object because there is no way for the object to determine the
@@ -968,8 +878,9 @@ public class ValueAnimator extends Animator {
if (mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
- for (AnimatorListener listener : tmpListeners) {
- listener.onAnimationStart(this);
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationStart(this);
}
}
// This sets the initial value of the animation, prior to actually starting it running
@@ -1059,8 +970,9 @@ public class ValueAnimator extends Animator {
if (mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
- for (AnimatorListener listener : tmpListeners) {
- listener.onAnimationEnd(this);
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationEnd(this);
}
}
}
@@ -1077,8 +989,9 @@ public class ValueAnimator extends Animator {
// just for delayed animations
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
- for (AnimatorListener listener : tmpListeners) {
- listener.onAnimationStart(this);
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationStart(this);
}
}
}
@@ -1147,8 +1060,9 @@ public class ValueAnimator extends Animator {
if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
// Time to repeat
if (mListeners != null) {
- for (AnimatorListener listener : mListeners) {
- listener.onAnimationRepeat(this);
+ int numListeners = mListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ mListeners.get(i).onAnimationRepeat(this);
}
}
++mCurrentIteration;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a9f0780..7276044 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8288,7 +8288,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
(ArrayList<OnLayoutChangeListener>) mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
for (int i = 0; i < numListeners; ++i) {
- listenersCopy.get(i).onLayoutChange(this, l, r, t, b, oldL, oldT, oldR, oldB);
+ listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
}
}
}