Home | History | Annotate | Download | only in animation
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.animation;
     18 
     19 import android.util.FloatProperty;
     20 import android.util.IntProperty;
     21 import android.util.Log;
     22 import android.util.Property;
     23 
     24 import java.lang.reflect.InvocationTargetException;
     25 import java.lang.reflect.Method;
     26 import java.util.HashMap;
     27 import java.util.concurrent.locks.ReentrantReadWriteLock;
     28 
     29 /**
     30  * This class holds information about a property and the values that that property
     31  * should take on during an animation. PropertyValuesHolder objects can be used to create
     32  * animations with ValueAnimator or ObjectAnimator that operate on several different properties
     33  * in parallel.
     34  */
     35 public class PropertyValuesHolder implements Cloneable {
     36 
     37     /**
     38      * The name of the property associated with the values. This need not be a real property,
     39      * unless this object is being used with ObjectAnimator. But this is the name by which
     40      * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator.
     41      */
     42     String mPropertyName;
     43 
     44     /**
     45      * @hide
     46      */
     47     protected Property mProperty;
     48 
     49     /**
     50      * The setter function, if needed. ObjectAnimator hands off this functionality to
     51      * PropertyValuesHolder, since it holds all of the per-property information. This
     52      * property is automatically
     53      * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
     54      */
     55     Method mSetter = null;
     56 
     57     /**
     58      * The getter function, if needed. ObjectAnimator hands off this functionality to
     59      * PropertyValuesHolder, since it holds all of the per-property information. This
     60      * property is automatically
     61      * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
     62      * The getter is only derived and used if one of the values is null.
     63      */
     64     private Method mGetter = null;
     65 
     66     /**
     67      * The type of values supplied. This information is used both in deriving the setter/getter
     68      * functions and in deriving the type of TypeEvaluator.
     69      */
     70     Class mValueType;
     71 
     72     /**
     73      * The set of keyframes (time/value pairs) that define this animation.
     74      */
     75     KeyframeSet mKeyframeSet = null;
     76 
     77 
     78     // type evaluators for the primitive types handled by this implementation
     79     private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
     80     private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
     81 
     82     // We try several different types when searching for appropriate setter/getter functions.
     83     // The caller may have supplied values in a type that does not match the setter/getter
     84     // functions (such as the integers 0 and 1 to represent floating point values for alpha).
     85     // Also, the use of generics in constructors means that we end up with the Object versions
     86     // of primitive types (Float vs. float). But most likely, the setter/getter functions
     87     // will take primitive types instead.
     88     // So we supply an ordered array of other types to try before giving up.
     89     private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
     90             Double.class, Integer.class};
     91     private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
     92             Float.class, Double.class};
     93     private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class,
     94             Float.class, Integer.class};
     95 
     96     // These maps hold all property entries for a particular class. This map
     97     // is used to speed up property/setter/getter lookups for a given class/property
     98     // combination. No need to use reflection on the combination more than once.
     99     private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap =
    100             new HashMap<Class, HashMap<String, Method>>();
    101     private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap =
    102             new HashMap<Class, HashMap<String, Method>>();
    103 
    104     // This lock is used to ensure that only one thread is accessing the property maps
    105     // at a time.
    106     final ReentrantReadWriteLock mPropertyMapLock = new ReentrantReadWriteLock();
    107 
    108     // Used to pass single value to varargs parameter in setter invocation
    109     final Object[] mTmpValueArray = new Object[1];
    110 
    111     /**
    112      * The type evaluator used to calculate the animated values. This evaluator is determined
    113      * automatically based on the type of the start/end objects passed into the constructor,
    114      * but the system only knows about the primitive types int and float. Any other
    115      * type will need to set the evaluator to a custom evaluator for that type.
    116      */
    117     private TypeEvaluator mEvaluator;
    118 
    119     /**
    120      * The value most recently calculated by calculateValue(). This is set during
    121      * that function and might be retrieved later either by ValueAnimator.animatedValue() or
    122      * by the property-setting logic in ObjectAnimator.animatedValue().
    123      */
    124     private Object mAnimatedValue;
    125 
    126     /**
    127      * Internal utility constructor, used by the factory methods to set the property name.
    128      * @param propertyName The name of the property for this holder.
    129      */
    130     private PropertyValuesHolder(String propertyName) {
    131         mPropertyName = propertyName;
    132     }
    133 
    134     /**
    135      * Internal utility constructor, used by the factory methods to set the property.
    136      * @param property The property for this holder.
    137      */
    138     private PropertyValuesHolder(Property property) {
    139         mProperty = property;
    140         if (property != null) {
    141             mPropertyName = property.getName();
    142         }
    143     }
    144 
    145     /**
    146      * Constructs and returns a PropertyValuesHolder with a given property name and
    147      * set of int values.
    148      * @param propertyName The name of the property being animated.
    149      * @param values The values that the named property will animate between.
    150      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    151      */
    152     public static PropertyValuesHolder ofInt(String propertyName, int... values) {
    153         return new IntPropertyValuesHolder(propertyName, values);
    154     }
    155 
    156     /**
    157      * Constructs and returns a PropertyValuesHolder with a given property and
    158      * set of int values.
    159      * @param property The property being animated. Should not be null.
    160      * @param values The values that the property will animate between.
    161      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    162      */
    163     public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
    164         return new IntPropertyValuesHolder(property, values);
    165     }
    166 
    167     /**
    168      * Constructs and returns a PropertyValuesHolder with a given property name and
    169      * set of float values.
    170      * @param propertyName The name of the property being animated.
    171      * @param values The values that the named property will animate between.
    172      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    173      */
    174     public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
    175         return new FloatPropertyValuesHolder(propertyName, values);
    176     }
    177 
    178     /**
    179      * Constructs and returns a PropertyValuesHolder with a given property and
    180      * set of float values.
    181      * @param property The property being animated. Should not be null.
    182      * @param values The values that the property will animate between.
    183      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    184      */
    185     public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
    186         return new FloatPropertyValuesHolder(property, values);
    187     }
    188 
    189     /**
    190      * Constructs and returns a PropertyValuesHolder with a given property name and
    191      * set of Object values. This variant also takes a TypeEvaluator because the system
    192      * cannot automatically interpolate between objects of unknown type.
    193      *
    194      * @param propertyName The name of the property being animated.
    195      * @param evaluator A TypeEvaluator that will be called on each animation frame to
    196      * provide the necessary interpolation between the Object values to derive the animated
    197      * value.
    198      * @param values The values that the named property will animate between.
    199      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    200      */
    201     public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
    202             Object... values) {
    203         PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
    204         pvh.setObjectValues(values);
    205         pvh.setEvaluator(evaluator);
    206         return pvh;
    207     }
    208 
    209     /**
    210      * Constructs and returns a PropertyValuesHolder with a given property and
    211      * set of Object values. This variant also takes a TypeEvaluator because the system
    212      * cannot automatically interpolate between objects of unknown type.
    213      *
    214      * @param property The property being animated. Should not be null.
    215      * @param evaluator A TypeEvaluator that will be called on each animation frame to
    216      * provide the necessary interpolation between the Object values to derive the animated
    217      * value.
    218      * @param values The values that the property will animate between.
    219      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    220      */
    221     public static <V> PropertyValuesHolder ofObject(Property property,
    222             TypeEvaluator<V> evaluator, V... values) {
    223         PropertyValuesHolder pvh = new PropertyValuesHolder(property);
    224         pvh.setObjectValues(values);
    225         pvh.setEvaluator(evaluator);
    226         return pvh;
    227     }
    228 
    229     /**
    230      * Constructs and returns a PropertyValuesHolder object with the specified property name and set
    231      * of values. These values can be of any type, but the type should be consistent so that
    232      * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
    233      * the common type.
    234      * <p>If there is only one value, it is assumed to be the end value of an animation,
    235      * and an initial value will be derived, if possible, by calling a getter function
    236      * on the object. Also, if any value is null, the value will be filled in when the animation
    237      * starts in the same way. This mechanism of automatically getting null values only works
    238      * if the PropertyValuesHolder object is used in conjunction
    239      * {@link ObjectAnimator}, and with a getter function
    240      * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
    241      * no way of determining what the value should be.
    242      * @param propertyName The name of the property associated with this set of values. This
    243      * can be the actual property name to be used when using a ObjectAnimator object, or
    244      * just a name used to get animated values, such as if this object is used with an
    245      * ValueAnimator object.
    246      * @param values The set of values to animate between.
    247      */
    248     public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
    249         KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
    250         if (keyframeSet instanceof IntKeyframeSet) {
    251             return new IntPropertyValuesHolder(propertyName, (IntKeyframeSet) keyframeSet);
    252         } else if (keyframeSet instanceof FloatKeyframeSet) {
    253             return new FloatPropertyValuesHolder(propertyName, (FloatKeyframeSet) keyframeSet);
    254         }
    255         else {
    256             PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
    257             pvh.mKeyframeSet = keyframeSet;
    258             pvh.mValueType = ((Keyframe)values[0]).getType();
    259             return pvh;
    260         }
    261     }
    262 
    263     /**
    264      * Constructs and returns a PropertyValuesHolder object with the specified property and set
    265      * of values. These values can be of any type, but the type should be consistent so that
    266      * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
    267      * the common type.
    268      * <p>If there is only one value, it is assumed to be the end value of an animation,
    269      * and an initial value will be derived, if possible, by calling the property's
    270      * {@link android.util.Property#get(Object)} function.
    271      * Also, if any value is null, the value will be filled in when the animation
    272      * starts in the same way. This mechanism of automatically getting null values only works
    273      * if the PropertyValuesHolder object is used in conjunction with
    274      * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has
    275      * no way of determining what the value should be.
    276      * @param property The property associated with this set of values. Should not be null.
    277      * @param values The set of values to animate between.
    278      */
    279     public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
    280         KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
    281         if (keyframeSet instanceof IntKeyframeSet) {
    282             return new IntPropertyValuesHolder(property, (IntKeyframeSet) keyframeSet);
    283         } else if (keyframeSet instanceof FloatKeyframeSet) {
    284             return new FloatPropertyValuesHolder(property, (FloatKeyframeSet) keyframeSet);
    285         }
    286         else {
    287             PropertyValuesHolder pvh = new PropertyValuesHolder(property);
    288             pvh.mKeyframeSet = keyframeSet;
    289             pvh.mValueType = ((Keyframe)values[0]).getType();
    290             return pvh;
    291         }
    292     }
    293 
    294     /**
    295      * Set the animated values for this object to this set of ints.
    296      * If there is only one value, it is assumed to be the end value of an animation,
    297      * and an initial value will be derived, if possible, by calling a getter function
    298      * on the object. Also, if any value is null, the value will be filled in when the animation
    299      * starts in the same way. This mechanism of automatically getting null values only works
    300      * if the PropertyValuesHolder object is used in conjunction
    301      * {@link ObjectAnimator}, and with a getter function
    302      * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
    303      * no way of determining what the value should be.
    304      *
    305      * @param values One or more values that the animation will animate between.
    306      */
    307     public void setIntValues(int... values) {
    308         mValueType = int.class;
    309         mKeyframeSet = KeyframeSet.ofInt(values);
    310     }
    311 
    312     /**
    313      * Set the animated values for this object to this set of floats.
    314      * If there is only one value, it is assumed to be the end value of an animation,
    315      * and an initial value will be derived, if possible, by calling a getter function
    316      * on the object. Also, if any value is null, the value will be filled in when the animation
    317      * starts in the same way. This mechanism of automatically getting null values only works
    318      * if the PropertyValuesHolder object is used in conjunction
    319      * {@link ObjectAnimator}, and with a getter function
    320      * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
    321      * no way of determining what the value should be.
    322      *
    323      * @param values One or more values that the animation will animate between.
    324      */
    325     public void setFloatValues(float... values) {
    326         mValueType = float.class;
    327         mKeyframeSet = KeyframeSet.ofFloat(values);
    328     }
    329 
    330     /**
    331      * Set the animated values for this object to this set of Keyframes.
    332      *
    333      * @param values One or more values that the animation will animate between.
    334      */
    335     public void setKeyframes(Keyframe... values) {
    336         int numKeyframes = values.length;
    337         Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
    338         mValueType = ((Keyframe)values[0]).getType();
    339         for (int i = 0; i < numKeyframes; ++i) {
    340             keyframes[i] = (Keyframe)values[i];
    341         }
    342         mKeyframeSet = new KeyframeSet(keyframes);
    343     }
    344 
    345     /**
    346      * Set the animated values for this object to this set of Objects.
    347      * If there is only one value, it is assumed to be the end value of an animation,
    348      * and an initial value will be derived, if possible, by calling a getter function
    349      * on the object. Also, if any value is null, the value will be filled in when the animation
    350      * starts in the same way. This mechanism of automatically getting null values only works
    351      * if the PropertyValuesHolder object is used in conjunction
    352      * {@link ObjectAnimator}, and with a getter function
    353      * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
    354      * no way of determining what the value should be.
    355      *
    356      * @param values One or more values that the animation will animate between.
    357      */
    358     public void setObjectValues(Object... values) {
    359         mValueType = values[0].getClass();
    360         mKeyframeSet = KeyframeSet.ofObject(values);
    361     }
    362 
    363     /**
    364      * Determine the setter or getter function using the JavaBeans convention of setFoo or
    365      * getFoo for a property named 'foo'. This function figures out what the name of the
    366      * function should be and uses reflection to find the Method with that name on the
    367      * target object.
    368      *
    369      * @param targetClass The class to search for the method
    370      * @param prefix "set" or "get", depending on whether we need a setter or getter.
    371      * @param valueType The type of the parameter (in the case of a setter). This type
    372      * is derived from the values set on this PropertyValuesHolder. This type is used as
    373      * a first guess at the parameter type, but we check for methods with several different
    374      * types to avoid problems with slight mis-matches between supplied values and actual
    375      * value types used on the setter.
    376      * @return Method the method associated with mPropertyName.
    377      */
    378     private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
    379         // TODO: faster implementation...
    380         Method returnVal = null;
    381         String methodName = getMethodName(prefix, mPropertyName);
    382         Class args[] = null;
    383         if (valueType == null) {
    384             try {
    385                 returnVal = targetClass.getMethod(methodName, args);
    386             } catch (NoSuchMethodException e) {
    387                 Log.e("PropertyValuesHolder",
    388                         "Couldn't find no-arg method for property " + mPropertyName + ": " + e);
    389             }
    390         } else {
    391             args = new Class[1];
    392             Class typeVariants[];
    393             if (mValueType.equals(Float.class)) {
    394                 typeVariants = FLOAT_VARIANTS;
    395             } else if (mValueType.equals(Integer.class)) {
    396                 typeVariants = INTEGER_VARIANTS;
    397             } else if (mValueType.equals(Double.class)) {
    398                 typeVariants = DOUBLE_VARIANTS;
    399             } else {
    400                 typeVariants = new Class[1];
    401                 typeVariants[0] = mValueType;
    402             }
    403             for (Class typeVariant : typeVariants) {
    404                 args[0] = typeVariant;
    405                 try {
    406                     returnVal = targetClass.getMethod(methodName, args);
    407                     // change the value type to suit
    408                     mValueType = typeVariant;
    409                     return returnVal;
    410                 } catch (NoSuchMethodException e) {
    411                     // Swallow the error and keep trying other variants
    412                 }
    413             }
    414             // If we got here, then no appropriate function was found
    415             Log.e("PropertyValuesHolder",
    416                     "Couldn't find setter/getter for property " + mPropertyName +
    417                             " with value type "+ mValueType);
    418         }
    419 
    420         return returnVal;
    421     }
    422 
    423 
    424     /**
    425      * Returns the setter or getter requested. This utility function checks whether the
    426      * requested method exists in the propertyMapMap cache. If not, it calls another
    427      * utility function to request the Method from the targetClass directly.
    428      * @param targetClass The Class on which the requested method should exist.
    429      * @param propertyMapMap The cache of setters/getters derived so far.
    430      * @param prefix "set" or "get", for the setter or getter.
    431      * @param valueType The type of parameter passed into the method (null for getter).
    432      * @return Method the method associated with mPropertyName.
    433      */
    434     private Method setupSetterOrGetter(Class targetClass,
    435             HashMap<Class, HashMap<String, Method>> propertyMapMap,
    436             String prefix, Class valueType) {
    437         Method setterOrGetter = null;
    438         try {
    439             // Have to lock property map prior to reading it, to guard against
    440             // another thread putting something in there after we've checked it
    441             // but before we've added an entry to it
    442             mPropertyMapLock.writeLock().lock();
    443             HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
    444             if (propertyMap != null) {
    445                 setterOrGetter = propertyMap.get(mPropertyName);
    446             }
    447             if (setterOrGetter == null) {
    448                 setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
    449                 if (propertyMap == null) {
    450                     propertyMap = new HashMap<String, Method>();
    451                     propertyMapMap.put(targetClass, propertyMap);
    452                 }
    453                 propertyMap.put(mPropertyName, setterOrGetter);
    454             }
    455         } finally {
    456             mPropertyMapLock.writeLock().unlock();
    457         }
    458         return setterOrGetter;
    459     }
    460 
    461     /**
    462      * Utility function to get the setter from targetClass
    463      * @param targetClass The Class on which the requested method should exist.
    464      */
    465     void setupSetter(Class targetClass) {
    466         mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);
    467     }
    468 
    469     /**
    470      * Utility function to get the getter from targetClass
    471      */
    472     private void setupGetter(Class targetClass) {
    473         mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
    474     }
    475 
    476     /**
    477      * Internal function (called from ObjectAnimator) to set up the setter and getter
    478      * prior to running the animation. If the setter has not been manually set for this
    479      * object, it will be derived automatically given the property name, target object, and
    480      * types of values supplied. If no getter has been set, it will be supplied iff any of the
    481      * supplied values was null. If there is a null value, then the getter (supplied or derived)
    482      * will be called to set those null values to the current value of the property
    483      * on the target object.
    484      * @param target The object on which the setter (and possibly getter) exist.
    485      */
    486     void setupSetterAndGetter(Object target) {
    487         if (mProperty != null) {
    488             // check to make sure that mProperty is on the class of target
    489             try {
    490                 Object testValue = mProperty.get(target);
    491                 for (Keyframe kf : mKeyframeSet.mKeyframes) {
    492                     if (!kf.hasValue()) {
    493                         kf.setValue(mProperty.get(target));
    494                     }
    495                 }
    496                 return;
    497             } catch (ClassCastException e) {
    498                 Log.e("PropertyValuesHolder","No such property (" + mProperty.getName() +
    499                         ") on target object " + target + ". Trying reflection instead");
    500                 mProperty = null;
    501             }
    502         }
    503         Class targetClass = target.getClass();
    504         if (mSetter == null) {
    505             setupSetter(targetClass);
    506         }
    507         for (Keyframe kf : mKeyframeSet.mKeyframes) {
    508             if (!kf.hasValue()) {
    509                 if (mGetter == null) {
    510                     setupGetter(targetClass);
    511                 }
    512                 try {
    513                     kf.setValue(mGetter.invoke(target));
    514                 } catch (InvocationTargetException e) {
    515                     Log.e("PropertyValuesHolder", e.toString());
    516                 } catch (IllegalAccessException e) {
    517                     Log.e("PropertyValuesHolder", e.toString());
    518                 }
    519             }
    520         }
    521     }
    522 
    523     /**
    524      * Utility function to set the value stored in a particular Keyframe. The value used is
    525      * whatever the value is for the property name specified in the keyframe on the target object.
    526      *
    527      * @param target The target object from which the current value should be extracted.
    528      * @param kf The keyframe which holds the property name and value.
    529      */
    530     private void setupValue(Object target, Keyframe kf) {
    531         if (mProperty != null) {
    532             kf.setValue(mProperty.get(target));
    533         }
    534         try {
    535             if (mGetter == null) {
    536                 Class targetClass = target.getClass();
    537                 setupGetter(targetClass);
    538             }
    539             kf.setValue(mGetter.invoke(target));
    540         } catch (InvocationTargetException e) {
    541             Log.e("PropertyValuesHolder", e.toString());
    542         } catch (IllegalAccessException e) {
    543             Log.e("PropertyValuesHolder", e.toString());
    544         }
    545     }
    546 
    547     /**
    548      * This function is called by ObjectAnimator when setting the start values for an animation.
    549      * The start values are set according to the current values in the target object. The
    550      * property whose value is extracted is whatever is specified by the propertyName of this
    551      * PropertyValuesHolder object.
    552      *
    553      * @param target The object which holds the start values that should be set.
    554      */
    555     void setupStartValue(Object target) {
    556         setupValue(target, mKeyframeSet.mKeyframes.get(0));
    557     }
    558 
    559     /**
    560      * This function is called by ObjectAnimator when setting the end values for an animation.
    561      * The end values are set according to the current values in the target object. The
    562      * property whose value is extracted is whatever is specified by the propertyName of this
    563      * PropertyValuesHolder object.
    564      *
    565      * @param target The object which holds the start values that should be set.
    566      */
    567     void setupEndValue(Object target) {
    568         setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
    569     }
    570 
    571     @Override
    572     public PropertyValuesHolder clone() {
    573         try {
    574             PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
    575             newPVH.mPropertyName = mPropertyName;
    576             newPVH.mProperty = mProperty;
    577             newPVH.mKeyframeSet = mKeyframeSet.clone();
    578             newPVH.mEvaluator = mEvaluator;
    579             return newPVH;
    580         } catch (CloneNotSupportedException e) {
    581             // won't reach here
    582             return null;
    583         }
    584     }
    585 
    586     /**
    587      * Internal function to set the value on the target object, using the setter set up
    588      * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
    589      * to handle turning the value calculated by ValueAnimator into a value set on the object
    590      * according to the name of the property.
    591      * @param target The target object on which the value is set
    592      */
    593     void setAnimatedValue(Object target) {
    594         if (mProperty != null) {
    595             mProperty.set(target, getAnimatedValue());
    596         }
    597         if (mSetter != null) {
    598             try {
    599                 mTmpValueArray[0] = getAnimatedValue();
    600                 mSetter.invoke(target, mTmpValueArray);
    601             } catch (InvocationTargetException e) {
    602                 Log.e("PropertyValuesHolder", e.toString());
    603             } catch (IllegalAccessException e) {
    604                 Log.e("PropertyValuesHolder", e.toString());
    605             }
    606         }
    607     }
    608 
    609     /**
    610      * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
    611      * to calculate animated values.
    612      */
    613     void init() {
    614         if (mEvaluator == null) {
    615             // We already handle int and float automatically, but not their Object
    616             // equivalents
    617             mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
    618                     (mValueType == Float.class) ? sFloatEvaluator :
    619                     null;
    620         }
    621         if (mEvaluator != null) {
    622             // KeyframeSet knows how to evaluate the common types - only give it a custom
    623             // evaluator if one has been set on this class
    624             mKeyframeSet.setEvaluator(mEvaluator);
    625         }
    626     }
    627 
    628     /**
    629      * The TypeEvaluator will the automatically determined based on the type of values
    630      * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
    631      * desired. This may be important in cases where either the type of the values supplied
    632      * do not match the way that they should be interpolated between, or if the values
    633      * are of a custom type or one not currently understood by the animation system. Currently,
    634      * only values of type float and int (and their Object equivalents: Float
    635      * and Integer) are  correctly interpolated; all other types require setting a TypeEvaluator.
    636      * @param evaluator
    637      */
    638     public void setEvaluator(TypeEvaluator evaluator) {
    639         mEvaluator = evaluator;
    640         mKeyframeSet.setEvaluator(evaluator);
    641     }
    642 
    643     /**
    644      * Function used to calculate the value according to the evaluator set up for
    645      * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
    646      *
    647      * @param fraction The elapsed, interpolated fraction of the animation.
    648      */
    649     void calculateValue(float fraction) {
    650         mAnimatedValue = mKeyframeSet.getValue(fraction);
    651     }
    652 
    653     /**
    654      * Sets the name of the property that will be animated. This name is used to derive
    655      * a setter function that will be called to set animated values.
    656      * For example, a property name of <code>foo</code> will result
    657      * in a call to the function <code>setFoo()</code> on the target object. If either
    658      * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
    659      * also be derived and called.
    660      *
    661      * <p>Note that the setter function derived from this property name
    662      * must take the same parameter type as the
    663      * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
    664      * the setter function will fail.</p>
    665      *
    666      * @param propertyName The name of the property being animated.
    667      */
    668     public void setPropertyName(String propertyName) {
    669         mPropertyName = propertyName;
    670     }
    671 
    672     /**
    673      * Sets the property that will be animated.
    674      *
    675      * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
    676      * must exist on the target object specified in that ObjectAnimator.</p>
    677      *
    678      * @param property The property being animated.
    679      */
    680     public void setProperty(Property property) {
    681         mProperty = property;
    682     }
    683 
    684     /**
    685      * Gets the name of the property that will be animated. This name will be used to derive
    686      * a setter function that will be called to set animated values.
    687      * For example, a property name of <code>foo</code> will result
    688      * in a call to the function <code>setFoo()</code> on the target object. If either
    689      * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
    690      * also be derived and called.
    691      */
    692     public String getPropertyName() {
    693         return mPropertyName;
    694     }
    695 
    696     /**
    697      * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
    698      * most recently calculated in calculateValue().
    699      * @return
    700      */
    701     Object getAnimatedValue() {
    702         return mAnimatedValue;
    703     }
    704 
    705     @Override
    706     public String toString() {
    707         return mPropertyName + ": " + mKeyframeSet.toString();
    708     }
    709 
    710     /**
    711      * Utility method to derive a setter/getter method name from a property name, where the
    712      * prefix is typically "set" or "get" and the first letter of the property name is
    713      * capitalized.
    714      *
    715      * @param prefix The precursor to the method name, before the property name begins, typically
    716      * "set" or "get".
    717      * @param propertyName The name of the property that represents the bulk of the method name
    718      * after the prefix. The first letter of this word will be capitalized in the resulting
    719      * method name.
    720      * @return String the property name converted to a method name according to the conventions
    721      * specified above.
    722      */
    723     static String getMethodName(String prefix, String propertyName) {
    724         if (propertyName == null || propertyName.length() == 0) {
    725             // shouldn't get here
    726             return prefix;
    727         }
    728         char firstLetter = Character.toUpperCase(propertyName.charAt(0));
    729         String theRest = propertyName.substring(1);
    730         return prefix + firstLetter + theRest;
    731     }
    732 
    733     static class IntPropertyValuesHolder extends PropertyValuesHolder {
    734 
    735         // Cache JNI functions to avoid looking them up twice
    736         private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
    737                 new HashMap<Class, HashMap<String, Integer>>();
    738         int mJniSetter;
    739         private IntProperty mIntProperty;
    740 
    741         IntKeyframeSet mIntKeyframeSet;
    742         int mIntAnimatedValue;
    743 
    744         public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) {
    745             super(propertyName);
    746             mValueType = int.class;
    747             mKeyframeSet = keyframeSet;
    748             mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
    749         }
    750 
    751         public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) {
    752             super(property);
    753             mValueType = int.class;
    754             mKeyframeSet = keyframeSet;
    755             mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
    756             if (property instanceof  IntProperty) {
    757                 mIntProperty = (IntProperty) mProperty;
    758             }
    759         }
    760 
    761         public IntPropertyValuesHolder(String propertyName, int... values) {
    762             super(propertyName);
    763             setIntValues(values);
    764         }
    765 
    766         public IntPropertyValuesHolder(Property property, int... values) {
    767             super(property);
    768             setIntValues(values);
    769             if (property instanceof  IntProperty) {
    770                 mIntProperty = (IntProperty) mProperty;
    771             }
    772         }
    773 
    774         @Override
    775         public void setIntValues(int... values) {
    776             super.setIntValues(values);
    777             mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
    778         }
    779 
    780         @Override
    781         void calculateValue(float fraction) {
    782             mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction);
    783         }
    784 
    785         @Override
    786         Object getAnimatedValue() {
    787             return mIntAnimatedValue;
    788         }
    789 
    790         @Override
    791         public IntPropertyValuesHolder clone() {
    792             IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
    793             newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet;
    794             return newPVH;
    795         }
    796 
    797         /**
    798          * Internal function to set the value on the target object, using the setter set up
    799          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
    800          * to handle turning the value calculated by ValueAnimator into a value set on the object
    801          * according to the name of the property.
    802          * @param target The target object on which the value is set
    803          */
    804         @Override
    805         void setAnimatedValue(Object target) {
    806             if (mIntProperty != null) {
    807                 mIntProperty.setValue(target, mIntAnimatedValue);
    808                 return;
    809             }
    810             if (mProperty != null) {
    811                 mProperty.set(target, mIntAnimatedValue);
    812                 return;
    813             }
    814             if (mJniSetter != 0) {
    815                 nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
    816                 return;
    817             }
    818             if (mSetter != null) {
    819                 try {
    820                     mTmpValueArray[0] = mIntAnimatedValue;
    821                     mSetter.invoke(target, mTmpValueArray);
    822                 } catch (InvocationTargetException e) {
    823                     Log.e("PropertyValuesHolder", e.toString());
    824                 } catch (IllegalAccessException e) {
    825                     Log.e("PropertyValuesHolder", e.toString());
    826                 }
    827             }
    828         }
    829 
    830         @Override
    831         void setupSetter(Class targetClass) {
    832             if (mProperty != null) {
    833                 return;
    834             }
    835             // Check new static hashmap<propName, int> for setter method
    836             try {
    837                 mPropertyMapLock.writeLock().lock();
    838                 HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
    839                 if (propertyMap != null) {
    840                     Integer mJniSetterInteger = propertyMap.get(mPropertyName);
    841                     if (mJniSetterInteger != null) {
    842                         mJniSetter = mJniSetterInteger;
    843                     }
    844                 }
    845                 if (mJniSetter == 0) {
    846                     String methodName = getMethodName("set", mPropertyName);
    847                     mJniSetter = nGetIntMethod(targetClass, methodName);
    848                     if (mJniSetter != 0) {
    849                         if (propertyMap == null) {
    850                             propertyMap = new HashMap<String, Integer>();
    851                             sJNISetterPropertyMap.put(targetClass, propertyMap);
    852                         }
    853                         propertyMap.put(mPropertyName, mJniSetter);
    854                     }
    855                 }
    856             } catch (NoSuchMethodError e) {
    857                 Log.d("PropertyValuesHolder",
    858                         "Can't find native method using JNI, use reflection" + e);
    859             } finally {
    860                 mPropertyMapLock.writeLock().unlock();
    861             }
    862             if (mJniSetter == 0) {
    863                 // Couldn't find method through fast JNI approach - just use reflection
    864                 super.setupSetter(targetClass);
    865             }
    866         }
    867     }
    868 
    869     static class FloatPropertyValuesHolder extends PropertyValuesHolder {
    870 
    871         // Cache JNI functions to avoid looking them up twice
    872         private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
    873                 new HashMap<Class, HashMap<String, Integer>>();
    874         int mJniSetter;
    875         private FloatProperty mFloatProperty;
    876 
    877         FloatKeyframeSet mFloatKeyframeSet;
    878         float mFloatAnimatedValue;
    879 
    880         public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
    881             super(propertyName);
    882             mValueType = float.class;
    883             mKeyframeSet = keyframeSet;
    884             mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
    885         }
    886 
    887         public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) {
    888             super(property);
    889             mValueType = float.class;
    890             mKeyframeSet = keyframeSet;
    891             mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
    892             if (property instanceof FloatProperty) {
    893                 mFloatProperty = (FloatProperty) mProperty;
    894             }
    895         }
    896 
    897         public FloatPropertyValuesHolder(String propertyName, float... values) {
    898             super(propertyName);
    899             setFloatValues(values);
    900         }
    901 
    902         public FloatPropertyValuesHolder(Property property, float... values) {
    903             super(property);
    904             setFloatValues(values);
    905             if (property instanceof  FloatProperty) {
    906                 mFloatProperty = (FloatProperty) mProperty;
    907             }
    908         }
    909 
    910         @Override
    911         public void setFloatValues(float... values) {
    912             super.setFloatValues(values);
    913             mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
    914         }
    915 
    916         @Override
    917         void calculateValue(float fraction) {
    918             mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
    919         }
    920 
    921         @Override
    922         Object getAnimatedValue() {
    923             return mFloatAnimatedValue;
    924         }
    925 
    926         @Override
    927         public FloatPropertyValuesHolder clone() {
    928             FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
    929             newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet;
    930             return newPVH;
    931         }
    932 
    933         /**
    934          * Internal function to set the value on the target object, using the setter set up
    935          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
    936          * to handle turning the value calculated by ValueAnimator into a value set on the object
    937          * according to the name of the property.
    938          * @param target The target object on which the value is set
    939          */
    940         @Override
    941         void setAnimatedValue(Object target) {
    942             if (mFloatProperty != null) {
    943                 mFloatProperty.setValue(target, mFloatAnimatedValue);
    944                 return;
    945             }
    946             if (mProperty != null) {
    947                 mProperty.set(target, mFloatAnimatedValue);
    948                 return;
    949             }
    950             if (mJniSetter != 0) {
    951                 nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
    952                 return;
    953             }
    954             if (mSetter != null) {
    955                 try {
    956                     mTmpValueArray[0] = mFloatAnimatedValue;
    957                     mSetter.invoke(target, mTmpValueArray);
    958                 } catch (InvocationTargetException e) {
    959                     Log.e("PropertyValuesHolder", e.toString());
    960                 } catch (IllegalAccessException e) {
    961                     Log.e("PropertyValuesHolder", e.toString());
    962                 }
    963             }
    964         }
    965 
    966         @Override
    967         void setupSetter(Class targetClass) {
    968             if (mProperty != null) {
    969                 return;
    970             }
    971             // Check new static hashmap<propName, int> for setter method
    972             try {
    973                 mPropertyMapLock.writeLock().lock();
    974                 HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
    975                 if (propertyMap != null) {
    976                     Integer mJniSetterInteger = propertyMap.get(mPropertyName);
    977                     if (mJniSetterInteger != null) {
    978                         mJniSetter = mJniSetterInteger;
    979                     }
    980                 }
    981                 if (mJniSetter == 0) {
    982                     String methodName = getMethodName("set", mPropertyName);
    983                     mJniSetter = nGetFloatMethod(targetClass, methodName);
    984                     if (mJniSetter != 0) {
    985                         if (propertyMap == null) {
    986                             propertyMap = new HashMap<String, Integer>();
    987                             sJNISetterPropertyMap.put(targetClass, propertyMap);
    988                         }
    989                         propertyMap.put(mPropertyName, mJniSetter);
    990                     }
    991                 }
    992             } catch (NoSuchMethodError e) {
    993                 Log.d("PropertyValuesHolder",
    994                         "Can't find native method using JNI, use reflection" + e);
    995             } finally {
    996                 mPropertyMapLock.writeLock().unlock();
    997             }
    998             if (mJniSetter == 0) {
    999                 // Couldn't find method through fast JNI approach - just use reflection
   1000                 super.setupSetter(targetClass);
   1001             }
   1002         }
   1003 
   1004     }
   1005 
   1006     native static private int nGetIntMethod(Class targetClass, String methodName);
   1007     native static private int nGetFloatMethod(Class targetClass, String methodName);
   1008     native static private void nCallIntMethod(Object target, int methodID, int arg);
   1009     native static private void nCallFloatMethod(Object target, int methodID, float arg);
   1010 }