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