aboutsummaryrefslogtreecommitdiffstats
path: root/com_actionbarsherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java
diff options
context:
space:
mode:
Diffstat (limited to 'com_actionbarsherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java')
-rw-r--r--com_actionbarsherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java1012
1 files changed, 1012 insertions, 0 deletions
diff --git a/com_actionbarsherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java b/com_actionbarsherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java
new file mode 100644
index 000000000..3a5659506
--- /dev/null
+++ b/com_actionbarsherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java
@@ -0,0 +1,1012 @@
+/*
+ * 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 com.actionbarsherlock.internal.nineoldandroids.animation;
+
+//import android.util.FloatProperty;
+//import android.util.IntProperty;
+import android.util.Log;
+//import android.util.Property;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * This class holds information about a property and the values that that property
+ * should take on during an animation. PropertyValuesHolder objects can be used to create
+ * animations with ValueAnimator or ObjectAnimator that operate on several different properties
+ * in parallel.
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class PropertyValuesHolder implements Cloneable {
+
+ /**
+ * The name of the property associated with the values. This need not be a real property,
+ * unless this object is being used with ObjectAnimator. But this is the name by which
+ * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator.
+ */
+ String mPropertyName;
+
+ /**
+ * @hide
+ */
+ //protected Property mProperty;
+
+ /**
+ * The setter function, if needed. ObjectAnimator hands off this functionality to
+ * PropertyValuesHolder, since it holds all of the per-property information. This
+ * property is automatically
+ * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
+ */
+ Method mSetter = null;
+
+ /**
+ * The getter function, if needed. ObjectAnimator hands off this functionality to
+ * PropertyValuesHolder, since it holds all of the per-property information. This
+ * property is automatically
+ * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
+ * 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.
+ */
+ Class mValueType;
+
+ /**
+ * The set of keyframes (time/value pairs) that define this animation.
+ */
+ KeyframeSet mKeyframeSet = null;
+
+
+ // type evaluators for the primitive types handled by this implementation
+ private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
+ private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
+
+ // 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.
+ final ReentrantReadWriteLock mPropertyMapLock = new ReentrantReadWriteLock();
+
+ // Used to pass single value to varargs parameter in setter invocation
+ final 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 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 ValueAnimator.animatedValue() or
+ * by the property-setting logic in ObjectAnimator.animatedValue().
+ */
+ private Object mAnimatedValue;
+
+ /**
+ * Internal utility constructor, used by the factory methods to set the property name.
+ * @param propertyName The name of the property for this holder.
+ */
+ private PropertyValuesHolder(String propertyName) {
+ mPropertyName = propertyName;
+ }
+
+ /**
+ * Internal utility constructor, used by the factory methods to set the property.
+ * @param property The property for this holder.
+ */
+ //private PropertyValuesHolder(Property property) {
+ // mProperty = property;
+ // if (property != null) {
+ // mPropertyName = property.getName();
+ // }
+ //}
+
+ /**
+ * Constructs and returns a PropertyValuesHolder with a given property name and
+ * set of int 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 ofInt(String propertyName, int... values) {
+ return new IntPropertyValuesHolder(propertyName, values);
+ }
+
+ /**
+ * Constructs and returns a PropertyValuesHolder with a given property and
+ * set of int values.
+ * @param property The property being animated. Should not be null.
+ * @param values The values that the property will animate between.
+ * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+ */
+ //public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
+ // return new IntPropertyValuesHolder(property, values);
+ //}
+
+ /**
+ * Constructs and returns a PropertyValuesHolder with a given property name and
+ * set of float 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 ofFloat(String propertyName, float... values) {
+ return new FloatPropertyValuesHolder(propertyName, values);
+ }
+
+ /**
+ * Constructs and returns a PropertyValuesHolder with a given property and
+ * set of float values.
+ * @param property The property being animated. Should not be null.
+ * @param values The values that the property will animate between.
+ * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+ */
+ //public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
+ // return new FloatPropertyValuesHolder(property, values);
+ //}
+
+ /**
+ * Constructs and returns a PropertyValuesHolder with a given property name and
+ * set of Object values. This variant also takes a TypeEvaluator because the system
+ * cannot automatically interpolate between objects of unknown type.
+ *
+ * @param propertyName The name of the property being animated.
+ * @param evaluator A TypeEvaluator that will be called on each animation frame to
+ * provide the necessary interpolation between the Object values to derive the animated
+ * value.
+ * @param values The values that the named property will animate between.
+ * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+ */
+ public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
+ Object... values) {
+ PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
+ pvh.setObjectValues(values);
+ pvh.setEvaluator(evaluator);
+ return pvh;
+ }
+
+ /**
+ * Constructs and returns a PropertyValuesHolder with a given property and
+ * set of Object values. This variant also takes a TypeEvaluator because the system
+ * cannot automatically interpolate between objects of unknown type.
+ *
+ * @param property The property being animated. Should not be null.
+ * @param evaluator A TypeEvaluator that will be called on each animation frame to
+ * provide the necessary interpolation between the Object values to derive the animated
+ * value.
+ * @param values The values that the property will animate between.
+ * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+ */
+ //public static <V> PropertyValuesHolder ofObject(Property property,
+ // TypeEvaluator<V> evaluator, V... values) {
+ // PropertyValuesHolder pvh = new PropertyValuesHolder(property);
+ // pvh.setObjectValues(values);
+ // pvh.setEvaluator(evaluator);
+ // return pvh;
+ //}
+
+ /**
+ * Constructs and returns 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 ObjectAnimator}, and with a getter function
+ * derived automatically from <code>propertyName</code>, 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 ObjectAnimator object, or
+ * just a name used to get animated values, such as if this object is used with an
+ * ValueAnimator object.
+ * @param values The set of values to animate between.
+ */
+ public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
+ 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;
+ }
+ }
+
+ /**
+ * Constructs and returns a PropertyValuesHolder object with the specified property 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 the property's
+ * {@link android.util.Property#get(Object)} function.
+ * 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 with
+ * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has
+ * no way of determining what the value should be.
+ * @param property The property associated with this set of values. Should not be null.
+ * @param values The set of values to animate between.
+ */
+ //public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
+ // KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
+ // if (keyframeSet instanceof IntKeyframeSet) {
+ // return new IntPropertyValuesHolder(property, (IntKeyframeSet) keyframeSet);
+ // } else if (keyframeSet instanceof FloatKeyframeSet) {
+ // return new FloatPropertyValuesHolder(property, (FloatKeyframeSet) keyframeSet);
+ // }
+ // else {
+ // PropertyValuesHolder pvh = new PropertyValuesHolder(property);
+ // pvh.mKeyframeSet = keyframeSet;
+ // pvh.mValueType = ((Keyframe)values[0]).getType();
+ // return pvh;
+ // }
+ //}
+
+ /**
+ * Set the animated values for this object to this set of ints.
+ * 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
+ * derived automatically from <code>propertyName</code>, 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 setIntValues(int... values) {
+ mValueType = int.class;
+ mKeyframeSet = KeyframeSet.ofInt(values);
+ }
+
+ /**
+ * Set the animated values for this object to this set of floats.
+ * 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
+ * derived automatically from <code>propertyName</code>, 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 setFloatValues(float... values) {
+ mValueType = float.class;
+ mKeyframeSet = KeyframeSet.ofFloat(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.
+ */
+ public void setKeyframes(Keyframe... values) {
+ int numKeyframes = values.length;
+ Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
+ mValueType = ((Keyframe)values[0]).getType();
+ for (int i = 0; i < numKeyframes; ++i) {
+ keyframes[i] = (Keyframe)values[i];
+ }
+ mKeyframeSet = new KeyframeSet(keyframes);
+ }
+
+ /**
+ * Set the animated values for this object to this set of Objects.
+ * 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
+ * derived automatically from <code>propertyName</code>, 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 setObjectValues(Object... values) {
+ mValueType = values[0].getClass();
+ mKeyframeSet = KeyframeSet.ofObject(values);
+ }
+
+ /**
+ * 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 methodName = getMethodName(prefix, mPropertyName);
+ Class args[] = null;
+ if (valueType == null) {
+ try {
+ returnVal = targetClass.getMethod(methodName, args);
+ } catch (NoSuchMethodException e) {
+ Log.e("PropertyValuesHolder", targetClass.getSimpleName() + " - " +
+ "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);
+ // change the value type to suit
+ mValueType = typeVariant;
+ 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 " + prefix + "ter property " + mPropertyName +
+ " for " + targetClass.getSimpleName() +
+ " 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
+ mPropertyMapLock.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 {
+ mPropertyMapLock.writeLock().unlock();
+ }
+ return setterOrGetter;
+ }
+
+ /**
+ * Utility function to get the setter from targetClass
+ * @param targetClass The Class on which the requested method should exist.
+ */
+ 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 ObjectAnimator) 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) {
+ //if (mProperty != null) {
+ // // check to make sure that mProperty is on the class of target
+ // try {
+ // Object testValue = mProperty.get(target);
+ // for (Keyframe kf : mKeyframeSet.mKeyframes) {
+ // if (!kf.hasValue()) {
+ // kf.setValue(mProperty.get(target));
+ // }
+ // }
+ // return;
+ // } catch (ClassCastException e) {
+ // Log.e("PropertyValuesHolder","No such property (" + mProperty.getName() +
+ // ") on target object " + target + ". Trying reflection instead");
+ // mProperty = null;
+ // }
+ //}
+ Class targetClass = target.getClass();
+ if (mSetter == null) {
+ setupSetter(targetClass);
+ }
+ for (Keyframe kf : mKeyframeSet.mKeyframes) {
+ if (!kf.hasValue()) {
+ if (mGetter == null) {
+ setupGetter(targetClass);
+ }
+ try {
+ kf.setValue(mGetter.invoke(target));
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+ }
+ }
+
+ /**
+ * Utility function to set the value stored in a particular Keyframe. The value used is
+ * whatever the value is for the property name specified in the keyframe on the target object.
+ *
+ * @param target The target object from which the current value should be extracted.
+ * @param kf The keyframe which holds the property name and value.
+ */
+ private void setupValue(Object target, Keyframe kf) {
+ //if (mProperty != null) {
+ // kf.setValue(mProperty.get(target));
+ //}
+ try {
+ if (mGetter == null) {
+ Class targetClass = target.getClass();
+ setupGetter(targetClass);
+ }
+ kf.setValue(mGetter.invoke(target));
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+
+ /**
+ * This function is called by ObjectAnimator when setting the start values for an animation.
+ * The start values are set according to the current values in the target object. The
+ * property whose value is extracted is whatever is specified by the propertyName of this
+ * PropertyValuesHolder object.
+ *
+ * @param target The object which holds the start values that should be set.
+ */
+ void setupStartValue(Object target) {
+ setupValue(target, mKeyframeSet.mKeyframes.get(0));
+ }
+
+ /**
+ * This function is called by ObjectAnimator when setting the end values for an animation.
+ * The end values are set according to the current values in the target object. The
+ * property whose value is extracted is whatever is specified by the propertyName of this
+ * PropertyValuesHolder object.
+ *
+ * @param target The object which holds the start values that should be set.
+ */
+ void setupEndValue(Object target) {
+ setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
+ }
+
+ @Override
+ public PropertyValuesHolder clone() {
+ try {
+ PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
+ newPVH.mPropertyName = mPropertyName;
+ //newPVH.mProperty = mProperty;
+ newPVH.mKeyframeSet = mKeyframeSet.clone();
+ newPVH.mEvaluator = mEvaluator;
+ return newPVH;
+ } catch (CloneNotSupportedException e) {
+ // won't reach here
+ return null;
+ }
+ }
+
+ /**
+ * 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
+ */
+ void setAnimatedValue(Object target) {
+ //if (mProperty != null) {
+ // mProperty.set(target, getAnimatedValue());
+ //}
+ if (mSetter != null) {
+ try {
+ mTmpValueArray[0] = getAnimatedValue();
+ mSetter.invoke(target, mTmpValueArray);
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+ }
+
+ /**
+ * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
+ * to calculate animated values.
+ */
+ void init() {
+ if (mEvaluator == null) {
+ // We already handle int and float 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
+ // evaluator if one has been set on this class
+ mKeyframeSet.setEvaluator(mEvaluator);
+ }
+ }
+
+ /**
+ * 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 and int (and their Object equivalents: Float
+ * and Integer) are correctly interpolated; all other types require setting a TypeEvaluator.
+ * @param evaluator
+ */
+ public void setEvaluator(TypeEvaluator evaluator) {
+ mEvaluator = evaluator;
+ mKeyframeSet.setEvaluator(evaluator);
+ }
+
+ /**
+ * Function used to calculate the value according to the evaluator set up for
+ * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
+ *
+ * @param fraction The elapsed, interpolated fraction of the animation.
+ */
+ void calculateValue(float fraction) {
+ mAnimatedValue = mKeyframeSet.getValue(fraction);
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Sets the property that will be animated.
+ *
+ * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
+ * must exist on the target object specified in that ObjectAnimator.</p>
+ *
+ * @param property The property being animated.
+ */
+ //public void setProperty(Property property) {
+ // mProperty = property;
+ //}
+
+ /**
+ * 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 ValueAnimator and ObjectAnimator, to retrieve the value
+ * most recently calculated in calculateValue().
+ * @return
+ */
+ Object getAnimatedValue() {
+ return mAnimatedValue;
+ }
+
+ @Override
+ public String toString() {
+ return mPropertyName + ": " + mKeyframeSet.toString();
+ }
+
+ /**
+ * Utility method to derive a setter/getter method name from a property name, where the
+ * prefix is typically "set" or "get" and the first letter of the property name is
+ * capitalized.
+ *
+ * @param prefix The precursor to the method name, before the property name begins, typically
+ * "set" or "get".
+ * @param propertyName The name of the property that represents the bulk of the method name
+ * after the prefix. The first letter of this word will be capitalized in the resulting
+ * method name.
+ * @return String the property name converted to a method name according to the conventions
+ * specified above.
+ */
+ static String getMethodName(String prefix, String propertyName) {
+ if (propertyName == null || propertyName.length() == 0) {
+ // shouldn't get here
+ return prefix;
+ }
+ char firstLetter = Character.toUpperCase(propertyName.charAt(0));
+ String theRest = propertyName.substring(1);
+ return prefix + firstLetter + theRest;
+ }
+
+ static class IntPropertyValuesHolder extends PropertyValuesHolder {
+
+ // Cache JNI functions to avoid looking them up twice
+ //private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
+ // new HashMap<Class, HashMap<String, Integer>>();
+ //int mJniSetter;
+ //private IntProperty mIntProperty;
+
+ IntKeyframeSet mIntKeyframeSet;
+ int mIntAnimatedValue;
+
+ public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) {
+ super(propertyName);
+ mValueType = int.class;
+ mKeyframeSet = keyframeSet;
+ mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
+ }
+
+ //public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) {
+ // super(property);
+ // mValueType = int.class;
+ // mKeyframeSet = keyframeSet;
+ // mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
+ // if (property instanceof IntProperty) {
+ // mIntProperty = (IntProperty) mProperty;
+ // }
+ //}
+
+ public IntPropertyValuesHolder(String propertyName, int... values) {
+ super(propertyName);
+ setIntValues(values);
+ }
+
+ //public IntPropertyValuesHolder(Property property, int... values) {
+ // super(property);
+ // setIntValues(values);
+ // if (property instanceof IntProperty) {
+ // mIntProperty = (IntProperty) mProperty;
+ // }
+ //}
+
+ @Override
+ public void setIntValues(int... values) {
+ super.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 (mIntProperty != null) {
+ // mIntProperty.setValue(target, mIntAnimatedValue);
+ // return;
+ //}
+ //if (mProperty != null) {
+ // mProperty.set(target, mIntAnimatedValue);
+ // return;
+ //}
+ //if (mJniSetter != 0) {
+ // nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
+ // return;
+ //}
+ 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());
+ }
+ }
+ }
+
+ @Override
+ void setupSetter(Class targetClass) {
+ //if (mProperty != null) {
+ // return;
+ //}
+ // Check new static hashmap<propName, int> for setter method
+ //try {
+ // mPropertyMapLock.writeLock().lock();
+ // HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
+ // if (propertyMap != null) {
+ // Integer mJniSetterInteger = propertyMap.get(mPropertyName);
+ // if (mJniSetterInteger != null) {
+ // mJniSetter = mJniSetterInteger;
+ // }
+ // }
+ // if (mJniSetter == 0) {
+ // String methodName = getMethodName("set", mPropertyName);
+ // mJniSetter = nGetIntMethod(targetClass, methodName);
+ // if (mJniSetter != 0) {
+ // if (propertyMap == null) {
+ // propertyMap = new HashMap<String, Integer>();
+ // sJNISetterPropertyMap.put(targetClass, propertyMap);
+ // }
+ // propertyMap.put(mPropertyName, mJniSetter);
+ // }
+ // }
+ //} catch (NoSuchMethodError e) {
+ // Log.d("PropertyValuesHolder",
+ // "Can't find native method using JNI, use reflection" + e);
+ //} finally {
+ // mPropertyMapLock.writeLock().unlock();
+ //}
+ //if (mJniSetter == 0) {
+ // Couldn't find method through fast JNI approach - just use reflection
+ super.setupSetter(targetClass);
+ //}
+ }
+ }
+
+ static class FloatPropertyValuesHolder extends PropertyValuesHolder {
+
+ // Cache JNI functions to avoid looking them up twice
+ //private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
+ // new HashMap<Class, HashMap<String, Integer>>();
+ //int mJniSetter;
+ //private FloatProperty mFloatProperty;
+
+ FloatKeyframeSet mFloatKeyframeSet;
+ float mFloatAnimatedValue;
+
+ public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
+ super(propertyName);
+ mValueType = float.class;
+ mKeyframeSet = keyframeSet;
+ mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
+ }
+
+ //public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) {
+ // super(property);
+ // mValueType = float.class;
+ // mKeyframeSet = keyframeSet;
+ // mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
+ // if (property instanceof FloatProperty) {
+ // mFloatProperty = (FloatProperty) mProperty;
+ // }
+ //}
+
+ public FloatPropertyValuesHolder(String propertyName, float... values) {
+ super(propertyName);
+ setFloatValues(values);
+ }
+
+ //public FloatPropertyValuesHolder(Property property, float... values) {
+ // super(property);
+ // setFloatValues(values);
+ // if (property instanceof FloatProperty) {
+ // mFloatProperty = (FloatProperty) mProperty;
+ // }
+ //}
+
+ @Override
+ public void setFloatValues(float... values) {
+ super.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 (mFloatProperty != null) {
+ // mFloatProperty.setValue(target, mFloatAnimatedValue);
+ // return;
+ //}
+ //if (mProperty != null) {
+ // mProperty.set(target, mFloatAnimatedValue);
+ // return;
+ //}
+ //if (mJniSetter != 0) {
+ // nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
+ // return;
+ //}
+ 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());
+ }
+ }
+ }
+
+ @Override
+ void setupSetter(Class targetClass) {
+ //if (mProperty != null) {
+ // return;
+ //}
+ // Check new static hashmap<propName, int> for setter method
+ //try {
+ // mPropertyMapLock.writeLock().lock();
+ // HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
+ // if (propertyMap != null) {
+ // Integer mJniSetterInteger = propertyMap.get(mPropertyName);
+ // if (mJniSetterInteger != null) {
+ // mJniSetter = mJniSetterInteger;
+ // }
+ // }
+ // if (mJniSetter == 0) {
+ // String methodName = getMethodName("set", mPropertyName);
+ // mJniSetter = nGetFloatMethod(targetClass, methodName);
+ // if (mJniSetter != 0) {
+ // if (propertyMap == null) {
+ // propertyMap = new HashMap<String, Integer>();
+ // sJNISetterPropertyMap.put(targetClass, propertyMap);
+ // }
+ // propertyMap.put(mPropertyName, mJniSetter);
+ // }
+ // }
+ //} catch (NoSuchMethodError e) {
+ // Log.d("PropertyValuesHolder",
+ // "Can't find native method using JNI, use reflection" + e);
+ //} finally {
+ // mPropertyMapLock.writeLock().unlock();
+ //}
+ //if (mJniSetter == 0) {
+ // Couldn't find method through fast JNI approach - just use reflection
+ super.setupSetter(targetClass);
+ //}
+ }
+
+ }
+
+ //native static private int nGetIntMethod(Class targetClass, String methodName);
+ //native static private int nGetFloatMethod(Class targetClass, String methodName);
+ //native static private void nCallIntMethod(Object target, int methodID, int arg);
+ //native static private void nCallFloatMethod(Object target, int methodID, float arg);
+}