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                 // Swallow the error, log it later
    388             }
    389         } else {
    390             args = new Class[1];
    391             Class typeVariants[];
    392             if (mValueType.equals(Float.class)) {
    393                 typeVariants = FLOAT_VARIANTS;
    394             } else if (mValueType.equals(Integer.class)) {
    395                 typeVariants = INTEGER_VARIANTS;
    396             } else if (mValueType.equals(Double.class)) {
    397                 typeVariants = DOUBLE_VARIANTS;
    398             } else {
    399                 typeVariants = new Class[1];
    400                 typeVariants[0] = mValueType;
    401             }
    402             for (Class typeVariant : typeVariants) {
    403                 args[0] = typeVariant;
    404                 try {
    405                     returnVal = targetClass.getMethod(methodName, args);
    406                     // change the value type to suit
    407                     mValueType = typeVariant;
    408                     return returnVal;
    409                 } catch (NoSuchMethodException e) {
    410                     // Swallow the error and keep trying other variants
    411                 }
    412             }
    413             // If we got here, then no appropriate function was found
    414         }
    415 
    416         if (returnVal == null) {
    417             Log.w("PropertyValuesHolder", "Method " +
    418                     getMethodName(prefix, mPropertyName) + "() with type " + mValueType +
    419                     " not found on target class " + targetClass);
    420         }
    421 
    422         return returnVal;
    423     }
    424 
    425 
    426     /**
    427      * Returns the setter or getter requested. This utility function checks whether the
    428      * requested method exists in the propertyMapMap cache. If not, it calls another
    429      * utility function to request the Method from the targetClass directly.
    430      * @param targetClass The Class on which the requested method should exist.
    431      * @param propertyMapMap The cache of setters/getters derived so far.
    432      * @param prefix "set" or "get", for the setter or getter.
    433      * @param valueType The type of parameter passed into the method (null for getter).
    434      * @return Method the method associated with mPropertyName.
    435      */
    436     private Method setupSetterOrGetter(Class targetClass,
    437             HashMap<Class, HashMap<String, Method>> propertyMapMap,
    438             String prefix, Class valueType) {
    439         Method setterOrGetter = null;
    440         try {
    441             // Have to lock property map prior to reading it, to guard against
    442             // another thread putting something in there after we've checked it
    443             // but before we've added an entry to it
    444             mPropertyMapLock.writeLock().lock();
    445             HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
    446             if (propertyMap != null) {
    447                 setterOrGetter = propertyMap.get(mPropertyName);
    448             }
    449             if (setterOrGetter == null) {
    450                 setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
    451                 if (propertyMap == null) {
    452                     propertyMap = new HashMap<String, Method>();
    453                     propertyMapMap.put(targetClass, propertyMap);
    454                 }
    455                 propertyMap.put(mPropertyName, setterOrGetter);
    456             }
    457         } finally {
    458             mPropertyMapLock.writeLock().unlock();
    459         }
    460         return setterOrGetter;
    461     }
    462 
    463     /**
    464      * Utility function to get the setter from targetClass
    465      * @param targetClass The Class on which the requested method should exist.
    466      */
    467     void setupSetter(Class targetClass) {
    468         mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);
    469     }
    470 
    471     /**
    472      * Utility function to get the getter from targetClass
    473      */
    474     private void setupGetter(Class targetClass) {
    475         mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
    476     }
    477 
    478     /**
    479      * Internal function (called from ObjectAnimator) to set up the setter and getter
    480      * prior to running the animation. If the setter has not been manually set for this
    481      * object, it will be derived automatically given the property name, target object, and
    482      * types of values supplied. If no getter has been set, it will be supplied iff any of the
    483      * supplied values was null. If there is a null value, then the getter (supplied or derived)
    484      * will be called to set those null values to the current value of the property
    485      * on the target object.
    486      * @param target The object on which the setter (and possibly getter) exist.
    487      */
    488     void setupSetterAndGetter(Object target) {
    489         if (mProperty != null) {
    490             // check to make sure that mProperty is on the class of target
    491             try {
    492                 Object testValue = mProperty.get(target);
    493                 for (Keyframe kf : mKeyframeSet.mKeyframes) {
    494                     if (!kf.hasValue()) {
    495                         kf.setValue(mProperty.get(target));
    496                     }
    497                 }
    498                 return;
    499             } catch (ClassCastException e) {
    500                 Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
    501                         ") on target object " + target + ". Trying reflection instead");
    502                 mProperty = null;
    503             }
    504         }
    505         Class targetClass = target.getClass();
    506         if (mSetter == null) {
    507             setupSetter(targetClass);
    508         }
    509         for (Keyframe kf : mKeyframeSet.mKeyframes) {
    510             if (!kf.hasValue()) {
    511                 if (mGetter == null) {
    512                     setupGetter(targetClass);
    513                     if (mGetter == null) {
    514                         // Already logged the error - just return to avoid NPE
    515                         return;
    516                     }
    517                 }
    518                 try {
    519                     kf.setValue(mGetter.invoke(target));
    520                 } catch (InvocationTargetException e) {
    521                     Log.e("PropertyValuesHolder", e.toString());
    522                 } catch (IllegalAccessException e) {
    523                     Log.e("PropertyValuesHolder", e.toString());
    524                 }
    525             }
    526         }
    527     }
    528 
    529     /**
    530      * Utility function to set the value stored in a particular Keyframe. The value used is
    531      * whatever the value is for the property name specified in the keyframe on the target object.
    532      *
    533      * @param target The target object from which the current value should be extracted.
    534      * @param kf The keyframe which holds the property name and value.
    535      */
    536     private void setupValue(Object target, Keyframe kf) {
    537         if (mProperty != null) {
    538             kf.setValue(mProperty.get(target));
    539         }
    540         try {
    541             if (mGetter == null) {
    542                 Class targetClass = target.getClass();
    543                 setupGetter(targetClass);
    544                 if (mGetter == null) {
    545                     // Already logged the error - just return to avoid NPE
    546                     return;
    547                 }
    548             }
    549             kf.setValue(mGetter.invoke(target));
    550         } catch (InvocationTargetException e) {
    551             Log.e("PropertyValuesHolder", e.toString());
    552         } catch (IllegalAccessException e) {
    553             Log.e("PropertyValuesHolder", e.toString());
    554         }
    555     }
    556 
    557     /**
    558      * This function is called by ObjectAnimator when setting the start values for an animation.
    559      * The start values are set according to the current values in the target object. The
    560      * property whose value is extracted is whatever is specified by the propertyName of this
    561      * PropertyValuesHolder object.
    562      *
    563      * @param target The object which holds the start values that should be set.
    564      */
    565     void setupStartValue(Object target) {
    566         setupValue(target, mKeyframeSet.mKeyframes.get(0));
    567     }
    568 
    569     /**
    570      * This function is called by ObjectAnimator when setting the end values for an animation.
    571      * The end values are set according to the current values in the target object. The
    572      * property whose value is extracted is whatever is specified by the propertyName of this
    573      * PropertyValuesHolder object.
    574      *
    575      * @param target The object which holds the start values that should be set.
    576      */
    577     void setupEndValue(Object target) {
    578         setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
    579     }
    580 
    581     @Override
    582     public PropertyValuesHolder clone() {
    583         try {
    584             PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
    585             newPVH.mPropertyName = mPropertyName;
    586             newPVH.mProperty = mProperty;
    587             newPVH.mKeyframeSet = mKeyframeSet.clone();
    588             newPVH.mEvaluator = mEvaluator;
    589             return newPVH;
    590         } catch (CloneNotSupportedException e) {
    591             // won't reach here
    592             return null;
    593         }
    594     }
    595 
    596     /**
    597      * Internal function to set the value on the target object, using the setter set up
    598      * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
    599      * to handle turning the value calculated by ValueAnimator into a value set on the object
    600      * according to the name of the property.
    601      * @param target The target object on which the value is set
    602      */
    603     void setAnimatedValue(Object target) {
    604         if (mProperty != null) {
    605             mProperty.set(target, getAnimatedValue());
    606         }
    607         if (mSetter != null) {
    608             try {
    609                 mTmpValueArray[0] = getAnimatedValue();
    610                 mSetter.invoke(target, mTmpValueArray);
    611             } catch (InvocationTargetException e) {
    612                 Log.e("PropertyValuesHolder", e.toString());
    613             } catch (IllegalAccessException e) {
    614                 Log.e("PropertyValuesHolder", e.toString());
    615             }
    616         }
    617     }
    618 
    619     /**
    620      * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
    621      * to calculate animated values.
    622      */
    623     void init() {
    624         if (mEvaluator == null) {
    625             // We already handle int and float automatically, but not their Object
    626             // equivalents
    627             mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
    628                     (mValueType == Float.class) ? sFloatEvaluator :
    629                     null;
    630         }
    631         if (mEvaluator != null) {
    632             // KeyframeSet knows how to evaluate the common types - only give it a custom
    633             // evaluator if one has been set on this class
    634             mKeyframeSet.setEvaluator(mEvaluator);
    635         }
    636     }
    637 
    638     /**
    639      * The TypeEvaluator will the automatically determined based on the type of values
    640      * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
    641      * desired. This may be important in cases where either the type of the values supplied
    642      * do not match the way that they should be interpolated between, or if the values
    643      * are of a custom type or one not currently understood by the animation system. Currently,
    644      * only values of type float and int (and their Object equivalents: Float
    645      * and Integer) are  correctly interpolated; all other types require setting a TypeEvaluator.
    646      * @param evaluator
    647      */
    648     public void setEvaluator(TypeEvaluator evaluator) {
    649         mEvaluator = evaluator;
    650         mKeyframeSet.setEvaluator(evaluator);
    651     }
    652 
    653     /**
    654      * Function used to calculate the value according to the evaluator set up for
    655      * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
    656      *
    657      * @param fraction The elapsed, interpolated fraction of the animation.
    658      */
    659     void calculateValue(float fraction) {
    660         mAnimatedValue = mKeyframeSet.getValue(fraction);
    661     }
    662 
    663     /**
    664      * Sets the name of the property that will be animated. This name is used to derive
    665      * a setter function that will be called to set animated values.
    666      * For example, a property name of <code>foo</code> will result
    667      * in a call to the function <code>setFoo()</code> on the target object. If either
    668      * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
    669      * also be derived and called.
    670      *
    671      * <p>Note that the setter function derived from this property name
    672      * must take the same parameter type as the
    673      * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
    674      * the setter function will fail.</p>
    675      *
    676      * @param propertyName The name of the property being animated.
    677      */
    678     public void setPropertyName(String propertyName) {
    679         mPropertyName = propertyName;
    680     }
    681 
    682     /**
    683      * Sets the property that will be animated.
    684      *
    685      * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
    686      * must exist on the target object specified in that ObjectAnimator.</p>
    687      *
    688      * @param property The property being animated.
    689      */
    690     public void setProperty(Property property) {
    691         mProperty = property;
    692     }
    693 
    694     /**
    695      * Gets the name of the property that will be animated. This name will be used to derive
    696      * a setter function that will be called to set animated values.
    697      * For example, a property name of <code>foo</code> will result
    698      * in a call to the function <code>setFoo()</code> on the target object. If either
    699      * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
    700      * also be derived and called.
    701      */
    702     public String getPropertyName() {
    703         return mPropertyName;
    704     }
    705 
    706     /**
    707      * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
    708      * most recently calculated in calculateValue().
    709      * @return
    710      */
    711     Object getAnimatedValue() {
    712         return mAnimatedValue;
    713     }
    714 
    715     @Override
    716     public String toString() {
    717         return mPropertyName + ": " + mKeyframeSet.toString();
    718     }
    719 
    720     /**
    721      * Utility method to derive a setter/getter method name from a property name, where the
    722      * prefix is typically "set" or "get" and the first letter of the property name is
    723      * capitalized.
    724      *
    725      * @param prefix The precursor to the method name, before the property name begins, typically
    726      * "set" or "get".
    727      * @param propertyName The name of the property that represents the bulk of the method name
    728      * after the prefix. The first letter of this word will be capitalized in the resulting
    729      * method name.
    730      * @return String the property name converted to a method name according to the conventions
    731      * specified above.
    732      */
    733     static String getMethodName(String prefix, String propertyName) {
    734         if (propertyName == null || propertyName.length() == 0) {
    735             // shouldn't get here
    736             return prefix;
    737         }
    738         char firstLetter = Character.toUpperCase(propertyName.charAt(0));
    739         String theRest = propertyName.substring(1);
    740         return prefix + firstLetter + theRest;
    741     }
    742 
    743     static class IntPropertyValuesHolder extends PropertyValuesHolder {
    744 
    745         // Cache JNI functions to avoid looking them up twice
    746         private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
    747                 new HashMap<Class, HashMap<String, Integer>>();
    748         int mJniSetter;
    749         private IntProperty mIntProperty;
    750 
    751         IntKeyframeSet mIntKeyframeSet;
    752         int mIntAnimatedValue;
    753 
    754         public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) {
    755             super(propertyName);
    756             mValueType = int.class;
    757             mKeyframeSet = keyframeSet;
    758             mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
    759         }
    760 
    761         public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) {
    762             super(property);
    763             mValueType = int.class;
    764             mKeyframeSet = keyframeSet;
    765             mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
    766             if (property instanceof  IntProperty) {
    767                 mIntProperty = (IntProperty) mProperty;
    768             }
    769         }
    770 
    771         public IntPropertyValuesHolder(String propertyName, int... values) {
    772             super(propertyName);
    773             setIntValues(values);
    774         }
    775 
    776         public IntPropertyValuesHolder(Property property, int... values) {
    777             super(property);
    778             setIntValues(values);
    779             if (property instanceof  IntProperty) {
    780                 mIntProperty = (IntProperty) mProperty;
    781             }
    782         }
    783 
    784         @Override
    785         public void setIntValues(int... values) {
    786             super.setIntValues(values);
    787             mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
    788         }
    789 
    790         @Override
    791         void calculateValue(float fraction) {
    792             mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction);
    793         }
    794 
    795         @Override
    796         Object getAnimatedValue() {
    797             return mIntAnimatedValue;
    798         }
    799 
    800         @Override
    801         public IntPropertyValuesHolder clone() {
    802             IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
    803             newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet;
    804             return newPVH;
    805         }
    806 
    807         /**
    808          * Internal function to set the value on the target object, using the setter set up
    809          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
    810          * to handle turning the value calculated by ValueAnimator into a value set on the object
    811          * according to the name of the property.
    812          * @param target The target object on which the value is set
    813          */
    814         @Override
    815         void setAnimatedValue(Object target) {
    816             if (mIntProperty != null) {
    817                 mIntProperty.setValue(target, mIntAnimatedValue);
    818                 return;
    819             }
    820             if (mProperty != null) {
    821                 mProperty.set(target, mIntAnimatedValue);
    822                 return;
    823             }
    824             if (mJniSetter != 0) {
    825                 nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
    826                 return;
    827             }
    828             if (mSetter != null) {
    829                 try {
    830                     mTmpValueArray[0] = mIntAnimatedValue;
    831                     mSetter.invoke(target, mTmpValueArray);
    832                 } catch (InvocationTargetException e) {
    833                     Log.e("PropertyValuesHolder", e.toString());
    834                 } catch (IllegalAccessException e) {
    835                     Log.e("PropertyValuesHolder", e.toString());
    836                 }
    837             }
    838         }
    839 
    840         @Override
    841         void setupSetter(Class targetClass) {
    842             if (mProperty != null) {
    843                 return;
    844             }
    845             // Check new static hashmap<propName, int> for setter method
    846             try {
    847                 mPropertyMapLock.writeLock().lock();
    848                 HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
    849                 if (propertyMap != null) {
    850                     Integer mJniSetterInteger = propertyMap.get(mPropertyName);
    851                     if (mJniSetterInteger != null) {
    852                         mJniSetter = mJniSetterInteger;
    853                     }
    854                 }
    855                 if (mJniSetter == 0) {
    856                     String methodName = getMethodName("set", mPropertyName);
    857                     mJniSetter = nGetIntMethod(targetClass, methodName);
    858                     if (mJniSetter != 0) {
    859                         if (propertyMap == null) {
    860                             propertyMap = new HashMap<String, Integer>();
    861                             sJNISetterPropertyMap.put(targetClass, propertyMap);
    862                         }
    863                         propertyMap.put(mPropertyName, mJniSetter);
    864                     }
    865                 }
    866             } catch (NoSuchMethodError e) {
    867                 // Couldn't find it via JNI - try reflection next. Probably means the method
    868                 // doesn't exist, or the type is wrong. An error will be logged later if
    869                 // reflection fails as well.
    870             } finally {
    871                 mPropertyMapLock.writeLock().unlock();
    872             }
    873             if (mJniSetter == 0) {
    874                 // Couldn't find method through fast JNI approach - just use reflection
    875                 super.setupSetter(targetClass);
    876             }
    877         }
    878     }
    879 
    880     static class FloatPropertyValuesHolder extends PropertyValuesHolder {
    881 
    882         // Cache JNI functions to avoid looking them up twice
    883         private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
    884                 new HashMap<Class, HashMap<String, Integer>>();
    885         int mJniSetter;
    886         private FloatProperty mFloatProperty;
    887 
    888         FloatKeyframeSet mFloatKeyframeSet;
    889         float mFloatAnimatedValue;
    890 
    891         public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
    892             super(propertyName);
    893             mValueType = float.class;
    894             mKeyframeSet = keyframeSet;
    895             mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
    896         }
    897 
    898         public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) {
    899             super(property);
    900             mValueType = float.class;
    901             mKeyframeSet = keyframeSet;
    902             mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
    903             if (property instanceof FloatProperty) {
    904                 mFloatProperty = (FloatProperty) mProperty;
    905             }
    906         }
    907 
    908         public FloatPropertyValuesHolder(String propertyName, float... values) {
    909             super(propertyName);
    910             setFloatValues(values);
    911         }
    912 
    913         public FloatPropertyValuesHolder(Property property, float... values) {
    914             super(property);
    915             setFloatValues(values);
    916             if (property instanceof  FloatProperty) {
    917                 mFloatProperty = (FloatProperty) mProperty;
    918             }
    919         }
    920 
    921         @Override
    922         public void setFloatValues(float... values) {
    923             super.setFloatValues(values);
    924             mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
    925         }
    926 
    927         @Override
    928         void calculateValue(float fraction) {
    929             mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
    930         }
    931 
    932         @Override
    933         Object getAnimatedValue() {
    934             return mFloatAnimatedValue;
    935         }
    936 
    937         @Override
    938         public FloatPropertyValuesHolder clone() {
    939             FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
    940             newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet;
    941             return newPVH;
    942         }
    943 
    944         /**
    945          * Internal function to set the value on the target object, using the setter set up
    946          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
    947          * to handle turning the value calculated by ValueAnimator into a value set on the object
    948          * according to the name of the property.
    949          * @param target The target object on which the value is set
    950          */
    951         @Override
    952         void setAnimatedValue(Object target) {
    953             if (mFloatProperty != null) {
    954                 mFloatProperty.setValue(target, mFloatAnimatedValue);
    955                 return;
    956             }
    957             if (mProperty != null) {
    958                 mProperty.set(target, mFloatAnimatedValue);
    959                 return;
    960             }
    961             if (mJniSetter != 0) {
    962                 nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
    963                 return;
    964             }
    965             if (mSetter != null) {
    966                 try {
    967                     mTmpValueArray[0] = mFloatAnimatedValue;
    968                     mSetter.invoke(target, mTmpValueArray);
    969                 } catch (InvocationTargetException e) {
    970                     Log.e("PropertyValuesHolder", e.toString());
    971                 } catch (IllegalAccessException e) {
    972                     Log.e("PropertyValuesHolder", e.toString());
    973                 }
    974             }
    975         }
    976 
    977         @Override
    978         void setupSetter(Class targetClass) {
    979             if (mProperty != null) {
    980                 return;
    981             }
    982             // Check new static hashmap<propName, int> for setter method
    983             try {
    984                 mPropertyMapLock.writeLock().lock();
    985                 HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
    986                 if (propertyMap != null) {
    987                     Integer mJniSetterInteger = propertyMap.get(mPropertyName);
    988                     if (mJniSetterInteger != null) {
    989                         mJniSetter = mJniSetterInteger;
    990                     }
    991                 }
    992                 if (mJniSetter == 0) {
    993                     String methodName = getMethodName("set", mPropertyName);
    994                     mJniSetter = nGetFloatMethod(targetClass, methodName);
    995                     if (mJniSetter != 0) {
    996                         if (propertyMap == null) {
    997                             propertyMap = new HashMap<String, Integer>();
    998                             sJNISetterPropertyMap.put(targetClass, propertyMap);
    999                         }
   1000                         propertyMap.put(mPropertyName, mJniSetter);
   1001                     }
   1002                 }
   1003             } catch (NoSuchMethodError e) {
   1004                 // Couldn't find it via JNI - try reflection next. Probably means the method
   1005                 // doesn't exist, or the type is wrong. An error will be logged later if
   1006                 // reflection fails as well.
   1007             } finally {
   1008                 mPropertyMapLock.writeLock().unlock();
   1009             }
   1010             if (mJniSetter == 0) {
   1011                 // Couldn't find method through fast JNI approach - just use reflection
   1012                 super.setupSetter(targetClass);
   1013             }
   1014         }
   1015 
   1016     }
   1017 
   1018     native static private int nGetIntMethod(Class targetClass, String methodName);
   1019     native static private int nGetFloatMethod(Class targetClass, String methodName);
   1020     native static private void nCallIntMethod(Object target, int methodID, int arg);
   1021     native static private void nCallFloatMethod(Object target, int methodID, float arg);
   1022 }