diff options
| author | 2010-08-20 06:27:27 -0700 | |
|---|---|---|
| committer | 2010-08-20 06:27:27 -0700 | |
| commit | 1d8b34ecbb97063db49ae2f55a91f44fbafbb1f2 (patch) | |
| tree | 7cb9f3f09ae718ca0c0b8933c6bad0b2b43b1237 | |
| parent | a2c1583eacec13696449038f3b29cf0191f6d0e3 (diff) | |
| parent | cf6f0d58a8444427a5aea372af86fca89386f987 (diff) | |
Merge branch 'master' of ssh://android-git:29418/platform/frameworks/base
| -rw-r--r-- | api/current.xml | 226 | ||||
| -rwxr-xr-x | core/java/android/animation/Animator.java | 314 | ||||
| -rw-r--r-- | core/java/android/animation/DoubleEvaluator.java | 4 | ||||
| -rw-r--r-- | core/java/android/animation/FloatEvaluator.java | 4 | ||||
| -rw-r--r-- | core/java/android/animation/IntEvaluator.java | 4 | ||||
| -rw-r--r-- | core/java/android/animation/PropertyAnimator.java | 407 | ||||
| -rw-r--r-- | core/java/android/animation/PropertyValuesHolder.java | 514 |
7 files changed, 742 insertions, 731 deletions
diff --git a/api/current.xml b/api/current.xml index 9c35a53613fb..3de7fca4c3d1 100644 --- a/api/current.xml +++ b/api/current.xml @@ -19952,67 +19952,24 @@ > <parameter name="duration" type="long"> </parameter> -<parameter name="keyframes" type="android.animation.Keyframe..."> +<parameter name="values" type="T..."> </parameter> </constructor> -<constructor name="Animator" - type="android.animation.Animator" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="duration" type="long"> -</parameter> -<parameter name="valueFrom" type="float"> -</parameter> -<parameter name="valueTo" type="float"> -</parameter> -</constructor> -<constructor name="Animator" - type="android.animation.Animator" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="duration" type="long"> -</parameter> -<parameter name="valueFrom" type="int"> -</parameter> -<parameter name="valueTo" type="int"> -</parameter> -</constructor> -<constructor name="Animator" - type="android.animation.Animator" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="duration" type="long"> -</parameter> -<parameter name="valueFrom" type="double"> -</parameter> -<parameter name="valueTo" type="double"> -</parameter> -</constructor> -<constructor name="Animator" - type="android.animation.Animator" +<method name="addUpdateListener" + return="void" + abstract="false" + native="false" + synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > -<parameter name="duration" type="long"> -</parameter> -<parameter name="valueFrom" type="java.lang.Object"> -</parameter> -<parameter name="valueTo" type="java.lang.Object"> +<parameter name="listener" type="android.animation.Animator.AnimatorUpdateListener"> </parameter> -</constructor> -<method name="addUpdateListener" - return="void" +</method> +<method name="getAnimatedValue" + return="java.lang.Object" abstract="false" native="false" synchronized="false" @@ -20021,8 +19978,6 @@ deprecated="not deprecated" visibility="public" > -<parameter name="listener" type="android.animation.Animator.AnimatorUpdateListener"> -</parameter> </method> <method name="getAnimatedValue" return="java.lang.Object" @@ -20034,6 +19989,8 @@ deprecated="not deprecated" visibility="public" > +<parameter name="propertyName" type="java.lang.String"> +</parameter> </method> <method name="getCurrentPlayTime" return="long" @@ -20090,28 +20047,6 @@ visibility="public" > </method> -<method name="getValueFrom" - return="java.lang.Object" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -</method> -<method name="getValueTo" - return="java.lang.Object" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -</method> <method name="isRunning" return="boolean" abstract="false" @@ -20238,7 +20173,7 @@ <parameter name="startDelay" type="long"> </parameter> </method> -<method name="setValueFrom" +<method name="setValues" return="void" abstract="false" native="false" @@ -20248,10 +20183,10 @@ deprecated="not deprecated" visibility="public" > -<parameter name="valueFrom" type="java.lang.Object"> +<parameter name="values" type="android.animation.PropertyValuesHolder..."> </parameter> </method> -<method name="setValueTo" +<method name="setValues" return="void" abstract="false" native="false" @@ -20261,7 +20196,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="valueTo" type="java.lang.Object"> +<parameter name="values" type="T..."> </parameter> </method> <field name="INFINITE" @@ -20594,15 +20529,13 @@ deprecated="not deprecated" visibility="public" > -<parameter name="duration" type="int"> +<parameter name="duration" type="long"> </parameter> <parameter name="target" type="java.lang.Object"> </parameter> <parameter name="propertyName" type="java.lang.String"> </parameter> -<parameter name="valueFrom" type="float"> -</parameter> -<parameter name="valueTo" type="float"> +<parameter name="values" type="T..."> </parameter> </constructor> <constructor name="PropertyAnimator" @@ -20612,131 +20545,90 @@ deprecated="not deprecated" visibility="public" > -<parameter name="duration" type="int"> +<parameter name="duration" type="long"> </parameter> <parameter name="target" type="java.lang.Object"> </parameter> -<parameter name="propertyName" type="java.lang.String"> -</parameter> -<parameter name="valueTo" type="float"> +<parameter name="values" type="android.animation.PropertyValuesHolder..."> </parameter> </constructor> -<constructor name="PropertyAnimator" - type="android.animation.PropertyAnimator" +<method name="getPropertyName" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > -<parameter name="duration" type="int"> -</parameter> -<parameter name="target" type="java.lang.Object"> -</parameter> -<parameter name="propertyName" type="java.lang.String"> -</parameter> -<parameter name="valueFrom" type="int"> -</parameter> -<parameter name="valueTo" type="int"> -</parameter> -</constructor> -<constructor name="PropertyAnimator" - type="android.animation.PropertyAnimator" +</method> +<method name="getTarget" + return="java.lang.Object" + abstract="false" + native="false" + synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > -<parameter name="duration" type="int"> -</parameter> -<parameter name="target" type="java.lang.Object"> -</parameter> -<parameter name="propertyName" type="java.lang.String"> -</parameter> -<parameter name="valueTo" type="int"> -</parameter> -</constructor> -<constructor name="PropertyAnimator" - type="android.animation.PropertyAnimator" +</method> +<method name="setPropertyName" + return="void" + abstract="false" + native="false" + synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > -<parameter name="duration" type="int"> -</parameter> -<parameter name="target" type="java.lang.Object"> -</parameter> <parameter name="propertyName" type="java.lang.String"> </parameter> -<parameter name="valueFrom" type="double"> -</parameter> -<parameter name="valueTo" type="double"> -</parameter> -</constructor> -<constructor name="PropertyAnimator" - type="android.animation.PropertyAnimator" +</method> +<method name="setTarget" + return="void" + abstract="false" + native="false" + synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > -<parameter name="duration" type="int"> -</parameter> <parameter name="target" type="java.lang.Object"> </parameter> -<parameter name="propertyName" type="java.lang.String"> -</parameter> -<parameter name="valueTo" type="double"> -</parameter> -</constructor> -<constructor name="PropertyAnimator" - type="android.animation.PropertyAnimator" +</method> +</class> +<class name="PropertyValuesHolder" + extends="java.lang.Object" + abstract="false" static="false" final="false" deprecated="not deprecated" visibility="public" > -<parameter name="duration" type="int"> -</parameter> -<parameter name="target" type="java.lang.Object"> -</parameter> -<parameter name="propertyName" type="java.lang.String"> -</parameter> -<parameter name="valueFrom" type="java.lang.Object"> -</parameter> -<parameter name="valueTo" type="java.lang.Object"> -</parameter> -</constructor> -<constructor name="PropertyAnimator" - type="android.animation.PropertyAnimator" +<constructor name="PropertyValuesHolder" + type="android.animation.PropertyValuesHolder" static="false" final="false" deprecated="not deprecated" visibility="public" > -<parameter name="duration" type="int"> -</parameter> -<parameter name="target" type="java.lang.Object"> -</parameter> -<parameter name="propertyName" type="java.lang.String"> -</parameter> -<parameter name="valueTo" type="java.lang.Object"> +<parameter name="values" type="T..."> </parameter> </constructor> -<constructor name="PropertyAnimator" - type="android.animation.PropertyAnimator" +<constructor name="PropertyValuesHolder" + type="android.animation.PropertyValuesHolder" static="false" final="false" deprecated="not deprecated" visibility="public" > -<parameter name="duration" type="int"> -</parameter> -<parameter name="target" type="java.lang.Object"> -</parameter> <parameter name="propertyName" type="java.lang.String"> </parameter> -<parameter name="keyframes" type="android.animation.Keyframe..."> +<parameter name="values" type="T..."> </parameter> </constructor> <method name="getGetter" @@ -20772,8 +20664,8 @@ visibility="public" > </method> -<method name="getTarget" - return="java.lang.Object" +<method name="setEvaluator" + return="void" abstract="false" native="false" synchronized="false" @@ -20782,6 +20674,8 @@ deprecated="not deprecated" visibility="public" > +<parameter name="evaluator" type="android.animation.TypeEvaluator"> +</parameter> </method> <method name="setGetter" return="void" @@ -20822,7 +20716,7 @@ <parameter name="setter" type="java.lang.reflect.Method"> </parameter> </method> -<method name="setTarget" +<method name="setValues" return="void" abstract="false" native="false" @@ -20832,7 +20726,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="target" type="java.lang.Object"> +<parameter name="values" type="T..."> </parameter> </method> </class> diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java index cd6531b5bda7..951d104ad3fe 100755 --- a/core/java/android/animation/Animator.java +++ b/core/java/android/animation/Animator.java @@ -26,6 +26,7 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import java.util.ArrayList; +import java.util.HashMap; /** * This class provides a simple timing engine for running animations @@ -39,7 +40,7 @@ import java.util.ArrayList; * out of an animation. This behavior can be changed by calling * {@link Animator#setInterpolator(Interpolator)}.</p> */ -public class Animator extends Animatable { +public class Animator<T> extends Animatable { /** * Internal constants @@ -164,12 +165,6 @@ public class Animator extends Animatable { // How long the animation should last in ms private long mDuration; - // The value that the animation should start from, set in the constructor - private Object mValueFrom; - - // The value that the animation should animate to, set in the constructor - private Object mValueTo; - // The amount of time in ms to delay starting the animation after start() is called private long mStartDelay = 0; @@ -195,29 +190,22 @@ public class Animator extends Animatable { private Interpolator mInterpolator = sDefaultInterpolator; /** - * The type evaluator used to calculate the animated values. This evaluator is determined - * automatically based on the type of the start/end objects passed into the constructor, - * but the system only knows about the primitive types int, double, and float. Any other - * type will need to set the evaluator to a custom evaluator for that type. - */ - private TypeEvaluator mEvaluator; - - /** * The set of listeners to be sent events through the life of an animation. */ private ArrayList<AnimatorUpdateListener> mUpdateListeners = null; /** - * The current value calculated by the animation. The value is calculated in animateFraction(), - * prior to calling the setter (if set) and sending out the onAnimationUpdate() callback - * to the update listeners. + * The property/value sets being animated. */ - private Object mAnimatedValue = null; + HashMap<String, PropertyValuesHolder> mValues; /** - * The set of keyframes (time/value pairs) that define this animation. + * This value is used in the simple/common case of animating just one value; the user + * may call getAnimatedValue(), which should return the value of the first (and only) + * ProeprtyValuesHolder animated value, which is looked up using this string. */ - private KeyframeSet mKeyframeSet = null; + String mFirstPropertyName; + /** * The type of the values, as determined by the valueFrom/valueTo properties. @@ -267,144 +255,120 @@ public class Animator extends Animatable { int valueType = a.getInt(com.android.internal.R.styleable.Animator_valueType, VALUE_TYPE_FLOAT); + Object valueFrom = null; + Object valueTo = null; + switch (valueType) { case VALUE_TYPE_FLOAT: - mValueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f); - mValueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); + valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f); + valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); mValueType = float.class; break; case VALUE_TYPE_INT: - mValueFrom = a.getInt(com.android.internal.R.styleable.Animator_valueFrom, 0); - mValueTo = a.getInt(com.android.internal.R.styleable.Animator_valueTo, 0); + valueFrom = a.getInt(com.android.internal.R.styleable.Animator_valueFrom, 0); + valueTo = a.getInt(com.android.internal.R.styleable.Animator_valueTo, 0); mValueType = int.class; break; case VALUE_TYPE_DOUBLE: - mValueFrom = (double) + valueFrom = (double) a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f); - mValueTo = (double) + valueTo = (double) a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); mValueType = double.class; break; case VALUE_TYPE_COLOR: - mValueFrom = a.getInt(com.android.internal.R.styleable.Animator_valueFrom, 0); - mValueTo = a.getInt(com.android.internal.R.styleable.Animator_valueTo, 0); - mEvaluator = new RGBEvaluator(); + valueFrom = a.getInt(com.android.internal.R.styleable.Animator_valueFrom, 0); + valueTo = a.getInt(com.android.internal.R.styleable.Animator_valueTo, 0); + setEvaluator(new RGBEvaluator()); mValueType = int.class; break; case VALUE_TYPE_CUSTOM: // TODO: How to get an 'Object' value? - mValueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f); - mValueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); + valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f); + valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); mValueType = Object.class; break; } + mValues = new HashMap<String, PropertyValuesHolder>(1); + mFirstPropertyName = ""; + PropertyValuesHolder valuesHolder = new PropertyValuesHolder(mFirstPropertyName, + valueFrom, valueTo); + mValues.put(mFirstPropertyName, valuesHolder); + mRepeatCount = a.getInt(com.android.internal.R.styleable.Animator_repeatCount, mRepeatCount); mRepeatMode = a.getInt(com.android.internal.R.styleable.Animator_repeatMode, RESTART); a.recycle(); } - private Animator(long duration, Object valueFrom, Object valueTo, Class valueType) { - mDuration = duration; - mValueFrom = valueFrom; - mValueTo= valueTo; - this.mValueType = valueType; - } /** - * This constructor takes a set of {@link Keyframe} objects that define the values - * for the animation, along with the times at which those values will hold true during - * the animation. + * Constructs an Animator object with the specified duration and set of + * values. If the values are a set of PropertyValuesHolder objects, then these objects + * define the potentially multiple properties being animated and the values the properties are + * animated between. Otherwise, the values define a single set of values animated between. * * @param duration The length of the animation, in milliseconds. - * @param keyframes The set of keyframes that define the time/value pairs for the animation. + * @param values The set of values to animate between. If these values are not + * PropertyValuesHolder objects, then there should be more than one value, since the values + * determine the interval to animate between. */ - public Animator(long duration, Keyframe...keyframes) { + public Animator(long duration, T...values) { mDuration = duration; - mKeyframeSet = new KeyframeSet(keyframes); - mValueType = keyframes[0].getType(); - } - - /** - * A constructor that takes <code>float</code> values. - * - * @param duration The length of the animation, in milliseconds. - * @param valueFrom The initial value of the property when the animation begins. - * @param valueTo The value to which the property will animate. - */ - public Animator(long duration, float valueFrom, float valueTo) { - this(duration, valueFrom, valueTo, float.class); - } - - /** - * A constructor that takes <code>int</code> values. - * - * @param duration The length of the animation, in milliseconds. - * @param valueFrom The initial value of the property when the animation begins. - * @param valueTo The value to which the property will animate. - */ - public Animator(long duration, int valueFrom, int valueTo) { - this(duration, valueFrom, valueTo, int.class); - } - - /** - * A constructor that takes <code>double</code> values. - * - * @param duration The length of the animation, in milliseconds. - * @param valueFrom The initial value of the property when the animation begins. - * @param valueTo The value to which the property will animate. - */ - public Animator(long duration, double valueFrom, double valueTo) { - this(duration, valueFrom, valueTo, double.class); - } - - /** - * A constructor that takes <code>Object</code> values. - * - * @param duration The length of the animation, in milliseconds. - * @param valueFrom The initial value of the property when the animation begins. - * @param valueTo The value to which the property will animate. - */ - public Animator(long duration, Object valueFrom, Object valueTo) { - this(duration, valueFrom, valueTo, - (valueFrom != null) ? valueFrom.getClass() : valueTo.getClass()); - } - - /** - * Internal constructor that takes a single <code>float</code> value. - * This constructor is called by PropertyAnimator. - * - * @param duration The length of the animation, in milliseconds. - * @param valueFrom The initial value of the property when the animation begins. - * @param valueTo The value to which the property will animate. - */ - Animator(long duration, float valueTo) { - this(duration, null, valueTo, float.class); + if (values.length > 0) { + setValues(values); + } } - /** - * Internal constructor that takes a single <code>int</code> value. - * This constructor is called by PropertyAnimator. - * - * @param duration The length of the animation, in milliseconds. - * @param valueFrom The initial value of the property when the animation begins. - * @param valueTo The value to which the property will animate. - */ - Animator(long duration, int valueTo) { - this(duration, null, valueTo, int.class); + public void setValues(PropertyValuesHolder... values) { + int numValues = values.length; + mValues = new HashMap<String, PropertyValuesHolder>(numValues); + for (int i = 0; i < numValues; ++i) { + PropertyValuesHolder valuesHolder = (PropertyValuesHolder) values[i]; + mValues.put(valuesHolder.getPropertyName(), valuesHolder); + } + if (numValues > 0 && values[0] != null) { + mFirstPropertyName = ((PropertyValuesHolder) values[0]).getPropertyName(); + } else { + mFirstPropertyName = ""; + } } /** - * Internal constructor that takes a single <code>double</code> value. - * This constructor is called by PropertyAnimator. + * Sets the values to animate between for this animation. If <code>values</code> is + * a set of PropertyValuesHolder objects, these objects will become the set of properties + * animated and the values that those properties are animated between. Otherwise, this method + * will set only one set of values for the Animator. Also, if the values are not + * PropertyValuesHolder objects and if there are already multiple sets of + * values defined for this Animator via + * more than one PropertyValuesHolder objects, this method will set the values for + * the first of those objects. * - * @param duration The length of the animation, in milliseconds. - * @param valueFrom The initial value of the property when the animation begins. - * @param valueTo The value to which the property will animate. + * @param values The set of values to animate between. */ - Animator(long duration, double valueTo) { - this(duration, null, valueTo, double.class); + public void setValues(T... values) { + if (values[0] instanceof PropertyValuesHolder) { + int numValues = values.length; + mValues = new HashMap<String, PropertyValuesHolder>(numValues); + for (int i = 0; i < numValues; ++i) { + PropertyValuesHolder valuesHolder = (PropertyValuesHolder) values[i]; + mValues.put(valuesHolder.getPropertyName(), valuesHolder); + } + if (numValues > 0 && values[0] != null) { + mFirstPropertyName = ((PropertyValuesHolder) values[0]).getPropertyName(); + } else { + mFirstPropertyName = ""; + } + } else { + if (mValues == null) { + setValues(new PropertyValuesHolder[]{ + new PropertyValuesHolder("", (Object[])values)}); + } else { + PropertyValuesHolder valuesHolder = mValues.get(mFirstPropertyName); + valuesHolder.setValues(values); + } + } } /** @@ -418,9 +382,8 @@ public class Animator extends Animatable { * that internal mechanisms for the animation are set up correctly.</p> */ void initAnimation() { - if (mEvaluator == null) { - mEvaluator = (mValueType == int.class) ? sIntEvaluator : - (mValueType == double.class) ? sDoubleEvaluator : sFloatEvaluator; + for (PropertyValuesHolder pvHolder: mValues.values()) { + pvHolder.init(); } mCurrentIteration = 0; mInitialized = true; @@ -599,67 +562,6 @@ public class Animator extends Animatable { } /** - * Gets the set of keyframes that define this animation. - * - * @return KeyframeSet The set of keyframes for this animation. - */ - KeyframeSet getKeyframes() { - return mKeyframeSet; - } - - /** - * Gets the value that this animation will start from. - * - * @return Object The starting value for the animation. - */ - public Object getValueFrom() { - if (mKeyframeSet != null) { - return mKeyframeSet.mKeyframes.get(0).getValue(); - } - return mValueFrom; - } - - /** - * Sets the value that this animation will start from. - */ - public void setValueFrom(Object valueFrom) { - if (mKeyframeSet != null) { - Keyframe kf = mKeyframeSet.mKeyframes.get(0); - kf.setValue(valueFrom); - } else { - mValueFrom = valueFrom; - } - } - - /** - * Gets the value that this animation will animate to. - * - * @return Object The ending value for the animation. - */ - public Object getValueTo() { - if (mKeyframeSet != null) { - int numKeyframes = mKeyframeSet.mKeyframes.size(); - return mKeyframeSet.mKeyframes.get(numKeyframes - 1).getValue(); - } - return mValueTo; - } - - /** - * Sets the value that this animation will animate to. - * - * @return Object The ending value for the animation. - */ - public void setValueTo(Object valueTo) { - if (mKeyframeSet != null) { - int numKeyframes = mKeyframeSet.mKeyframes.size(); - Keyframe kf = mKeyframeSet.mKeyframes.get(numKeyframes - 1); - kf.setValue(valueTo); - } else { - mValueTo = valueTo; - } - } - - /** * The amount of time, in milliseconds, between each frame of the animation. This is a * requested time that the animation will attempt to honor, but the actual delay between * frames may be different, depending on system load and capabilities. This is a static @@ -673,17 +575,33 @@ public class Animator extends Animatable { } /** - * The most recent value calculated by this <code>Animator</code> for the property - * being animated. This value is only sensible while the animation is running. The main + * The most recent value calculated by this <code>Animator</code> when there is just one + * property being animated. This value is only sensible while the animation is running. The main * purpose for this read-only property is to retrieve the value from the <code>Animator</code> * during a call to {@link AnimatorUpdateListener#onAnimationUpdate(Animator)}, which * is called during each animation frame, immediately after the value is calculated. * * @return animatedValue The value most recently calculated by this <code>Animator</code> for - * the property specified in the constructor. + * the single property being animated. If there are several properties being animated + * (specified by several PropertyValuesHolder objects in the constructor), this function + * returns the animated value for the first of those objects. */ public Object getAnimatedValue() { - return mAnimatedValue; + return getAnimatedValue(mFirstPropertyName); + } + + /** + * The most recent value calculated by this <code>Animator</code> for <code>propertyName</code>. + * The main purpose for this read-only property is to retrieve the value from the + * <code>Animator</code> during a call to + * {@link AnimatorUpdateListener#onAnimationUpdate(Animator)}, which + * is called during each animation frame, immediately after the value is calculated. + * + * @return animatedValue The value most recently calculated for the named property + * by this <code>Animator</code>. + */ + public Object getAnimatedValue(String propertyName) { + return mValues.get(mFirstPropertyName).getAnimatedValue(); } /** @@ -781,14 +699,26 @@ public class Animator extends Animatable { * For example, when running an animation on color values, the {@link RGBEvaluator} * should be used to get correct RGB color interpolation. * + * <p>If this Animator has only one set of values being animated between, this evaluator + * will be used for that set. If there are several sets of values being animated, which is + * the case if PropertyValuesHOlder objects were set on the Animator, then the evaluator + * is assigned just to the first PropertyValuesHolder object.</p> + * * @param value the evaluator to be used this animation */ public void setEvaluator(TypeEvaluator value) { - if (value != null) { - mEvaluator = value; + if (value != null && mValues != null) { + mValues.get(mFirstPropertyName).setEvaluator(value); } } + /** + * Start the animation playing. This version of start() takes a boolean flag that indicates + * whether the animation should play in reverse. The flag is usually false, but may be set + * to true if called from the reverse() method/ + * + * @param playBackwards Whether the Animator should start playing in reverse. + */ private void start(boolean playBackwards) { mPlayingBackwards = playBackwards; mPlayingState = STOPPED; @@ -998,10 +928,8 @@ public class Animator extends Animatable { */ void animateValue(float fraction) { fraction = mInterpolator.getInterpolation(fraction); - if (mKeyframeSet != null) { - mAnimatedValue = mKeyframeSet.getValue(fraction, mEvaluator); - } else { - mAnimatedValue = mEvaluator.evaluate(fraction, mValueFrom, mValueTo); + for (PropertyValuesHolder valuesHolder : mValues.values()) { + valuesHolder.calculateValue(fraction); } if (mUpdateListeners != null) { int numListeners = mUpdateListeners.size(); diff --git a/core/java/android/animation/DoubleEvaluator.java b/core/java/android/animation/DoubleEvaluator.java index 86e3f225e64c..e46eb372e554 100644 --- a/core/java/android/animation/DoubleEvaluator.java +++ b/core/java/android/animation/DoubleEvaluator.java @@ -36,7 +36,7 @@ public class DoubleEvaluator implements TypeEvaluator { * <code>fraction</code> parameter. */ public Object evaluate(float fraction, Object startValue, Object endValue) { - double startDouble = (Double) startValue; - return startDouble + fraction * ((Double) endValue - startDouble); + double startDouble = ((Number) startValue).doubleValue(); + return startDouble + fraction * (((Number) endValue).doubleValue() - startDouble); } }
\ No newline at end of file diff --git a/core/java/android/animation/FloatEvaluator.java b/core/java/android/animation/FloatEvaluator.java index 29a6f71c2246..9e2054d0dc02 100644 --- a/core/java/android/animation/FloatEvaluator.java +++ b/core/java/android/animation/FloatEvaluator.java @@ -36,7 +36,7 @@ public class FloatEvaluator implements TypeEvaluator { * <code>fraction</code> parameter. */ public Object evaluate(float fraction, Object startValue, Object endValue) { - float startFloat = (Float) startValue; - return startFloat + fraction * ((Float) endValue - startFloat); + float startFloat = ((Number) startValue).floatValue(); + return startFloat + fraction * (((Number) endValue).floatValue() - startFloat); } }
\ No newline at end of file diff --git a/core/java/android/animation/IntEvaluator.java b/core/java/android/animation/IntEvaluator.java index 7a2911a5a2b3..7288927f783b 100644 --- a/core/java/android/animation/IntEvaluator.java +++ b/core/java/android/animation/IntEvaluator.java @@ -36,7 +36,7 @@ public class IntEvaluator implements TypeEvaluator { * <code>fraction</code> parameter. */ public Object evaluate(float fraction, Object startValue, Object endValue) { - int startInt = (Integer) startValue; - return (int) (startInt + fraction * ((Integer) endValue - startInt)); + int startInt = ((Number) startValue).intValue(); + return (int) (startInt + fraction * (((Number) endValue).intValue() - startInt)); } }
\ No newline at end of file diff --git a/core/java/android/animation/PropertyAnimator.java b/core/java/android/animation/PropertyAnimator.java index eac9798548c4..9366a713971e 100644 --- a/core/java/android/animation/PropertyAnimator.java +++ b/core/java/android/animation/PropertyAnimator.java @@ -33,35 +33,13 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * are then determined internally and the animation will call these functions as necessary to * animate the property. */ -public final class PropertyAnimator extends Animator { +public final class PropertyAnimator<T> extends Animator<T> { // The target object on which the property exists, set in the constructor private Object mTarget; private String mPropertyName; - private Method mGetter = null; - - // The property setter that is assigned internally, based on the propertyName passed into - // the constructor - private Method mSetter; - - // These maps hold all property entries for a particular class. This map - // is used to speed up property/setter/getter lookups for a given class/property - // combination. No need to use reflection on the combination more than once. - private static final HashMap<Object, HashMap<String, Method>> sSetterPropertyMap = - new HashMap<Object, HashMap<String, Method>>(); - private static final HashMap<Object, HashMap<String, Method>> sGetterPropertyMap = - new HashMap<Object, HashMap<String, Method>>(); - - // This lock is used to ensure that only one thread is accessing the property maps - // at a time. - private ReentrantReadWriteLock propertyMapLock = new ReentrantReadWriteLock(); - - // Used to pass single value to varargs parameter in setter invocation - private Object[] mTmpValueArray = new Object[1]; - - /** * 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. @@ -75,9 +53,19 @@ public final class PropertyAnimator extends Animator { * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to * the setter function will fail.</p> * + * <p>If this PropertyAnimator has been set up to animate several properties together, + * using more than one PropertyValuesHolder objects, then setting the propertyName simply + * sets the propertyName in the first of those PropertyValuesHolder objects.</p> + * * @param propertyName The name of the property being animated. */ public void setPropertyName(String propertyName) { + if (mValues != null) { + // should always be the case + PropertyValuesHolder valuesHolder = mValues.get(mFirstPropertyName); + valuesHolder.setPropertyName(propertyName); + mFirstPropertyName = propertyName; + } mPropertyName = propertyName; } @@ -94,67 +82,6 @@ public final class PropertyAnimator extends Animator { } /** - * 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>PropertyAnimator</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>PropertyAnimator</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; - } - - /** * Creates a new animation whose parameters come from the specified context and * attributes set. * @@ -167,7 +94,8 @@ public final class PropertyAnimator extends Animator { TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.PropertyAnimator); - mPropertyName = a.getString(com.android.internal.R.styleable.PropertyAnimator_propertyName); + setPropertyName(a.getString( + com.android.internal.R.styleable.PropertyAnimator_propertyName)); a.recycle(); @@ -203,224 +131,42 @@ public final class PropertyAnimator extends Animator { } /** - * A constructor that takes <code>float</code> values. When this constructor - * is called, the system expects to find a setter for <code>propertyName</code> on - * the target object that takes a <code>float</code> value. - * - * @param duration The length of the animation, in milliseconds. - * @param target The object whose property is to be animated. This object should - * have a public function on it called <code>setName()</code>, where <code>name</code> is - * the name of the property passed in as the <code>propertyName</code> parameter. - * @param propertyName The name of the property on the <code>target</code> object - * that will be animated. Given this name, the constructor will search for a - * setter on the target object with the name <code>setPropertyName</code>. For example, - * if the constructor is called with <code>propertyName = "foo"</code>, then the - * target object should have a setter function with the name <code>setFoo()</code>. - * @param valueFrom The initial value of the property when the animation begins. - * @param valueTo The value to which the property will animate. - */ - public PropertyAnimator(int duration, Object target, String propertyName, - float valueFrom, float valueTo) { - super(duration, valueFrom, valueTo); - mTarget = target; - mPropertyName = propertyName; - } - - /** - * A constructor that takes a single <code>float</code> value, which is the value that the - * target object will animate to. When this constructor - * is called, the system expects to find a setter for <code>propertyName</code> on - * the target object that takes a value of the same type as the <code>Object</code>s. The - * system also expects to find a similar getter function with which to derive the starting - * value for the animation. - * - * @param duration The length of the animation, in milliseconds. - * @param target The object whose property is to be animated. This object should - * have a public function on it called <code>setName()</code>, where <code>name</code> is - * the name of the property passed in as the <code>propertyName</code> parameter. - * @param propertyName The name of the property on the <code>target</code> object - * that will be animated. Given this name, the constructor will search for a - * setter on the target object with the name <code>setPropertyName</code>. For example, - * if the constructor is called with <code>propertyName = "foo"</code>, then the - * target object should have a setter function with the name <code>setFoo()</code>. - * @param valueTo The value to which the property will animate. - */ - public PropertyAnimator(int duration, Object target, String propertyName, float valueTo) { - super(duration, valueTo); - mTarget = target; - mPropertyName = propertyName; - } - - /** - * A constructor that takes <code>int</code> values. When this constructor - * is called, the system expects to find a setter for <code>propertyName</code> on - * the target object that takes a <code>int</code> value. - * - * @param duration The length of the animation, in milliseconds. - * @param target The object whose property is to be animated. This object should - * have a public function on it called <code>setName()</code>, where <code>name</code> is - * the name of the property passed in as the <code>propertyName</code> parameter. - * @param propertyName The name of the property on the <code>target</code> object - * that will be animated. Given this name, the constructor will search for a - * setter on the target object with the name <code>setPropertyName</code>. For example, - * if the constructor is called with <code>propertyName = "foo"</code>, then the - * target object should have a setter function with the name <code>setFoo()</code>. - * @param valueFrom The initial value of the property when the animation begins. - * @param valueTo The value to which the property will animate. - */ - public PropertyAnimator(int duration, Object target, String propertyName, - int valueFrom, int valueTo) { - super(duration, valueFrom, valueTo); - mTarget = target; - mPropertyName = propertyName; - } - - /** - * A constructor that takes a single <code>int</code> value, which is the value that the - * target object will animate to. When this constructor - * is called, the system expects to find a setter for <code>propertyName</code> on - * the target object that takes a value of the same type as the <code>Object</code>s. The - * system also expects to find a similar getter function with which to derive the starting - * value for the animation. - * - * @param duration The length of the animation, in milliseconds. - * @param target The object whose property is to be animated. This object should - * have a public function on it called <code>setName()</code>, where <code>name</code> is - * the name of the property passed in as the <code>propertyName</code> parameter. - * @param propertyName The name of the property on the <code>target</code> object - * that will be animated. Given this name, the constructor will search for a - * setter on the target object with the name <code>setPropertyName</code>. For example, - * if the constructor is called with <code>propertyName = "foo"</code>, then the - * target object should have a setter function with the name <code>setFoo()</code>. - * @param valueTo The value to which the property will animate. - */ - public PropertyAnimator(int duration, Object target, String propertyName, int valueTo) { - super(duration, valueTo); - mTarget = target; - mPropertyName = propertyName; - } - - /** - * A constructor that takes <code>double</code> values. When this constructor - * is called, the system expects to find a setter for <code>propertyName</code> on - * the target object that takes a <code>double</code> value. - * - * @param duration The length of the animation, in milliseconds. - * @param target The object whose property is to be animated. This object should - * have a public function on it called <code>setName()</code>, where <code>name</code> is - * the name of the property passed in as the <code>propertyName</code> parameter. - * @param propertyName The name of the property on the <code>target</code> object - * that will be animated. Given this name, the constructor will search for a - * setter on the target object with the name <code>setPropertyName</code>. For example, - * if the constructor is called with <code>propertyName = "foo"</code>, then the - * target object should have a setter function with the name <code>setFoo()</code>. - * @param valueFrom The initial value of the property when the animation begins. - * @param valueTo The value to which the property will animate. - */ - public PropertyAnimator(int duration, Object target, String propertyName, - double valueFrom, double valueTo) { - super(duration, valueFrom, valueTo); - mTarget = target; - mPropertyName = propertyName; - } - - /** - * A constructor that takes a single <code>double</code> value, which is the value that the - * target object will animate to. When this constructor - * is called, the system expects to find a setter for <code>propertyName</code> on - * the target object that takes a value of the same type as the <code>Object</code>s. The - * system also expects to find a similar getter function with which to derive the starting - * value for the animation. + * A constructor that takes a single property name and set of values. This constructor is + * used in the simple case of animating a single property. * * @param duration The length of the animation, in milliseconds. * @param target The object whose property is to be animated. This object should - * have a public function on it called <code>setName()</code>, where <code>name</code> is - * the name of the property passed in as the <code>propertyName</code> parameter. - * @param propertyName The name of the property on the <code>target</code> object - * that will be animated. Given this name, the constructor will search for a - * setter on the target object with the name <code>setPropertyName</code>. For example, - * if the constructor is called with <code>propertyName = "foo"</code>, then the - * target object should have a setter function with the name <code>setFoo()</code>. - * @param valueTo The value to which the property will animate. + * 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 The set of values to animate between. If there is only one value, it + * is assumed to be the final value being animated to, and the initial value will be + * derived on the fly. */ - public PropertyAnimator(int duration, Object target, String propertyName, double valueTo) { - super(duration, valueTo); + public PropertyAnimator(long duration, Object target, String propertyName, T...values) { + super(duration, (T[]) values); mTarget = target; - mPropertyName = propertyName; + setPropertyName(propertyName); } /** - * A constructor that takes <code>Object</code> values. When this constructor - * is called, the system expects to find a setter for <code>propertyName</code> on - * the target object that takes a value of the same type as the <code>Object</code>s. + * A constructor that takes <code>PropertyValueHolder</code> values. This constructor should + * be used when animating several properties at once with the same PropertyAnimator, since + * PropertyValuesHolder allows you to associate a set of animation values with a property + * name. * * @param duration The length of the animation, in milliseconds. * @param target The object whose property is to be animated. This object should - * have a public function on it called <code>setName()</code>, where <code>name</code> is - * the name of the property passed in as the <code>propertyName</code> parameter. - * @param propertyName The name of the property on the <code>target</code> object - * that will be animated. Given this name, the constructor will search for a - * setter on the target object with the name <code>setPropertyName</code>. For example, - * if the constructor is called with <code>propertyName = "foo"</code>, then the - * target object should have a setter function with the name <code>setFoo()</code>. - * @param valueFrom The initial value of the property when the animation begins. - * @param valueTo The value to which the property will animate. - */ - public PropertyAnimator(int duration, Object target, String propertyName, - Object valueFrom, Object valueTo) { - super(duration, valueFrom, valueTo); + * have public methods on it called <code>setName()</code>, where <code>name</code> is + * the name of the property passed in as the <code>propertyName</code> parameter for + * each of the PropertyValuesHolder objects. + * @param values The PropertyValuesHolder objects which hold each the property name and values + * to animate that property between. + */ + public PropertyAnimator(long duration, Object target, PropertyValuesHolder...values) { + super(duration); + setValues(values); mTarget = target; - mPropertyName = propertyName; - } - - /** - * A constructor that takes a single <code>Object</code> value, which is the value that the - * target object will animate to. When this constructor - * is called, the system expects to find a setter for <code>propertyName</code> on - * the target object that takes a value of the same type as the <code>Object</code>s. The - * system also expects to find a similar getter function with which to derive the starting - * value for the animation. - * - * @param duration The length of the animation, in milliseconds. - * @param target The object whose property is to be animated. This object should - * have a public function on it called <code>setName()</code>, where <code>name</code> is - * the name of the property passed in as the <code>propertyName</code> parameter. - * @param propertyName The name of the property on the <code>target</code> object - * that will be animated. Given this name, the constructor will search for a - * setter on the target object with the name <code>setPropertyName</code>. For example, - * if the constructor is called with <code>propertyName = "foo"</code>, then the - * target object should have a setter function with the name <code>setFoo()</code>. - * @param valueTo The value to which the property will animate. - */ - public PropertyAnimator(int duration, Object target, String propertyName, Object valueTo) { - this(duration, target, propertyName, null, valueTo); - } - - /** - * A constructor that takes <code>Keyframe</code>s. When this constructor - * is called, the system expects to find a setter for <code>propertyName</code> on - * the target object that takes a value of the same type as that returned from - * {@link Keyframe#getType()}. - * . - * - * @param duration The length of the animation, in milliseconds. - * @param target The object whose property is to be animated. This object should - * have a public function on it called <code>setName()</code>, where <code>name</code> is - * the name of the property passed in as the <code>propertyName</code> parameter. - * @param propertyName The name of the property on the <code>target</code> object - * that will be animated. Given this name, the constructor will search for a - * setter on the target object with the name <code>setPropertyName</code>. For example, - * if the constructor is called with <code>propertyName = "foo"</code>, then the - * target object should have a setter function with the name <code>setFoo()</code>. - * @param keyframes The set of keyframes that define the times and values for the animation. - * These keyframes should be ordered in increasing time value, should have a starting - * keyframe with a fraction of 0 and and ending keyframe with a fraction of 1. - */ - public PropertyAnimator(int duration, Object target, String propertyName, - Keyframe...keyframes) { - super(duration, keyframes); - mTarget = target; - mPropertyName = propertyName; } /** @@ -438,64 +184,8 @@ public final class PropertyAnimator extends Animator { @Override void initAnimation() { super.initAnimation(); - if (mSetter == null) { - try { - // Have to lock property map prior to reading it, to guard against - // another thread putting something in there after we've checked it - // but before we've added an entry to it - propertyMapLock.writeLock().lock(); - HashMap<String, Method> propertyMap = sSetterPropertyMap.get(mTarget); - if (propertyMap != null) { - mSetter = propertyMap.get(mPropertyName); - } - if (mSetter == null) { - mSetter = getPropertyFunction("set", mValueType); - if (propertyMap == null) { - propertyMap = new HashMap<String, Method>(); - sSetterPropertyMap.put(mTarget, propertyMap); - } - propertyMap.put(mPropertyName, mSetter); - } - } finally { - propertyMapLock.writeLock().unlock(); - } - } - if (getKeyframes() == null && (getValueFrom() == null || getValueTo() == null)) { - // Need to set up the getter if not set by the user, then call it - // to get the initial values - if (mGetter == null) { - try { - propertyMapLock.writeLock().lock(); - HashMap<String, Method> propertyMap = sGetterPropertyMap.get(mTarget); - if (propertyMap != null) { - mGetter = propertyMap.get(mPropertyName); - } - if (mGetter == null) { - mGetter = getPropertyFunction("get", null); - if (propertyMap == null) { - propertyMap = new HashMap<String, Method>(); - sGetterPropertyMap.put(mTarget, propertyMap); - } - propertyMap.put(mPropertyName, mGetter); - } - } finally { - propertyMapLock.writeLock().unlock(); - } - } - try { - if (getValueFrom() == null) { - setValueFrom(mGetter.invoke(mTarget)); - } - if (getValueTo() == null) { - setValueTo(mGetter.invoke(mTarget)); - } - } catch (IllegalArgumentException e) { - Log.e("PropertyAnimator", e.toString()); - } catch (IllegalAccessException e) { - Log.e("PropertyAnimator", e.toString()); - } catch (InvocationTargetException e) { - Log.e("PropertyAnimator", e.toString()); - } + for (PropertyValuesHolder valuesHolder : mValues.values()) { + valuesHolder.setupSetterAndGetter(mTarget); } } @@ -533,23 +223,8 @@ public final class PropertyAnimator extends Animator { @Override void animateValue(float fraction) { super.animateValue(fraction); - if (mSetter != null) { - try { - mTmpValueArray[0] = getAnimatedValue(); - mSetter.invoke(mTarget, mTmpValueArray); - } catch (InvocationTargetException e) { - Log.e("PropertyAnimator", e.toString()); - } catch (IllegalAccessException e) { - Log.e("PropertyAnimator", e.toString()); - } + for (PropertyValuesHolder valuesHolder : mValues.values()) { + valuesHolder.setAnimatedValue(mTarget); } } - - @Override - public String toString() { - return "Animator: target: " + this.mTarget + "\n" + - " property: " + mPropertyName + "\n" + - " from: " + getValueFrom() + "\n" + - " to: " + getValueTo(); - } } diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java new file mode 100644 index 000000000000..05e0bc108b28 --- /dev/null +++ b/core/java/android/animation/PropertyValuesHolder.java @@ -0,0 +1,514 @@ +/* + * 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.util.Log; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + */ +public class PropertyValuesHolder<T> { + + /** + * The name of the property associated with the values. This need not be a real property, + * unless this object is being used with PropertyAnimator. But this is the name by which + * aniamted values are looked up with getAnimatedValue(String) in Animator. + */ + private String mPropertyName; + + /** + * The setter function, if needed. PropertyAnimator hands off this functionality to + * PropertyValuesHolder, since it holds all of the per-property information. This + * property can be manually set via setSetter(). Otherwise, it is automatically + * derived when the animation starts in setupSetterAndGetter() if using PropertyAnimator. + */ + private Method mSetter = null; + + /** + * The getter function, if needed. PropertyAnimator hands off this functionality to + * PropertyValuesHolder, since it holds all of the per-property information. This + * property can be manually set via setSetter(). Otherwise, it is automatically + * derived when the animation starts in setupSetterAndGetter() if using PropertyAnimator. + * The getter is only derived and used if one of the values is null. + */ + private Method mGetter = null; + + /** + * 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; + + /** + * The set of keyframes (time/value pairs) that define this animation. + */ + private KeyframeSet mKeyframeSet = null; + + + // type evaluators for the three primitive types handled by this implementation + private static final TypeEvaluator sIntEvaluator = new IntEvaluator(); + private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator(); + private static final TypeEvaluator sDoubleEvaluator = new DoubleEvaluator(); + + // We try several different types when searching for appropriate setter/getter functions. + // The caller may have supplied values in a type that does not match the setter/getter + // functions (such as the integers 0 and 1 to represent floating point values for alpha). + // Also, the use of generics in constructors means that we end up with the Object versions + // of primitive types (Float vs. float). But most likely, the setter/getter functions + // will take primitive types instead. + // So we supply an ordered array of other types to try before giving up. + private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class, + Double.class, Integer.class}; + private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class, + Float.class, Double.class}; + private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class, + Float.class, Integer.class}; + + // These maps hold all property entries for a particular class. This map + // is used to speed up property/setter/getter lookups for a given class/property + // combination. No need to use reflection on the combination more than once. + private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap = + new HashMap<Class, HashMap<String, Method>>(); + private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap = + new HashMap<Class, HashMap<String, Method>>(); + + // This lock is used to ensure that only one thread is accessing the property maps + // at a time. + private ReentrantReadWriteLock propertyMapLock = new ReentrantReadWriteLock(); + + // Used to pass single value to varargs parameter in setter invocation + private Object[] mTmpValueArray = new Object[1]; + + /** + * The type evaluator used to calculate the animated values. This evaluator is determined + * automatically based on the type of the start/end objects passed into the constructor, + * but the system only knows about the primitive types int, double, and float. Any other + * type will need to set the evaluator to a custom evaluator for that type. + */ + private TypeEvaluator mEvaluator; + + /** + * The value most recently calculated by calculateValue(). This is set during + * that function and might be retrieved later either by Animator.animatedValue() or + * by the property-setting logic in PropertyAnimator.animatedValue(). + */ + private Object mAnimatedValue; + + /** + * Constructs a PropertyValuesHolder object with just a set of values. This constructor + * is typically not used when animating objects with PropertyAnimator, because that + * object needs distinct and meaningful property names. Simpler animations of one + * set of values using Animator may use this constructor, however, because no + * distinguishing name is needed. + * @param values The set of values to animate between. If there is only one value, it + * is assumed to be the final value being animated to, and the initial value will be + * derived on the fly. + */ + public PropertyValuesHolder(T...values) { + this(null, values); + } + + /** + * Constructs a PropertyValuesHolder object with the specified property name and set of + * values. These values can be of any type, but the type should be consistent so that + * an appropriate {@link android.animation.TypeEvaluator} can be found that matches + * the common type. + * <p>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 android.animation.PropertyAnimator}, 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 propertyName The name of the property associated with this set of values. This + * can be the actual property name to be used when using a PropertyAnimator object, or + * just a name used to get animated values, such as if this object is used with an + * Animator object. + * @param values The set of values to animate between. + */ + public PropertyValuesHolder(String propertyName, T... values) { + mPropertyName = propertyName; + setValues(values); + } + + /** + * Sets the values being animated between. + * 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 android.animation.PropertyAnimator}, 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 The set of values to animate between. + */ + public void setValues(T... values) { + int numKeyframes = values.length; + for (int i = 0; i < numKeyframes; ++i) { + if (values[i] != null) { + Class thisValueType = values[i].getClass(); + if (mValueType == null) { + mValueType = thisValueType; + } else { + if (thisValueType != mValueType) { + if (mValueType == Integer.class && + (thisValueType == Float.class || thisValueType == Double.class)) { + mValueType = thisValueType; + } else if (mValueType == Float.class && thisValueType == Double.class) { + mValueType = thisValueType; + } + } + } + } + } + Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)]; + if (mValueType.equals(Keyframe.class)) { + mValueType = ((Keyframe)values[0]).getType(); + for (int i = 0; i < numKeyframes; ++i) { + keyframes[i] = (Keyframe)values[i]; + } + } else { + if (numKeyframes == 1) { + keyframes[0] = new Keyframe(0f, null); + keyframes[1] = new Keyframe(1f, values[0]); + } else { + keyframes[0] = new Keyframe(0f, values[0]); + for (int i = 1; i < numKeyframes; ++i) { + if (values[i] != null && (values[i].getClass() != mValueType)) { + + } + keyframes[i] = new Keyframe((float) i / (numKeyframes - 1), values[i]); + } + } + } + mKeyframeSet = new KeyframeSet(keyframes); + } + + + + /** + * Determine the setter or getter function using the JavaBeans convention of setFoo or + * getFoo for a property named 'foo'. This function figures out what the name of the + * function should be and uses reflection to find the Method with that name on the + * target object. + * + * @param targetClass The class to search for the method + * @param prefix "set" or "get", depending on whether we need a setter or getter. + * @param valueType The type of the parameter (in the case of a setter). This type + * is derived from the values set on this PropertyValuesHolder. This type is used as + * a first guess at the parameter type, but we check for methods with several different + * types to avoid problems with slight mis-matches between supplied values and actual + * value types used on the setter. + * @return Method the method associated with mPropertyName. + */ + private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) { + // TODO: faster implementation... + Method returnVal = null; + String firstLetter = mPropertyName.substring(0, 1); + String theRest = mPropertyName.substring(1); + firstLetter = firstLetter.toUpperCase(); + String methodName = prefix + firstLetter + theRest; + Class args[] = null; + if (valueType == null) { + try { + returnVal = targetClass.getMethod(methodName, args); + } catch (NoSuchMethodException e) { + Log.e("PropertyValuesHolder", + "Couldn't find no-arg method for property " + mPropertyName + ": " + e); + } + } else { + args = new Class[1]; + Class typeVariants[]; + if (mValueType.equals(Float.class)) { + typeVariants = FLOAT_VARIANTS; + } else if (mValueType.equals(Integer.class)) { + typeVariants = INTEGER_VARIANTS; + } else if (mValueType.equals(Double.class)) { + typeVariants = DOUBLE_VARIANTS; + } else { + typeVariants = new Class[1]; + typeVariants[0] = mValueType; + } + for (Class typeVariant : typeVariants) { + args[0] = typeVariant; + try { + returnVal = targetClass.getMethod(methodName, args); + return returnVal; + } catch (NoSuchMethodException e) { + // Swallow the error and keep trying other variants + } + } + } + // If we got here, then no appropriate function was found + Log.e("PropertyValuesHolder", + "Couldn't find setter/getter for property " + mPropertyName + + "with value type "+ mValueType); + return returnVal; + } + + + /** + * Returns the setter or getter requested. This utility function checks whether the + * requested method exists in the propertyMapMap cache. If not, it calls another + * utility function to request the Method from the targetClass directly. + * @param targetClass The Class on which the requested method should exist. + * @param propertyMapMap The cache of setters/getters derived so far. + * @param prefix "set" or "get", for the setter or getter. + * @param valueType The type of parameter passed into the method (null for getter). + * @return Method the method associated with mPropertyName. + */ + private Method setupSetterOrGetter(Class targetClass, + HashMap<Class, HashMap<String, Method>> propertyMapMap, + String prefix, Class valueType) { + Method setterOrGetter = null; + try { + // Have to lock property map prior to reading it, to guard against + // another thread putting something in there after we've checked it + // but before we've added an entry to it + // TODO: can we store the setter/getter per Class instead of per Object? + propertyMapLock.writeLock().lock(); + HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass); + if (propertyMap != null) { + setterOrGetter = propertyMap.get(mPropertyName); + } + if (setterOrGetter == null) { + setterOrGetter = getPropertyFunction(targetClass, prefix, valueType); + if (propertyMap == null) { + propertyMap = new HashMap<String, Method>(); + propertyMapMap.put(targetClass, propertyMap); + } + propertyMap.put(mPropertyName, setterOrGetter); + } + } finally { + propertyMapLock.writeLock().unlock(); + } + return setterOrGetter; + } + + /** + * Utility function to get the setter from targetClass + * @param targetClass The Class on which the requested method should exist. + */ + private void setupSetter(Class targetClass) { + mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType); + } + + /** + * Utility function to get the getter from targetClass + */ + private void setupGetter(Class targetClass) { + mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null); + } + + /** + * Internal function (called from PropertyAnimator) to set up the setter and getter + * prior to running the animation. If the setter has not been manually set for this + * object, it will be derived automatically given the property name, target object, and + * types of values supplied. If no getter has been set, it will be supplied iff any of the + * supplied values was null. If there is a null value, then the getter (supplied or derived) + * will be called to set those null values to the current value of the property + * on the target object. + * @param target The object on which the setter (and possibly getter) exist. + */ + void setupSetterAndGetter(Object target) { + Class targetClass = target.getClass(); + if (mSetter == null) { + setupSetter(targetClass); + } + for (Keyframe kf : mKeyframeSet.mKeyframes) { + if (kf.getValue() == null) { + if (mGetter == null) { + setupGetter(targetClass); + } + try { + kf.setValue((T) mGetter.invoke(target)); + } catch (InvocationTargetException e) { + Log.e("PropertyValuesHolder", e.toString()); + } catch (IllegalAccessException e) { + Log.e("PropertyValuesHolder", e.toString()); + } + } + } + } + + /** + * 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 PropertyAnimator + * to handle turning the value calculated by Animator 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 + */ + void setAnimatedValue(Object target) { + if (mSetter != null) { + try { + mTmpValueArray[0] = mAnimatedValue; + mSetter.invoke(target, mTmpValueArray); + } catch (InvocationTargetException e) { + Log.e("PropertyValuesHolder", e.toString()); + } catch (IllegalAccessException e) { + Log.e("PropertyValuesHolder", e.toString()); + } + } + } + + /** + * Internal function, called by Animator, to set up the TypeEvaluator that will be used + * to calculate animated values. + */ + void init() { + if (mEvaluator == null) { + mEvaluator = (mValueType == int.class) ? sIntEvaluator : + (mValueType == double.class) ? sDoubleEvaluator : sFloatEvaluator; + } + } + + /** + * The TypeEvaluator will the automatically determined based on the type of values + * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so + * desired. This may be important in cases where either the type of the values supplied + * do not match the way that they should be interpolated between, or if the values + * are of a custom type or one not currently understood by the animation system. Currently, + * only values of type float, double, and int (and their Object equivalents, Float, Double, + * and Integer) are correctly interpolated; all other types require setting a TypeEvaluator. + * @param evaluator + */ + public void setEvaluator(TypeEvaluator evaluator) { + mEvaluator = evaluator; + } + + /** + * Function used to calculate the value according to the evaluator set up for + * this PropertyValuesHolder object. This function is called by Animator.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; + } + + /** + * 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>PropertyAnimator</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>PropertyAnimator</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 + * in a call to the function <code>setFoo()</code> on the target object. If either + * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will + * also be derived and called. + * + * <p>Note that the setter function derived from this property name + * 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 propertyName The name of the property being animated. + */ + public void setPropertyName(String propertyName) { + mPropertyName = propertyName; + } + + /** + * Gets the name of the property that will be animated. This name will be 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 + * in a call to the function <code>setFoo()</code> on the target object. If either + * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will + * also be derived and called. + */ + public String getPropertyName() { + return mPropertyName; + } + + /** + * Internal function, called by Animator and PropertyAnimator, to retrieve the value + * most recently calculated in calculateValue(). + * @return + */ + Object getAnimatedValue() { + return mAnimatedValue; + } +}
\ No newline at end of file |