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.graphics.Path;
     20 import android.graphics.PointF;
     21 import android.util.FloatProperty;
     22 import android.util.IntProperty;
     23 import android.util.Log;
     24 import android.util.Property;
     25 
     26 import java.lang.reflect.InvocationTargetException;
     27 import java.lang.reflect.Method;
     28 import java.util.ArrayList;
     29 import java.util.HashMap;
     30 import java.util.List;
     31 import java.util.concurrent.locks.ReentrantReadWriteLock;
     32 
     33 /**
     34  * This class holds information about a property and the values that that property
     35  * should take on during an animation. PropertyValuesHolder objects can be used to create
     36  * animations with ValueAnimator or ObjectAnimator that operate on several different properties
     37  * in parallel.
     38  */
     39 public class PropertyValuesHolder implements Cloneable {
     40 
     41     /**
     42      * The name of the property associated with the values. This need not be a real property,
     43      * unless this object is being used with ObjectAnimator. But this is the name by which
     44      * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator.
     45      */
     46     String mPropertyName;
     47 
     48     /**
     49      * @hide
     50      */
     51     protected Property mProperty;
     52 
     53     /**
     54      * The setter function, if needed. ObjectAnimator hands off this functionality to
     55      * PropertyValuesHolder, since it holds all of the per-property information. This
     56      * property is automatically
     57      * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
     58      */
     59     Method mSetter = null;
     60 
     61     /**
     62      * The getter function, if needed. ObjectAnimator hands off this functionality to
     63      * PropertyValuesHolder, since it holds all of the per-property information. This
     64      * property is automatically
     65      * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
     66      * The getter is only derived and used if one of the values is null.
     67      */
     68     private Method mGetter = null;
     69 
     70     /**
     71      * The type of values supplied. This information is used both in deriving the setter/getter
     72      * functions and in deriving the type of TypeEvaluator.
     73      */
     74     Class mValueType;
     75 
     76     /**
     77      * The set of keyframes (time/value pairs) that define this animation.
     78      */
     79     Keyframes mKeyframes = null;
     80 
     81 
     82     // type evaluators for the primitive types handled by this implementation
     83     private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
     84     private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
     85 
     86     // We try several different types when searching for appropriate setter/getter functions.
     87     // The caller may have supplied values in a type that does not match the setter/getter
     88     // functions (such as the integers 0 and 1 to represent floating point values for alpha).
     89     // Also, the use of generics in constructors means that we end up with the Object versions
     90     // of primitive types (Float vs. float). But most likely, the setter/getter functions
     91     // will take primitive types instead.
     92     // So we supply an ordered array of other types to try before giving up.
     93     private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
     94             Double.class, Integer.class};
     95     private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
     96             Float.class, Double.class};
     97     private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class,
     98             Float.class, Integer.class};
     99 
    100     // These maps hold all property entries for a particular class. This map
    101     // is used to speed up property/setter/getter lookups for a given class/property
    102     // combination. No need to use reflection on the combination more than once.
    103     private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap =
    104             new HashMap<Class, HashMap<String, Method>>();
    105     private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap =
    106             new HashMap<Class, HashMap<String, Method>>();
    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      * Converts from the source Object type to the setter Object type.
    128      */
    129     private TypeConverter mConverter;
    130 
    131     /**
    132      * Internal utility constructor, used by the factory methods to set the property name.
    133      * @param propertyName The name of the property for this holder.
    134      */
    135     private PropertyValuesHolder(String propertyName) {
    136         mPropertyName = propertyName;
    137     }
    138 
    139     /**
    140      * Internal utility constructor, used by the factory methods to set the property.
    141      * @param property The property for this holder.
    142      */
    143     private PropertyValuesHolder(Property property) {
    144         mProperty = property;
    145         if (property != null) {
    146             mPropertyName = property.getName();
    147         }
    148     }
    149 
    150     /**
    151      * Constructs and returns a PropertyValuesHolder with a given property name and
    152      * set of int values.
    153      * @param propertyName The name of the property being animated.
    154      * @param values The values that the named property will animate between.
    155      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    156      */
    157     public static PropertyValuesHolder ofInt(String propertyName, int... values) {
    158         return new IntPropertyValuesHolder(propertyName, values);
    159     }
    160 
    161     /**
    162      * Constructs and returns a PropertyValuesHolder with a given property and
    163      * set of int values.
    164      * @param property The property being animated. Should not be null.
    165      * @param values The values that the property will animate between.
    166      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    167      */
    168     public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
    169         return new IntPropertyValuesHolder(property, values);
    170     }
    171 
    172     /**
    173      * Constructs and returns a PropertyValuesHolder with a given property name and
    174      * set of <code>int[]</code> values. At least two <code>int[]</code> values must be supplied,
    175      * a start and end value. If more values are supplied, the values will be animated from the
    176      * start, through all intermediate values to the end value. When used with ObjectAnimator,
    177      * the elements of the array represent the parameters of the setter function.
    178      *
    179      * @param propertyName The name of the property being animated. Can also be the
    180      *                     case-sensitive name of the entire setter method. Should not be null.
    181      * @param values The values that the property will animate between.
    182      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    183      * @see IntArrayEvaluator#IntArrayEvaluator(int[])
    184      * @see ObjectAnimator#ofMultiInt(Object, String, TypeConverter, TypeEvaluator, Object[])
    185      */
    186     public static PropertyValuesHolder ofMultiInt(String propertyName, int[][] values) {
    187         if (values.length < 2) {
    188             throw new IllegalArgumentException("At least 2 values must be supplied");
    189         }
    190         int numParameters = 0;
    191         for (int i = 0; i < values.length; i++) {
    192             if (values[i] == null) {
    193                 throw new IllegalArgumentException("values must not be null");
    194             }
    195             int length = values[i].length;
    196             if (i == 0) {
    197                 numParameters = length;
    198             } else if (length != numParameters) {
    199                 throw new IllegalArgumentException("Values must all have the same length");
    200             }
    201         }
    202         IntArrayEvaluator evaluator = new IntArrayEvaluator(new int[numParameters]);
    203         return new MultiIntValuesHolder(propertyName, null, evaluator, (Object[]) values);
    204     }
    205 
    206     /**
    207      * Constructs and returns a PropertyValuesHolder with a given property name to use
    208      * as a multi-int setter. The values are animated along the path, with the first
    209      * parameter of the setter set to the x coordinate and the second set to the y coordinate.
    210      *
    211      * @param propertyName The name of the property being animated. Can also be the
    212      *                     case-sensitive name of the entire setter method. Should not be null.
    213      *                     The setter must take exactly two <code>int</code> parameters.
    214      * @param path The Path along which the values should be animated.
    215      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    216      * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...)
    217      */
    218     public static PropertyValuesHolder ofMultiInt(String propertyName, Path path) {
    219         Keyframes keyframes = KeyframeSet.ofPath(path);
    220         PointFToIntArray converter = new PointFToIntArray();
    221         return new MultiIntValuesHolder(propertyName, converter, null, keyframes);
    222     }
    223 
    224     /**
    225      * Constructs and returns a PropertyValuesHolder with a given property and
    226      * set of Object values for use with ObjectAnimator multi-value setters. The Object
    227      * values are converted to <code>int[]</code> using the converter.
    228      *
    229      * @param propertyName The property being animated or complete name of the setter.
    230      *                     Should not be null.
    231      * @param converter Used to convert the animated value to setter parameters.
    232      * @param evaluator A TypeEvaluator that will be called on each animation frame to
    233      * provide the necessary interpolation between the Object values to derive the animated
    234      * value.
    235      * @param values The values that the property will animate between.
    236      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    237      * @see ObjectAnimator#ofMultiInt(Object, String, TypeConverter, TypeEvaluator, Object[])
    238      * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...)
    239      */
    240     public static <V> PropertyValuesHolder ofMultiInt(String propertyName,
    241             TypeConverter<V, int[]> converter, TypeEvaluator<V> evaluator, V... values) {
    242         return new MultiIntValuesHolder(propertyName, converter, evaluator, values);
    243     }
    244 
    245     /**
    246      * Constructs and returns a PropertyValuesHolder object with the specified property name or
    247      * setter name for use in a multi-int setter function using ObjectAnimator. The values can be
    248      * of any type, but the type should be consistent so that the supplied
    249      * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The
    250      * <code>converter</code> converts the values to parameters in the setter function.
    251      *
    252      * <p>At least two values must be supplied, a start and an end value.</p>
    253      *
    254      * @param propertyName The name of the property to associate with the set of values. This
    255      *                     may also be the complete name of a setter function.
    256      * @param converter    Converts <code>values</code> into int parameters for the setter.
    257      *                     Can be null if the Keyframes have int[] values.
    258      * @param evaluator    Used to interpolate between values.
    259      * @param values       The values at specific fractional times to evaluate between
    260      * @return A PropertyValuesHolder for a multi-int parameter setter.
    261      */
    262     public static <T> PropertyValuesHolder ofMultiInt(String propertyName,
    263             TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, Keyframe... values) {
    264         KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
    265         return new MultiIntValuesHolder(propertyName, converter, evaluator, keyframeSet);
    266     }
    267 
    268     /**
    269      * Constructs and returns a PropertyValuesHolder with a given property name and
    270      * set of float values.
    271      * @param propertyName The name of the property being animated.
    272      * @param values The values that the named property will animate between.
    273      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    274      */
    275     public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
    276         return new FloatPropertyValuesHolder(propertyName, values);
    277     }
    278 
    279     /**
    280      * Constructs and returns a PropertyValuesHolder with a given property and
    281      * set of float values.
    282      * @param property The property being animated. Should not be null.
    283      * @param values The values that the property will animate between.
    284      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    285      */
    286     public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
    287         return new FloatPropertyValuesHolder(property, values);
    288     }
    289 
    290     /**
    291      * Constructs and returns a PropertyValuesHolder with a given property name and
    292      * set of <code>float[]</code> values. At least two <code>float[]</code> values must be supplied,
    293      * a start and end value. If more values are supplied, the values will be animated from the
    294      * start, through all intermediate values to the end value. When used with ObjectAnimator,
    295      * the elements of the array represent the parameters of the setter function.
    296      *
    297      * @param propertyName The name of the property being animated. Can also be the
    298      *                     case-sensitive name of the entire setter method. Should not be null.
    299      * @param values The values that the property will animate between.
    300      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    301      * @see FloatArrayEvaluator#FloatArrayEvaluator(float[])
    302      * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[])
    303      */
    304     public static PropertyValuesHolder ofMultiFloat(String propertyName, float[][] values) {
    305         if (values.length < 2) {
    306             throw new IllegalArgumentException("At least 2 values must be supplied");
    307         }
    308         int numParameters = 0;
    309         for (int i = 0; i < values.length; i++) {
    310             if (values[i] == null) {
    311                 throw new IllegalArgumentException("values must not be null");
    312             }
    313             int length = values[i].length;
    314             if (i == 0) {
    315                 numParameters = length;
    316             } else if (length != numParameters) {
    317                 throw new IllegalArgumentException("Values must all have the same length");
    318             }
    319         }
    320         FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[numParameters]);
    321         return new MultiFloatValuesHolder(propertyName, null, evaluator, (Object[]) values);
    322     }
    323 
    324     /**
    325      * Constructs and returns a PropertyValuesHolder with a given property name to use
    326      * as a multi-float setter. The values are animated along the path, with the first
    327      * parameter of the setter set to the x coordinate and the second set to the y coordinate.
    328      *
    329      * @param propertyName The name of the property being animated. Can also be the
    330      *                     case-sensitive name of the entire setter method. Should not be null.
    331      *                     The setter must take exactly two <code>float</code> parameters.
    332      * @param path The Path along which the values should be animated.
    333      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    334      * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...)
    335      */
    336     public static PropertyValuesHolder ofMultiFloat(String propertyName, Path path) {
    337         Keyframes keyframes = KeyframeSet.ofPath(path);
    338         PointFToFloatArray converter = new PointFToFloatArray();
    339         return new MultiFloatValuesHolder(propertyName, converter, null, keyframes);
    340     }
    341 
    342     /**
    343      * Constructs and returns a PropertyValuesHolder with a given property and
    344      * set of Object values for use with ObjectAnimator multi-value setters. The Object
    345      * values are converted to <code>float[]</code> using the converter.
    346      *
    347      * @param propertyName The property being animated or complete name of the setter.
    348      *                     Should not be null.
    349      * @param converter Used to convert the animated value to setter parameters.
    350      * @param evaluator A TypeEvaluator that will be called on each animation frame to
    351      * provide the necessary interpolation between the Object values to derive the animated
    352      * value.
    353      * @param values The values that the property will animate between.
    354      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    355      * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[])
    356      */
    357     public static <V> PropertyValuesHolder ofMultiFloat(String propertyName,
    358             TypeConverter<V, float[]> converter, TypeEvaluator<V> evaluator, V... values) {
    359         return new MultiFloatValuesHolder(propertyName, converter, evaluator, values);
    360     }
    361 
    362     /**
    363      * Constructs and returns a PropertyValuesHolder object with the specified property name or
    364      * setter name for use in a multi-float setter function using ObjectAnimator. The values can be
    365      * of any type, but the type should be consistent so that the supplied
    366      * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The
    367      * <code>converter</code> converts the values to parameters in the setter function.
    368      *
    369      * <p>At least two values must be supplied, a start and an end value.</p>
    370      *
    371      * @param propertyName The name of the property to associate with the set of values. This
    372      *                     may also be the complete name of a setter function.
    373      * @param converter    Converts <code>values</code> into float parameters for the setter.
    374      *                     Can be null if the Keyframes have float[] values.
    375      * @param evaluator    Used to interpolate between values.
    376      * @param values       The values at specific fractional times to evaluate between
    377      * @return A PropertyValuesHolder for a multi-float parameter setter.
    378      */
    379     public static <T> PropertyValuesHolder ofMultiFloat(String propertyName,
    380             TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, Keyframe... values) {
    381         KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
    382         return new MultiFloatValuesHolder(propertyName, converter, evaluator, keyframeSet);
    383     }
    384 
    385     /**
    386      * Constructs and returns a PropertyValuesHolder with a given property name and
    387      * set of Object values. This variant also takes a TypeEvaluator because the system
    388      * cannot automatically interpolate between objects of unknown type.
    389      *
    390      * @param propertyName The name of the property being animated.
    391      * @param evaluator A TypeEvaluator that will be called on each animation frame to
    392      * provide the necessary interpolation between the Object values to derive the animated
    393      * value.
    394      * @param values The values that the named property will animate between.
    395      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    396      */
    397     public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
    398             Object... values) {
    399         PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
    400         pvh.setObjectValues(values);
    401         pvh.setEvaluator(evaluator);
    402         return pvh;
    403     }
    404 
    405     /**
    406      * Constructs and returns a PropertyValuesHolder with a given property name and
    407      * a Path along which the values should be animated. This variant supports a
    408      * <code>TypeConverter</code> to convert from <code>PointF</code> to the target
    409      * type.
    410      *
    411      * <p>The PointF passed to <code>converter</code> or <code>property</code>, if
    412      * <code>converter</code> is <code>null</code>, is reused on each animation frame and should
    413      * not be stored by the setter or TypeConverter.</p>
    414      *
    415      * @param propertyName The name of the property being animated.
    416      * @param converter Converts a PointF to the type associated with the setter. May be
    417      *                  null if conversion is unnecessary.
    418      * @param path The Path along which the values should be animated.
    419      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    420      */
    421     public static PropertyValuesHolder ofObject(String propertyName,
    422             TypeConverter<PointF, ?> converter, Path path) {
    423         PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
    424         pvh.mKeyframes = KeyframeSet.ofPath(path);
    425         pvh.mValueType = PointF.class;
    426         pvh.setConverter(converter);
    427         return pvh;
    428     }
    429 
    430     /**
    431      * Constructs and returns a PropertyValuesHolder with a given property and
    432      * set of Object values. This variant also takes a TypeEvaluator because the system
    433      * cannot automatically interpolate between objects of unknown type.
    434      *
    435      * @param property The property being animated. Should not be null.
    436      * @param evaluator A TypeEvaluator that will be called on each animation frame to
    437      * provide the necessary interpolation between the Object values to derive the animated
    438      * value.
    439      * @param values The values that the property will animate between.
    440      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    441      */
    442     public static <V> PropertyValuesHolder ofObject(Property property,
    443             TypeEvaluator<V> evaluator, V... values) {
    444         PropertyValuesHolder pvh = new PropertyValuesHolder(property);
    445         pvh.setObjectValues(values);
    446         pvh.setEvaluator(evaluator);
    447         return pvh;
    448     }
    449 
    450     /**
    451      * Constructs and returns a PropertyValuesHolder with a given property and
    452      * set of Object values. This variant also takes a TypeEvaluator because the system
    453      * cannot automatically interpolate between objects of unknown type. This variant also
    454      * takes a <code>TypeConverter</code> to convert from animated values to the type
    455      * of the property. If only one value is supplied, the <code>TypeConverter</code>
    456      * must be a {@link android.animation.BidirectionalTypeConverter} to retrieve the current
    457      * value.
    458      *
    459      * @param property The property being animated. Should not be null.
    460      * @param converter Converts the animated object to the Property type.
    461      * @param evaluator A TypeEvaluator that will be called on each animation frame to
    462      * provide the necessary interpolation between the Object values to derive the animated
    463      * value.
    464      * @param values The values that the property will animate between.
    465      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    466      * @see #setConverter(TypeConverter)
    467      * @see TypeConverter
    468      */
    469     public static <T, V> PropertyValuesHolder ofObject(Property<?, V> property,
    470             TypeConverter<T, V> converter, TypeEvaluator<T> evaluator, T... values) {
    471         PropertyValuesHolder pvh = new PropertyValuesHolder(property);
    472         pvh.setConverter(converter);
    473         pvh.setObjectValues(values);
    474         pvh.setEvaluator(evaluator);
    475         return pvh;
    476     }
    477 
    478     /**
    479      * Constructs and returns a PropertyValuesHolder with a given property and
    480      * a Path along which the values should be animated. This variant supports a
    481      * <code>TypeConverter</code> to convert from <code>PointF</code> to the target
    482      * type.
    483      *
    484      * <p>The PointF passed to <code>converter</code> or <code>property</code>, if
    485      * <code>converter</code> is <code>null</code>, is reused on each animation frame and should
    486      * not be stored by the setter or TypeConverter.</p>
    487      *
    488      * @param property The property being animated. Should not be null.
    489      * @param converter Converts a PointF to the type associated with the setter. May be
    490      *                  null if conversion is unnecessary.
    491      * @param path The Path along which the values should be animated.
    492      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
    493      */
    494     public static <V> PropertyValuesHolder ofObject(Property<?, V> property,
    495             TypeConverter<PointF, V> converter, Path path) {
    496         PropertyValuesHolder pvh = new PropertyValuesHolder(property);
    497         pvh.mKeyframes = KeyframeSet.ofPath(path);
    498         pvh.mValueType = PointF.class;
    499         pvh.setConverter(converter);
    500         return pvh;
    501     }
    502 
    503     /**
    504      * Constructs and returns a PropertyValuesHolder object with the specified property name and set
    505      * of values. These values can be of any type, but the type should be consistent so that
    506      * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
    507      * the common type.
    508      * <p>If there is only one value, it is assumed to be the end value of an animation,
    509      * and an initial value will be derived, if possible, by calling a getter function
    510      * on the object. Also, if any value is null, the value will be filled in when the animation
    511      * starts in the same way. This mechanism of automatically getting null values only works
    512      * if the PropertyValuesHolder object is used in conjunction
    513      * {@link ObjectAnimator}, and with a getter function
    514      * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
    515      * no way of determining what the value should be.
    516      * @param propertyName The name of the property associated with this set of values. This
    517      * can be the actual property name to be used when using a ObjectAnimator object, or
    518      * just a name used to get animated values, such as if this object is used with an
    519      * ValueAnimator object.
    520      * @param values The set of values to animate between.
    521      */
    522     public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
    523         KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
    524         return ofKeyframes(propertyName, keyframeSet);
    525     }
    526 
    527     /**
    528      * Constructs and returns a PropertyValuesHolder object with the specified property and set
    529      * of values. These values can be of any type, but the type should be consistent so that
    530      * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
    531      * the common type.
    532      * <p>If there is only one value, it is assumed to be the end value of an animation,
    533      * and an initial value will be derived, if possible, by calling the property's
    534      * {@link android.util.Property#get(Object)} function.
    535      * Also, if any value is null, the value will be filled in when the animation
    536      * starts in the same way. This mechanism of automatically getting null values only works
    537      * if the PropertyValuesHolder object is used in conjunction with
    538      * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has
    539      * no way of determining what the value should be.
    540      * @param property The property associated with this set of values. Should not be null.
    541      * @param values The set of values to animate between.
    542      */
    543     public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
    544         KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
    545         return ofKeyframes(property, keyframeSet);
    546     }
    547 
    548     static PropertyValuesHolder ofKeyframes(String propertyName, Keyframes keyframes) {
    549         if (keyframes instanceof Keyframes.IntKeyframes) {
    550             return new IntPropertyValuesHolder(propertyName, (Keyframes.IntKeyframes) keyframes);
    551         } else if (keyframes instanceof Keyframes.FloatKeyframes) {
    552             return new FloatPropertyValuesHolder(propertyName,
    553                     (Keyframes.FloatKeyframes) keyframes);
    554         } else {
    555             PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
    556             pvh.mKeyframes = keyframes;
    557             pvh.mValueType = keyframes.getType();
    558             return pvh;
    559         }
    560     }
    561 
    562     static PropertyValuesHolder ofKeyframes(Property property, Keyframes keyframes) {
    563         if (keyframes instanceof Keyframes.IntKeyframes) {
    564             return new IntPropertyValuesHolder(property, (Keyframes.IntKeyframes) keyframes);
    565         } else if (keyframes instanceof Keyframes.FloatKeyframes) {
    566             return new FloatPropertyValuesHolder(property, (Keyframes.FloatKeyframes) keyframes);
    567         } else {
    568             PropertyValuesHolder pvh = new PropertyValuesHolder(property);
    569             pvh.mKeyframes = keyframes;
    570             pvh.mValueType = keyframes.getType();
    571             return pvh;
    572         }
    573     }
    574 
    575     /**
    576      * Set the animated values for this object to this set of ints.
    577      * If there is only one value, it is assumed to be the end value of an animation,
    578      * and an initial value will be derived, if possible, by calling a getter function
    579      * on the object. Also, if any value is null, the value will be filled in when the animation
    580      * starts in the same way. This mechanism of automatically getting null values only works
    581      * if the PropertyValuesHolder object is used in conjunction
    582      * {@link ObjectAnimator}, and with a getter function
    583      * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
    584      * no way of determining what the value should be.
    585      *
    586      * @param values One or more values that the animation will animate between.
    587      */
    588     public void setIntValues(int... values) {
    589         mValueType = int.class;
    590         mKeyframes = KeyframeSet.ofInt(values);
    591     }
    592 
    593     /**
    594      * Set the animated values for this object to this set of floats.
    595      * If there is only one value, it is assumed to be the end value of an animation,
    596      * and an initial value will be derived, if possible, by calling a getter function
    597      * on the object. Also, if any value is null, the value will be filled in when the animation
    598      * starts in the same way. This mechanism of automatically getting null values only works
    599      * if the PropertyValuesHolder object is used in conjunction
    600      * {@link ObjectAnimator}, and with a getter function
    601      * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
    602      * no way of determining what the value should be.
    603      *
    604      * @param values One or more values that the animation will animate between.
    605      */
    606     public void setFloatValues(float... values) {
    607         mValueType = float.class;
    608         mKeyframes = KeyframeSet.ofFloat(values);
    609     }
    610 
    611     /**
    612      * Set the animated values for this object to this set of Keyframes.
    613      *
    614      * @param values One or more values that the animation will animate between.
    615      */
    616     public void setKeyframes(Keyframe... values) {
    617         int numKeyframes = values.length;
    618         Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
    619         mValueType = ((Keyframe)values[0]).getType();
    620         for (int i = 0; i < numKeyframes; ++i) {
    621             keyframes[i] = (Keyframe)values[i];
    622         }
    623         mKeyframes = new KeyframeSet(keyframes);
    624     }
    625 
    626     /**
    627      * Set the animated values for this object to this set of Objects.
    628      * If there is only one value, it is assumed to be the end value of an animation,
    629      * and an initial value will be derived, if possible, by calling a getter function
    630      * on the object. Also, if any value is null, the value will be filled in when the animation
    631      * starts in the same way. This mechanism of automatically getting null values only works
    632      * if the PropertyValuesHolder object is used in conjunction
    633      * {@link ObjectAnimator}, and with a getter function
    634      * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
    635      * no way of determining what the value should be.
    636      *
    637      * @param values One or more values that the animation will animate between.
    638      */
    639     public void setObjectValues(Object... values) {
    640         mValueType = values[0].getClass();
    641         mKeyframes = KeyframeSet.ofObject(values);
    642         if (mEvaluator != null) {
    643             mKeyframes.setEvaluator(mEvaluator);
    644         }
    645     }
    646 
    647     /**
    648      * Sets the converter to convert from the values type to the setter's parameter type.
    649      * If only one value is supplied, <var>converter</var> must be a
    650      * {@link android.animation.BidirectionalTypeConverter}.
    651      * @param converter The converter to use to convert values.
    652      */
    653     public void setConverter(TypeConverter converter) {
    654         mConverter = converter;
    655     }
    656 
    657     /**
    658      * Determine the setter or getter function using the JavaBeans convention of setFoo or
    659      * getFoo for a property named 'foo'. This function figures out what the name of the
    660      * function should be and uses reflection to find the Method with that name on the
    661      * target object.
    662      *
    663      * @param targetClass The class to search for the method
    664      * @param prefix "set" or "get", depending on whether we need a setter or getter.
    665      * @param valueType The type of the parameter (in the case of a setter). This type
    666      * is derived from the values set on this PropertyValuesHolder. This type is used as
    667      * a first guess at the parameter type, but we check for methods with several different
    668      * types to avoid problems with slight mis-matches between supplied values and actual
    669      * value types used on the setter.
    670      * @return Method the method associated with mPropertyName.
    671      */
    672     private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
    673         // TODO: faster implementation...
    674         Method returnVal = null;
    675         String methodName = getMethodName(prefix, mPropertyName);
    676         Class args[] = null;
    677         if (valueType == null) {
    678             try {
    679                 returnVal = targetClass.getMethod(methodName, args);
    680             } catch (NoSuchMethodException e) {
    681                 // Swallow the error, log it later
    682             }
    683         } else {
    684             args = new Class[1];
    685             Class typeVariants[];
    686             if (valueType.equals(Float.class)) {
    687                 typeVariants = FLOAT_VARIANTS;
    688             } else if (valueType.equals(Integer.class)) {
    689                 typeVariants = INTEGER_VARIANTS;
    690             } else if (valueType.equals(Double.class)) {
    691                 typeVariants = DOUBLE_VARIANTS;
    692             } else {
    693                 typeVariants = new Class[1];
    694                 typeVariants[0] = valueType;
    695             }
    696             for (Class typeVariant : typeVariants) {
    697                 args[0] = typeVariant;
    698                 try {
    699                     returnVal = targetClass.getMethod(methodName, args);
    700                     if (mConverter == null) {
    701                         // change the value type to suit
    702                         mValueType = typeVariant;
    703                     }
    704                     return returnVal;
    705                 } catch (NoSuchMethodException e) {
    706                     // Swallow the error and keep trying other variants
    707                 }
    708             }
    709             // If we got here, then no appropriate function was found
    710         }
    711 
    712         if (returnVal == null) {
    713             Log.w("PropertyValuesHolder", "Method " +
    714                     getMethodName(prefix, mPropertyName) + "() with type " + valueType +
    715                     " not found on target class " + targetClass);
    716         }
    717 
    718         return returnVal;
    719     }
    720 
    721 
    722     /**
    723      * Returns the setter or getter requested. This utility function checks whether the
    724      * requested method exists in the propertyMapMap cache. If not, it calls another
    725      * utility function to request the Method from the targetClass directly.
    726      * @param targetClass The Class on which the requested method should exist.
    727      * @param propertyMapMap The cache of setters/getters derived so far.
    728      * @param prefix "set" or "get", for the setter or getter.
    729      * @param valueType The type of parameter passed into the method (null for getter).
    730      * @return Method the method associated with mPropertyName.
    731      */
    732     private Method setupSetterOrGetter(Class targetClass,
    733             HashMap<Class, HashMap<String, Method>> propertyMapMap,
    734             String prefix, Class valueType) {
    735         Method setterOrGetter = null;
    736         synchronized(propertyMapMap) {
    737             // Have to lock property map prior to reading it, to guard against
    738             // another thread putting something in there after we've checked it
    739             // but before we've added an entry to it
    740             HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
    741             boolean wasInMap = false;
    742             if (propertyMap != null) {
    743                 wasInMap = propertyMap.containsKey(mPropertyName);
    744                 if (wasInMap) {
    745                     setterOrGetter = propertyMap.get(mPropertyName);
    746                 }
    747             }
    748             if (!wasInMap) {
    749                 setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
    750                 if (propertyMap == null) {
    751                     propertyMap = new HashMap<String, Method>();
    752                     propertyMapMap.put(targetClass, propertyMap);
    753                 }
    754                 propertyMap.put(mPropertyName, setterOrGetter);
    755             }
    756         }
    757         return setterOrGetter;
    758     }
    759 
    760     /**
    761      * Utility function to get the setter from targetClass
    762      * @param targetClass The Class on which the requested method should exist.
    763      */
    764     void setupSetter(Class targetClass) {
    765         Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
    766         mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
    767     }
    768 
    769     /**
    770      * Utility function to get the getter from targetClass
    771      */
    772     private void setupGetter(Class targetClass) {
    773         mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
    774     }
    775 
    776     /**
    777      * Internal function (called from ObjectAnimator) to set up the setter and getter
    778      * prior to running the animation. If the setter has not been manually set for this
    779      * object, it will be derived automatically given the property name, target object, and
    780      * types of values supplied. If no getter has been set, it will be supplied iff any of the
    781      * supplied values was null. If there is a null value, then the getter (supplied or derived)
    782      * will be called to set those null values to the current value of the property
    783      * on the target object.
    784      * @param target The object on which the setter (and possibly getter) exist.
    785      */
    786     void setupSetterAndGetter(Object target) {
    787         mKeyframes.invalidateCache();
    788         if (mProperty != null) {
    789             // check to make sure that mProperty is on the class of target
    790             try {
    791                 Object testValue = null;
    792                 List<Keyframe> keyframes = mKeyframes.getKeyframes();
    793                 int keyframeCount = keyframes == null ? 0 : keyframes.size();
    794                 for (int i = 0; i < keyframeCount; i++) {
    795                     Keyframe kf = keyframes.get(i);
    796                     if (!kf.hasValue() || kf.valueWasSetOnStart()) {
    797                         if (testValue == null) {
    798                             testValue = convertBack(mProperty.get(target));
    799                         }
    800                         kf.setValue(testValue);
    801                         kf.setValueWasSetOnStart(true);
    802                     }
    803                 }
    804                 return;
    805             } catch (ClassCastException e) {
    806                 Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
    807                         ") on target object " + target + ". Trying reflection instead");
    808                 mProperty = null;
    809             }
    810         }
    811         // We can't just say 'else' here because the catch statement sets mProperty to null.
    812         if (mProperty == null) {
    813             Class targetClass = target.getClass();
    814             if (mSetter == null) {
    815                 setupSetter(targetClass);
    816             }
    817             List<Keyframe> keyframes = mKeyframes.getKeyframes();
    818             int keyframeCount = keyframes == null ? 0 : keyframes.size();
    819             for (int i = 0; i < keyframeCount; i++) {
    820                 Keyframe kf = keyframes.get(i);
    821                 if (!kf.hasValue() || kf.valueWasSetOnStart()) {
    822                     if (mGetter == null) {
    823                         setupGetter(targetClass);
    824                         if (mGetter == null) {
    825                             // Already logged the error - just return to avoid NPE
    826                             return;
    827                         }
    828                     }
    829                     try {
    830                         Object value = convertBack(mGetter.invoke(target));
    831                         kf.setValue(value);
    832                         kf.setValueWasSetOnStart(true);
    833                     } catch (InvocationTargetException e) {
    834                         Log.e("PropertyValuesHolder", e.toString());
    835                     } catch (IllegalAccessException e) {
    836                         Log.e("PropertyValuesHolder", e.toString());
    837                     }
    838                 }
    839             }
    840         }
    841     }
    842 
    843     private Object convertBack(Object value) {
    844         if (mConverter != null) {
    845             if (!(mConverter instanceof BidirectionalTypeConverter)) {
    846                 throw new IllegalArgumentException("Converter "
    847                         + mConverter.getClass().getName()
    848                         + " must be a BidirectionalTypeConverter");
    849             }
    850             value = ((BidirectionalTypeConverter) mConverter).convertBack(value);
    851         }
    852         return value;
    853     }
    854 
    855     /**
    856      * Utility function to set the value stored in a particular Keyframe. The value used is
    857      * whatever the value is for the property name specified in the keyframe on the target object.
    858      *
    859      * @param target The target object from which the current value should be extracted.
    860      * @param kf The keyframe which holds the property name and value.
    861      */
    862     private void setupValue(Object target, Keyframe kf) {
    863         if (mProperty != null) {
    864             Object value = convertBack(mProperty.get(target));
    865             kf.setValue(value);
    866         }
    867         try {
    868             if (mGetter == null) {
    869                 Class targetClass = target.getClass();
    870                 setupGetter(targetClass);
    871                 if (mGetter == null) {
    872                     // Already logged the error - just return to avoid NPE
    873                     return;
    874                 }
    875             }
    876             Object value = convertBack(mGetter.invoke(target));
    877             kf.setValue(value);
    878         } catch (InvocationTargetException e) {
    879             Log.e("PropertyValuesHolder", e.toString());
    880         } catch (IllegalAccessException e) {
    881             Log.e("PropertyValuesHolder", e.toString());
    882         }
    883     }
    884 
    885     /**
    886      * This function is called by ObjectAnimator when setting the start values for an animation.
    887      * The start values are set according to the current values in the target object. The
    888      * property whose value is extracted is whatever is specified by the propertyName of this
    889      * PropertyValuesHolder object.
    890      *
    891      * @param target The object which holds the start values that should be set.
    892      */
    893     void setupStartValue(Object target) {
    894         List<Keyframe> keyframes = mKeyframes.getKeyframes();
    895         if (!keyframes.isEmpty()) {
    896             setupValue(target, keyframes.get(0));
    897         }
    898     }
    899 
    900     /**
    901      * This function is called by ObjectAnimator when setting the end values for an animation.
    902      * The end values are set according to the current values in the target object. The
    903      * property whose value is extracted is whatever is specified by the propertyName of this
    904      * PropertyValuesHolder object.
    905      *
    906      * @param target The object which holds the start values that should be set.
    907      */
    908     void setupEndValue(Object target) {
    909         List<Keyframe> keyframes = mKeyframes.getKeyframes();
    910         if (!keyframes.isEmpty()) {
    911             setupValue(target, keyframes.get(keyframes.size() - 1));
    912         }
    913     }
    914 
    915     @Override
    916     public PropertyValuesHolder clone() {
    917         try {
    918             PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
    919             newPVH.mPropertyName = mPropertyName;
    920             newPVH.mProperty = mProperty;
    921             newPVH.mKeyframes = mKeyframes.clone();
    922             newPVH.mEvaluator = mEvaluator;
    923             return newPVH;
    924         } catch (CloneNotSupportedException e) {
    925             // won't reach here
    926             return null;
    927         }
    928     }
    929 
    930     /**
    931      * Internal function to set the value on the target object, using the setter set up
    932      * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
    933      * to handle turning the value calculated by ValueAnimator into a value set on the object
    934      * according to the name of the property.
    935      * @param target The target object on which the value is set
    936      */
    937     void setAnimatedValue(Object target) {
    938         if (mProperty != null) {
    939             mProperty.set(target, getAnimatedValue());
    940         }
    941         if (mSetter != null) {
    942             try {
    943                 mTmpValueArray[0] = getAnimatedValue();
    944                 mSetter.invoke(target, mTmpValueArray);
    945             } catch (InvocationTargetException e) {
    946                 Log.e("PropertyValuesHolder", e.toString());
    947             } catch (IllegalAccessException e) {
    948                 Log.e("PropertyValuesHolder", e.toString());
    949             }
    950         }
    951     }
    952 
    953     /**
    954      * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
    955      * to calculate animated values.
    956      */
    957     void init() {
    958         if (mEvaluator == null) {
    959             // We already handle int and float automatically, but not their Object
    960             // equivalents
    961             mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
    962                     (mValueType == Float.class) ? sFloatEvaluator :
    963                     null;
    964         }
    965         if (mEvaluator != null) {
    966             // KeyframeSet knows how to evaluate the common types - only give it a custom
    967             // evaluator if one has been set on this class
    968             mKeyframes.setEvaluator(mEvaluator);
    969         }
    970     }
    971 
    972     /**
    973      * The TypeEvaluator will be automatically determined based on the type of values
    974      * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
    975      * desired. This may be important in cases where either the type of the values supplied
    976      * do not match the way that they should be interpolated between, or if the values
    977      * are of a custom type or one not currently understood by the animation system. Currently,
    978      * only values of type float and int (and their Object equivalents: Float
    979      * and Integer) are  correctly interpolated; all other types require setting a TypeEvaluator.
    980      * @param evaluator
    981      */
    982     public void setEvaluator(TypeEvaluator evaluator) {
    983         mEvaluator = evaluator;
    984         mKeyframes.setEvaluator(evaluator);
    985     }
    986 
    987     /**
    988      * Function used to calculate the value according to the evaluator set up for
    989      * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
    990      *
    991      * @param fraction The elapsed, interpolated fraction of the animation.
    992      */
    993     void calculateValue(float fraction) {
    994         Object value = mKeyframes.getValue(fraction);
    995         mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
    996     }
    997 
    998     /**
    999      * Sets the name of the property that will be animated. This name is used to derive
   1000      * a setter function that will be called to set animated values.
   1001      * For example, a property name of <code>foo</code> will result
   1002      * in a call to the function <code>setFoo()</code> on the target object. If either
   1003      * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
   1004      * also be derived and called.
   1005      *
   1006      * <p>Note that the setter function derived from this property name
   1007      * must take the same parameter type as the
   1008      * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
   1009      * the setter function will fail.</p>
   1010      *
   1011      * @param propertyName The name of the property being animated.
   1012      */
   1013     public void setPropertyName(String propertyName) {
   1014         mPropertyName = propertyName;
   1015     }
   1016 
   1017     /**
   1018      * Sets the property that will be animated.
   1019      *
   1020      * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
   1021      * must exist on the target object specified in that ObjectAnimator.</p>
   1022      *
   1023      * @param property The property being animated.
   1024      */
   1025     public void setProperty(Property property) {
   1026         mProperty = property;
   1027     }
   1028 
   1029     /**
   1030      * Gets the name of the property that will be animated. This name will be used to derive
   1031      * a setter function that will be called to set animated values.
   1032      * For example, a property name of <code>foo</code> will result
   1033      * in a call to the function <code>setFoo()</code> on the target object. If either
   1034      * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
   1035      * also be derived and called.
   1036      */
   1037     public String getPropertyName() {
   1038         return mPropertyName;
   1039     }
   1040 
   1041     /**
   1042      * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
   1043      * most recently calculated in calculateValue().
   1044      * @return
   1045      */
   1046     Object getAnimatedValue() {
   1047         return mAnimatedValue;
   1048     }
   1049 
   1050     @Override
   1051     public String toString() {
   1052         return mPropertyName + ": " + mKeyframes.toString();
   1053     }
   1054 
   1055     /**
   1056      * Utility method to derive a setter/getter method name from a property name, where the
   1057      * prefix is typically "set" or "get" and the first letter of the property name is
   1058      * capitalized.
   1059      *
   1060      * @param prefix The precursor to the method name, before the property name begins, typically
   1061      * "set" or "get".
   1062      * @param propertyName The name of the property that represents the bulk of the method name
   1063      * after the prefix. The first letter of this word will be capitalized in the resulting
   1064      * method name.
   1065      * @return String the property name converted to a method name according to the conventions
   1066      * specified above.
   1067      */
   1068     static String getMethodName(String prefix, String propertyName) {
   1069         if (propertyName == null || propertyName.length() == 0) {
   1070             // shouldn't get here
   1071             return prefix;
   1072         }
   1073         char firstLetter = Character.toUpperCase(propertyName.charAt(0));
   1074         String theRest = propertyName.substring(1);
   1075         return prefix + firstLetter + theRest;
   1076     }
   1077 
   1078     static class IntPropertyValuesHolder extends PropertyValuesHolder {
   1079 
   1080         // Cache JNI functions to avoid looking them up twice
   1081         private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
   1082                 new HashMap<Class, HashMap<String, Long>>();
   1083         long mJniSetter;
   1084         private IntProperty mIntProperty;
   1085 
   1086         Keyframes.IntKeyframes mIntKeyframes;
   1087         int mIntAnimatedValue;
   1088 
   1089         public IntPropertyValuesHolder(String propertyName, Keyframes.IntKeyframes keyframes) {
   1090             super(propertyName);
   1091             mValueType = int.class;
   1092             mKeyframes = keyframes;
   1093             mIntKeyframes = keyframes;
   1094         }
   1095 
   1096         public IntPropertyValuesHolder(Property property, Keyframes.IntKeyframes keyframes) {
   1097             super(property);
   1098             mValueType = int.class;
   1099             mKeyframes = keyframes;
   1100             mIntKeyframes = keyframes;
   1101             if (property instanceof  IntProperty) {
   1102                 mIntProperty = (IntProperty) mProperty;
   1103             }
   1104         }
   1105 
   1106         public IntPropertyValuesHolder(String propertyName, int... values) {
   1107             super(propertyName);
   1108             setIntValues(values);
   1109         }
   1110 
   1111         public IntPropertyValuesHolder(Property property, int... values) {
   1112             super(property);
   1113             setIntValues(values);
   1114             if (property instanceof  IntProperty) {
   1115                 mIntProperty = (IntProperty) mProperty;
   1116             }
   1117         }
   1118 
   1119         @Override
   1120         public void setIntValues(int... values) {
   1121             super.setIntValues(values);
   1122             mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
   1123         }
   1124 
   1125         @Override
   1126         void calculateValue(float fraction) {
   1127             mIntAnimatedValue = mIntKeyframes.getIntValue(fraction);
   1128         }
   1129 
   1130         @Override
   1131         Object getAnimatedValue() {
   1132             return mIntAnimatedValue;
   1133         }
   1134 
   1135         @Override
   1136         public IntPropertyValuesHolder clone() {
   1137             IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
   1138             newPVH.mIntKeyframes = (Keyframes.IntKeyframes) newPVH.mKeyframes;
   1139             return newPVH;
   1140         }
   1141 
   1142         /**
   1143          * Internal function to set the value on the target object, using the setter set up
   1144          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
   1145          * to handle turning the value calculated by ValueAnimator into a value set on the object
   1146          * according to the name of the property.
   1147          * @param target The target object on which the value is set
   1148          */
   1149         @Override
   1150         void setAnimatedValue(Object target) {
   1151             if (mIntProperty != null) {
   1152                 mIntProperty.setValue(target, mIntAnimatedValue);
   1153                 return;
   1154             }
   1155             if (mProperty != null) {
   1156                 mProperty.set(target, mIntAnimatedValue);
   1157                 return;
   1158             }
   1159             if (mJniSetter != 0) {
   1160                 nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
   1161                 return;
   1162             }
   1163             if (mSetter != null) {
   1164                 try {
   1165                     mTmpValueArray[0] = mIntAnimatedValue;
   1166                     mSetter.invoke(target, mTmpValueArray);
   1167                 } catch (InvocationTargetException e) {
   1168                     Log.e("PropertyValuesHolder", e.toString());
   1169                 } catch (IllegalAccessException e) {
   1170                     Log.e("PropertyValuesHolder", e.toString());
   1171                 }
   1172             }
   1173         }
   1174 
   1175         @Override
   1176         void setupSetter(Class targetClass) {
   1177             if (mProperty != null) {
   1178                 return;
   1179             }
   1180             // Check new static hashmap<propName, int> for setter method
   1181             synchronized(sJNISetterPropertyMap) {
   1182                 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
   1183                 boolean wasInMap = false;
   1184                 if (propertyMap != null) {
   1185                     wasInMap = propertyMap.containsKey(mPropertyName);
   1186                     if (wasInMap) {
   1187                         Long jniSetter = propertyMap.get(mPropertyName);
   1188                         if (jniSetter != null) {
   1189                             mJniSetter = jniSetter;
   1190                         }
   1191                     }
   1192                 }
   1193                 if (!wasInMap) {
   1194                     String methodName = getMethodName("set", mPropertyName);
   1195                     try {
   1196                         mJniSetter = nGetIntMethod(targetClass, methodName);
   1197                     } catch (NoSuchMethodError e) {
   1198                         // Couldn't find it via JNI - try reflection next. Probably means the method
   1199                         // doesn't exist, or the type is wrong. An error will be logged later if
   1200                         // reflection fails as well.
   1201                     }
   1202                     if (propertyMap == null) {
   1203                         propertyMap = new HashMap<String, Long>();
   1204                         sJNISetterPropertyMap.put(targetClass, propertyMap);
   1205                     }
   1206                     propertyMap.put(mPropertyName, mJniSetter);
   1207                 }
   1208             }
   1209             if (mJniSetter == 0) {
   1210                 // Couldn't find method through fast JNI approach - just use reflection
   1211                 super.setupSetter(targetClass);
   1212             }
   1213         }
   1214     }
   1215 
   1216     static class FloatPropertyValuesHolder extends PropertyValuesHolder {
   1217 
   1218         // Cache JNI functions to avoid looking them up twice
   1219         private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
   1220                 new HashMap<Class, HashMap<String, Long>>();
   1221         long mJniSetter;
   1222         private FloatProperty mFloatProperty;
   1223 
   1224         Keyframes.FloatKeyframes mFloatKeyframes;
   1225         float mFloatAnimatedValue;
   1226 
   1227         public FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes) {
   1228             super(propertyName);
   1229             mValueType = float.class;
   1230             mKeyframes = keyframes;
   1231             mFloatKeyframes = keyframes;
   1232         }
   1233 
   1234         public FloatPropertyValuesHolder(Property property, Keyframes.FloatKeyframes keyframes) {
   1235             super(property);
   1236             mValueType = float.class;
   1237             mKeyframes = keyframes;
   1238             mFloatKeyframes = keyframes;
   1239             if (property instanceof FloatProperty) {
   1240                 mFloatProperty = (FloatProperty) mProperty;
   1241             }
   1242         }
   1243 
   1244         public FloatPropertyValuesHolder(String propertyName, float... values) {
   1245             super(propertyName);
   1246             setFloatValues(values);
   1247         }
   1248 
   1249         public FloatPropertyValuesHolder(Property property, float... values) {
   1250             super(property);
   1251             setFloatValues(values);
   1252             if (property instanceof  FloatProperty) {
   1253                 mFloatProperty = (FloatProperty) mProperty;
   1254             }
   1255         }
   1256 
   1257         @Override
   1258         public void setFloatValues(float... values) {
   1259             super.setFloatValues(values);
   1260             mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
   1261         }
   1262 
   1263         @Override
   1264         void calculateValue(float fraction) {
   1265             mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
   1266         }
   1267 
   1268         @Override
   1269         Object getAnimatedValue() {
   1270             return mFloatAnimatedValue;
   1271         }
   1272 
   1273         @Override
   1274         public FloatPropertyValuesHolder clone() {
   1275             FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
   1276             newPVH.mFloatKeyframes = (Keyframes.FloatKeyframes) newPVH.mKeyframes;
   1277             return newPVH;
   1278         }
   1279 
   1280         /**
   1281          * Internal function to set the value on the target object, using the setter set up
   1282          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
   1283          * to handle turning the value calculated by ValueAnimator into a value set on the object
   1284          * according to the name of the property.
   1285          * @param target The target object on which the value is set
   1286          */
   1287         @Override
   1288         void setAnimatedValue(Object target) {
   1289             if (mFloatProperty != null) {
   1290                 mFloatProperty.setValue(target, mFloatAnimatedValue);
   1291                 return;
   1292             }
   1293             if (mProperty != null) {
   1294                 mProperty.set(target, mFloatAnimatedValue);
   1295                 return;
   1296             }
   1297             if (mJniSetter != 0) {
   1298                 nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
   1299                 return;
   1300             }
   1301             if (mSetter != null) {
   1302                 try {
   1303                     mTmpValueArray[0] = mFloatAnimatedValue;
   1304                     mSetter.invoke(target, mTmpValueArray);
   1305                 } catch (InvocationTargetException e) {
   1306                     Log.e("PropertyValuesHolder", e.toString());
   1307                 } catch (IllegalAccessException e) {
   1308                     Log.e("PropertyValuesHolder", e.toString());
   1309                 }
   1310             }
   1311         }
   1312 
   1313         @Override
   1314         void setupSetter(Class targetClass) {
   1315             if (mProperty != null) {
   1316                 return;
   1317             }
   1318             // Check new static hashmap<propName, int> for setter method
   1319             synchronized (sJNISetterPropertyMap) {
   1320                 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
   1321                 boolean wasInMap = false;
   1322                 if (propertyMap != null) {
   1323                     wasInMap = propertyMap.containsKey(mPropertyName);
   1324                     if (wasInMap) {
   1325                         Long jniSetter = propertyMap.get(mPropertyName);
   1326                         if (jniSetter != null) {
   1327                             mJniSetter = jniSetter;
   1328                         }
   1329                     }
   1330                 }
   1331                 if (!wasInMap) {
   1332                     String methodName = getMethodName("set", mPropertyName);
   1333                     try {
   1334                         mJniSetter = nGetFloatMethod(targetClass, methodName);
   1335                     } catch (NoSuchMethodError e) {
   1336                         // Couldn't find it via JNI - try reflection next. Probably means the method
   1337                         // doesn't exist, or the type is wrong. An error will be logged later if
   1338                         // reflection fails as well.
   1339                     }
   1340                     if (propertyMap == null) {
   1341                         propertyMap = new HashMap<String, Long>();
   1342                         sJNISetterPropertyMap.put(targetClass, propertyMap);
   1343                     }
   1344                     propertyMap.put(mPropertyName, mJniSetter);
   1345                 }
   1346             }
   1347             if (mJniSetter == 0) {
   1348                 // Couldn't find method through fast JNI approach - just use reflection
   1349                 super.setupSetter(targetClass);
   1350             }
   1351         }
   1352 
   1353     }
   1354 
   1355     static class MultiFloatValuesHolder extends PropertyValuesHolder {
   1356         private long mJniSetter;
   1357         private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
   1358                 new HashMap<Class, HashMap<String, Long>>();
   1359 
   1360         public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
   1361                 TypeEvaluator evaluator, Object... values) {
   1362             super(propertyName);
   1363             setConverter(converter);
   1364             setObjectValues(values);
   1365             setEvaluator(evaluator);
   1366         }
   1367 
   1368         public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
   1369                 TypeEvaluator evaluator, Keyframes keyframes) {
   1370             super(propertyName);
   1371             setConverter(converter);
   1372             mKeyframes = keyframes;
   1373             setEvaluator(evaluator);
   1374         }
   1375 
   1376         /**
   1377          * Internal function to set the value on the target object, using the setter set up
   1378          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
   1379          * to handle turning the value calculated by ValueAnimator into a value set on the object
   1380          * according to the name of the property.
   1381          *
   1382          * @param target The target object on which the value is set
   1383          */
   1384         @Override
   1385         void setAnimatedValue(Object target) {
   1386             float[] values = (float[]) getAnimatedValue();
   1387             int numParameters = values.length;
   1388             if (mJniSetter != 0) {
   1389                 switch (numParameters) {
   1390                     case 1:
   1391                         nCallFloatMethod(target, mJniSetter, values[0]);
   1392                         break;
   1393                     case 2:
   1394                         nCallTwoFloatMethod(target, mJniSetter, values[0], values[1]);
   1395                         break;
   1396                     case 4:
   1397                         nCallFourFloatMethod(target, mJniSetter, values[0], values[1],
   1398                                 values[2], values[3]);
   1399                         break;
   1400                     default: {
   1401                         nCallMultipleFloatMethod(target, mJniSetter, values);
   1402                         break;
   1403                     }
   1404                 }
   1405             }
   1406         }
   1407 
   1408         /**
   1409          * Internal function (called from ObjectAnimator) to set up the setter and getter
   1410          * prior to running the animation. No getter can be used for multiple parameters.
   1411          *
   1412          * @param target The object on which the setter exists.
   1413          */
   1414         @Override
   1415         void setupSetterAndGetter(Object target) {
   1416             setupSetter(target.getClass());
   1417         }
   1418 
   1419         @Override
   1420         void setupSetter(Class targetClass) {
   1421             if (mJniSetter != 0) {
   1422                 return;
   1423             }
   1424             synchronized(sJNISetterPropertyMap) {
   1425                 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
   1426                 boolean wasInMap = false;
   1427                 if (propertyMap != null) {
   1428                     wasInMap = propertyMap.containsKey(mPropertyName);
   1429                     if (wasInMap) {
   1430                         Long jniSetter = propertyMap.get(mPropertyName);
   1431                         if (jniSetter != null) {
   1432                             mJniSetter = jniSetter;
   1433                         }
   1434                     }
   1435                 }
   1436                 if (!wasInMap) {
   1437                     String methodName = getMethodName("set", mPropertyName);
   1438                     calculateValue(0f);
   1439                     float[] values = (float[]) getAnimatedValue();
   1440                     int numParams = values.length;
   1441                     try {
   1442                         mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams);
   1443                     } catch (NoSuchMethodError e) {
   1444                         // try without the 'set' prefix
   1445                         try {
   1446                             mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName,
   1447                                     numParams);
   1448                         } catch (NoSuchMethodError e2) {
   1449                             // just try reflection next
   1450                         }
   1451                     }
   1452                     if (propertyMap == null) {
   1453                         propertyMap = new HashMap<String, Long>();
   1454                         sJNISetterPropertyMap.put(targetClass, propertyMap);
   1455                     }
   1456                     propertyMap.put(mPropertyName, mJniSetter);
   1457                 }
   1458            }
   1459         }
   1460     }
   1461 
   1462     static class MultiIntValuesHolder extends PropertyValuesHolder {
   1463         private long mJniSetter;
   1464         private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
   1465                 new HashMap<Class, HashMap<String, Long>>();
   1466 
   1467         public MultiIntValuesHolder(String propertyName, TypeConverter converter,
   1468                 TypeEvaluator evaluator, Object... values) {
   1469             super(propertyName);
   1470             setConverter(converter);
   1471             setObjectValues(values);
   1472             setEvaluator(evaluator);
   1473         }
   1474 
   1475         public MultiIntValuesHolder(String propertyName, TypeConverter converter,
   1476                 TypeEvaluator evaluator, Keyframes keyframes) {
   1477             super(propertyName);
   1478             setConverter(converter);
   1479             mKeyframes = keyframes;
   1480             setEvaluator(evaluator);
   1481         }
   1482 
   1483         /**
   1484          * Internal function to set the value on the target object, using the setter set up
   1485          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
   1486          * to handle turning the value calculated by ValueAnimator into a value set on the object
   1487          * according to the name of the property.
   1488          *
   1489          * @param target The target object on which the value is set
   1490          */
   1491         @Override
   1492         void setAnimatedValue(Object target) {
   1493             int[] values = (int[]) getAnimatedValue();
   1494             int numParameters = values.length;
   1495             if (mJniSetter != 0) {
   1496                 switch (numParameters) {
   1497                     case 1:
   1498                         nCallIntMethod(target, mJniSetter, values[0]);
   1499                         break;
   1500                     case 2:
   1501                         nCallTwoIntMethod(target, mJniSetter, values[0], values[1]);
   1502                         break;
   1503                     case 4:
   1504                         nCallFourIntMethod(target, mJniSetter, values[0], values[1],
   1505                                 values[2], values[3]);
   1506                         break;
   1507                     default: {
   1508                         nCallMultipleIntMethod(target, mJniSetter, values);
   1509                         break;
   1510                     }
   1511                 }
   1512             }
   1513         }
   1514 
   1515         /**
   1516          * Internal function (called from ObjectAnimator) to set up the setter and getter
   1517          * prior to running the animation. No getter can be used for multiple parameters.
   1518          *
   1519          * @param target The object on which the setter exists.
   1520          */
   1521         @Override
   1522         void setupSetterAndGetter(Object target) {
   1523             setupSetter(target.getClass());
   1524         }
   1525 
   1526         @Override
   1527         void setupSetter(Class targetClass) {
   1528             if (mJniSetter != 0) {
   1529                 return;
   1530             }
   1531             synchronized(sJNISetterPropertyMap) {
   1532                 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
   1533                 boolean wasInMap = false;
   1534                 if (propertyMap != null) {
   1535                     wasInMap = propertyMap.containsKey(mPropertyName);
   1536                     if (wasInMap) {
   1537                         Long jniSetter = propertyMap.get(mPropertyName);
   1538                         if (jniSetter != null) {
   1539                             mJniSetter = jniSetter;
   1540                         }
   1541                     }
   1542                 }
   1543                 if (!wasInMap) {
   1544                     String methodName = getMethodName("set", mPropertyName);
   1545                     calculateValue(0f);
   1546                     int[] values = (int[]) getAnimatedValue();
   1547                     int numParams = values.length;
   1548                     try {
   1549                         mJniSetter = nGetMultipleIntMethod(targetClass, methodName, numParams);
   1550                     } catch (NoSuchMethodError e) {
   1551                         // try without the 'set' prefix
   1552                         try {
   1553                             mJniSetter = nGetMultipleIntMethod(targetClass, mPropertyName,
   1554                                     numParams);
   1555                         } catch (NoSuchMethodError e2) {
   1556                             // couldn't find it.
   1557                         }
   1558                     }
   1559                     if (propertyMap == null) {
   1560                         propertyMap = new HashMap<String, Long>();
   1561                         sJNISetterPropertyMap.put(targetClass, propertyMap);
   1562                     }
   1563                     propertyMap.put(mPropertyName, mJniSetter);
   1564                 }
   1565             }
   1566         }
   1567     }
   1568 
   1569     /**
   1570      * Convert from PointF to float[] for multi-float setters along a Path.
   1571      */
   1572     private static class PointFToFloatArray extends TypeConverter<PointF, float[]> {
   1573         private float[] mCoordinates = new float[2];
   1574 
   1575         public PointFToFloatArray() {
   1576             super(PointF.class, float[].class);
   1577         }
   1578 
   1579         @Override
   1580         public float[] convert(PointF value) {
   1581             mCoordinates[0] = value.x;
   1582             mCoordinates[1] = value.y;
   1583             return mCoordinates;
   1584         }
   1585     };
   1586 
   1587     /**
   1588      * Convert from PointF to int[] for multi-int setters along a Path.
   1589      */
   1590     private static class PointFToIntArray extends TypeConverter<PointF, int[]> {
   1591         private int[] mCoordinates = new int[2];
   1592 
   1593         public PointFToIntArray() {
   1594             super(PointF.class, int[].class);
   1595         }
   1596 
   1597         @Override
   1598         public int[] convert(PointF value) {
   1599             mCoordinates[0] = Math.round(value.x);
   1600             mCoordinates[1] = Math.round(value.y);
   1601             return mCoordinates;
   1602         }
   1603     };
   1604 
   1605     native static private long nGetIntMethod(Class targetClass, String methodName);
   1606     native static private long nGetFloatMethod(Class targetClass, String methodName);
   1607     native static private long nGetMultipleIntMethod(Class targetClass, String methodName,
   1608             int numParams);
   1609     native static private long nGetMultipleFloatMethod(Class targetClass, String methodName,
   1610             int numParams);
   1611     native static private void nCallIntMethod(Object target, long methodID, int arg);
   1612     native static private void nCallFloatMethod(Object target, long methodID, float arg);
   1613     native static private void nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2);
   1614     native static private void nCallFourIntMethod(Object target, long methodID, int arg1, int arg2,
   1615             int arg3, int arg4);
   1616     native static private void nCallMultipleIntMethod(Object target, long methodID, int[] args);
   1617     native static private void nCallTwoFloatMethod(Object target, long methodID, float arg1,
   1618             float arg2);
   1619     native static private void nCallFourFloatMethod(Object target, long methodID, float arg1,
   1620             float arg2, float arg3, float arg4);
   1621     native static private void nCallMultipleFloatMethod(Object target, long methodID, float[] args);
   1622 }
   1623