PropertyValuesHolder.java revision 691ac26817d489d9770aa6ba7b098ff17e8be99a
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
17package android.animation;
18
19import android.util.Log;
20import android.animation.Keyframe.IntKeyframe;
21import android.animation.Keyframe.FloatKeyframe;
22
23import java.lang.reflect.InvocationTargetException;
24import java.lang.reflect.Method;
25import java.util.ArrayList;
26import java.util.HashMap;
27import java.util.concurrent.locks.ReentrantReadWriteLock;
28
29/**
30 * This class holds information about a property and the values that that property
31 * should take on during an animation. PropertyValuesHolder objects can be used to create
32 * animations with ValueAnimator or ObjectAnimator that operate on several different properties
33 * in parallel.
34 */
35public class PropertyValuesHolder implements Cloneable {
36
37    /**
38     * The name of the property associated with the values. This need not be a real property,
39     * unless this object is being used with ObjectAnimator. But this is the name by which
40     * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator.
41     */
42    private String mPropertyName;
43
44    /**
45     * The setter function, if needed. ObjectAnimator hands off this functionality to
46     * PropertyValuesHolder, since it holds all of the per-property information. This
47     * property can be manually set via setSetter(). Otherwise, it is automatically
48     * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
49     */
50    Method mSetter = null;
51
52    /**
53     * The getter function, if needed. ObjectAnimator hands off this functionality to
54     * PropertyValuesHolder, since it holds all of the per-property information. This
55     * property can be manually set via setSetter(). Otherwise, it is automatically
56     * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
57     * The getter is only derived and used if one of the values is null.
58     */
59    private Method mGetter = null;
60
61    /**
62     * The type of values supplied. This information is used both in deriving the setter/getter
63     * functions and in deriving the type of TypeEvaluator.
64     */
65    Class mValueType;
66
67    /**
68     * The set of keyframes (time/value pairs) that define this animation.
69     */
70    KeyframeSet mKeyframeSet = null;
71
72
73    // type evaluators for the three primitive types handled by this implementation
74    private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
75    private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
76    private static final TypeEvaluator sDoubleEvaluator = new DoubleEvaluator();
77
78    // We try several different types when searching for appropriate setter/getter functions.
79    // The caller may have supplied values in a type that does not match the setter/getter
80    // functions (such as the integers 0 and 1 to represent floating point values for alpha).
81    // Also, the use of generics in constructors means that we end up with the Object versions
82    // of primitive types (Float vs. float). But most likely, the setter/getter functions
83    // will take primitive types instead.
84    // So we supply an ordered array of other types to try before giving up.
85    private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
86            Double.class, Integer.class};
87    private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
88            Float.class, Double.class};
89    private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class,
90            Float.class, Integer.class};
91
92    // These maps hold all property entries for a particular class. This map
93    // is used to speed up property/setter/getter lookups for a given class/property
94    // combination. No need to use reflection on the combination more than once.
95    private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap =
96            new HashMap<Class, HashMap<String, Method>>();
97    private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap =
98            new HashMap<Class, HashMap<String, Method>>();
99
100    // This lock is used to ensure that only one thread is accessing the property maps
101    // at a time.
102    private ReentrantReadWriteLock propertyMapLock = new ReentrantReadWriteLock();
103
104    // Used to pass single value to varargs parameter in setter invocation
105    Object[] mTmpValueArray = new Object[1];
106
107    /**
108     * The type evaluator used to calculate the animated values. This evaluator is determined
109     * automatically based on the type of the start/end objects passed into the constructor,
110     * but the system only knows about the primitive types int, double, and float. Any other
111     * type will need to set the evaluator to a custom evaluator for that type.
112     */
113    private TypeEvaluator mEvaluator;
114
115    /**
116     * The value most recently calculated by calculateValue(). This is set during
117     * that function and might be retrieved later either by ValueAnimator.animatedValue() or
118     * by the property-setting logic in ObjectAnimator.animatedValue().
119     */
120    private Object mAnimatedValue;
121
122    /**
123     * Internal utility constructor, used by the factory methods to set the property name.
124     * @param propertyName The name of the property for this holder.
125     */
126    private PropertyValuesHolder(String propertyName) {
127        mPropertyName = propertyName;
128    }
129
130    /**
131     * Constructs and returns a PropertyValuesHolder with a given property name and
132     * set of int values.
133     * @param propertyName The name of the property being animated.
134     * @param values The values that the named property will animate between.
135     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
136     */
137    public static PropertyValuesHolder ofInt(String propertyName, int... values) {
138        PropertyValuesHolder pvh = new IntPropertyValuesHolder(propertyName, values);
139        return pvh;
140    }
141
142    /**
143     * Constructs and returns a PropertyValuesHolder with a given property name and
144     * set of float values.
145     * @param propertyName The name of the property being animated.
146     * @param values The values that the named property will animate between.
147     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
148     */
149    public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
150        PropertyValuesHolder pvh = new FloatPropertyValuesHolder(propertyName, values);
151        return pvh;
152    }
153
154    /**
155     * Constructs and returns a PropertyValuesHolder with a given property name and
156     * set of Object values. This variant also takes a TypeEvaluator because the system
157     * cannot interpolate between objects of unknown type.
158     *
159     * @param propertyName The name of the property being animated.
160     * @param evaluator A TypeEvaluator that will be called on each animation frame to
161     * provide the ncessry interpolation between the Object values to derive the animated
162     * value.
163     * @param values The values that the named property will animate between.
164     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
165     */
166    public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
167            Object... values) {
168        PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
169        pvh.setObjectValues(values);
170        pvh.setEvaluator(evaluator);
171        return pvh;
172    }
173
174    /**
175     * Constructs and returns a PropertyValuesHolder object with the specified property name and set
176     * of values. These values can be of any type, but the type should be consistent so that
177     * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
178     * the common type.
179     * <p>If there is only one value, it is assumed to be the end value of an animation,
180     * and an initial value will be derived, if possible, by calling a getter function
181     * on the object. Also, if any value is null, the value will be filled in when the animation
182     * starts in the same way. This mechanism of automatically getting null values only works
183     * if the PropertyValuesHolder object is used in conjunction
184     * {@link ObjectAnimator}, and with a getter function either
185     * derived automatically from <code>propertyName</code> or set explicitly via
186     * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
187     * no way of determining what the value should be.
188     * @param propertyName The name of the property associated with this set of values. This
189     * can be the actual property name to be used when using a ObjectAnimator object, or
190     * just a name used to get animated values, such as if this object is used with an
191     * ValueAnimator object.
192     * @param values The set of values to animate between.
193     */
194    public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
195        KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
196        if (keyframeSet instanceof IntKeyframeSet) {
197            return new IntPropertyValuesHolder(propertyName, (IntKeyframeSet) keyframeSet);
198        } else if (keyframeSet instanceof FloatKeyframeSet) {
199            return new FloatPropertyValuesHolder(propertyName, (FloatKeyframeSet) keyframeSet);
200        }
201        else {
202            PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
203            pvh.mKeyframeSet = keyframeSet;
204            pvh.mValueType = ((Keyframe)values[0]).getType();
205            return pvh;
206        }
207    }
208
209    /**
210     * Set the animated values for this object to this set of ints.
211     * If there is only one value, it is assumed to be the end value of an animation,
212     * and an initial value will be derived, if possible, by calling a getter function
213     * on the object. Also, if any value is null, the value will be filled in when the animation
214     * starts in the same way. This mechanism of automatically getting null values only works
215     * if the PropertyValuesHolder object is used in conjunction
216     * {@link ObjectAnimator}, and with a getter function either
217     * derived automatically from <code>propertyName</code> or set explicitly via
218     * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
219     * no way of determining what the value should be.
220     *
221     * @param values One or more values that the animation will animate between.
222     */
223    public void setIntValues(int... values) {
224        mValueType = int.class;
225        mKeyframeSet = KeyframeSet.ofInt(values);
226    }
227
228    /**
229     * Set the animated values for this object to this set of floats.
230     * If there is only one value, it is assumed to be the end value of an animation,
231     * and an initial value will be derived, if possible, by calling a getter function
232     * on the object. Also, if any value is null, the value will be filled in when the animation
233     * starts in the same way. This mechanism of automatically getting null values only works
234     * if the PropertyValuesHolder object is used in conjunction
235     * {@link ObjectAnimator}, and with a getter function either
236     * derived automatically from <code>propertyName</code> or set explicitly via
237     * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
238     * no way of determining what the value should be.
239     *
240     * @param values One or more values that the animation will animate between.
241     */
242    public void setFloatValues(float... values) {
243        mValueType = float.class;
244        mKeyframeSet = KeyframeSet.ofFloat(values);
245    }
246
247    /**
248     * Set the animated values for this object to this set of Keyframes.
249     *
250     * @param values One or more values that the animation will animate between.
251     */
252    public void setKeyframes(Keyframe... values) {
253        int numKeyframes = values.length;
254        Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
255        mValueType = ((Keyframe)values[0]).getType();
256        for (int i = 0; i < numKeyframes; ++i) {
257            keyframes[i] = (Keyframe)values[i];
258        }
259        mKeyframeSet = new KeyframeSet(keyframes);
260    }
261
262    /**
263     * Set the animated values for this object to this set of Objects.
264     * If there is only one value, it is assumed to be the end value of an animation,
265     * and an initial value will be derived, if possible, by calling a getter function
266     * on the object. Also, if any value is null, the value will be filled in when the animation
267     * starts in the same way. This mechanism of automatically getting null values only works
268     * if the PropertyValuesHolder object is used in conjunction
269     * {@link ObjectAnimator}, and with a getter function either
270     * derived automatically from <code>propertyName</code> or set explicitly via
271     * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
272     * no way of determining what the value should be.
273     *
274     * @param values One or more values that the animation will animate between.
275     */
276    public void setObjectValues(Object... values) {
277        mValueType = values[0].getClass();
278        mKeyframeSet = KeyframeSet.ofObject(values);
279    }
280
281    /**
282     * Determine the setter or getter function using the JavaBeans convention of setFoo or
283     * getFoo for a property named 'foo'. This function figures out what the name of the
284     * function should be and uses reflection to find the Method with that name on the
285     * target object.
286     *
287     * @param targetClass The class to search for the method
288     * @param prefix "set" or "get", depending on whether we need a setter or getter.
289     * @param valueType The type of the parameter (in the case of a setter). This type
290     * is derived from the values set on this PropertyValuesHolder. This type is used as
291     * a first guess at the parameter type, but we check for methods with several different
292     * types to avoid problems with slight mis-matches between supplied values and actual
293     * value types used on the setter.
294     * @return Method the method associated with mPropertyName.
295     */
296    private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
297        // TODO: faster implementation...
298        Method returnVal = null;
299        String firstLetter = mPropertyName.substring(0, 1);
300        String theRest = mPropertyName.substring(1);
301        firstLetter = firstLetter.toUpperCase();
302        String methodName = prefix + firstLetter + theRest;
303        Class args[] = null;
304        if (valueType == null) {
305            try {
306                returnVal = targetClass.getMethod(methodName, args);
307            } catch (NoSuchMethodException e) {
308                Log.e("PropertyValuesHolder",
309                        "Couldn't find no-arg method for property " + mPropertyName + ": " + e);
310            }
311        } else {
312            args = new Class[1];
313            Class typeVariants[];
314            if (mValueType.equals(Float.class)) {
315                typeVariants = FLOAT_VARIANTS;
316            } else if (mValueType.equals(Integer.class)) {
317                typeVariants = INTEGER_VARIANTS;
318            } else if (mValueType.equals(Double.class)) {
319                typeVariants = DOUBLE_VARIANTS;
320            } else {
321                typeVariants = new Class[1];
322                typeVariants[0] = mValueType;
323            }
324            for (Class typeVariant : typeVariants) {
325                args[0] = typeVariant;
326                try {
327                    returnVal = targetClass.getMethod(methodName, args);
328                    // change the value type to suit
329                    mValueType = typeVariant;
330                    return returnVal;
331                } catch (NoSuchMethodException e) {
332                    // Swallow the error and keep trying other variants
333                }
334            }
335            // If we got here, then no appropriate function was found
336            Log.e("PropertyValuesHolder",
337                    "Couldn't find setter/getter for property " + mPropertyName +
338                            "with value type "+ mValueType);
339        }
340
341        return returnVal;
342    }
343
344
345    /**
346     * Returns the setter or getter requested. This utility function checks whether the
347     * requested method exists in the propertyMapMap cache. If not, it calls another
348     * utility function to request the Method from the targetClass directly.
349     * @param targetClass The Class on which the requested method should exist.
350     * @param propertyMapMap The cache of setters/getters derived so far.
351     * @param prefix "set" or "get", for the setter or getter.
352     * @param valueType The type of parameter passed into the method (null for getter).
353     * @return Method the method associated with mPropertyName.
354     */
355    private Method setupSetterOrGetter(Class targetClass,
356            HashMap<Class, HashMap<String, Method>> propertyMapMap,
357            String prefix, Class valueType) {
358        Method setterOrGetter = null;
359        try {
360            // Have to lock property map prior to reading it, to guard against
361            // another thread putting something in there after we've checked it
362            // but before we've added an entry to it
363            // TODO: can we store the setter/getter per Class instead of per Object?
364            propertyMapLock.writeLock().lock();
365            HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
366            if (propertyMap != null) {
367                setterOrGetter = propertyMap.get(mPropertyName);
368            }
369            if (setterOrGetter == null) {
370                setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
371                if (propertyMap == null) {
372                    propertyMap = new HashMap<String, Method>();
373                    propertyMapMap.put(targetClass, propertyMap);
374                }
375                propertyMap.put(mPropertyName, setterOrGetter);
376            }
377        } finally {
378            propertyMapLock.writeLock().unlock();
379        }
380        return setterOrGetter;
381    }
382
383    /**
384     * Utility function to get the setter from targetClass
385     * @param targetClass The Class on which the requested method should exist.
386     */
387    private void setupSetter(Class targetClass) {
388        mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);
389    }
390
391    /**
392     * Utility function to get the getter from targetClass
393     */
394    private void setupGetter(Class targetClass) {
395        mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
396    }
397
398    /**
399     * Internal function (called from ObjectAnimator) to set up the setter and getter
400     * prior to running the animation. If the setter has not been manually set for this
401     * object, it will be derived automatically given the property name, target object, and
402     * types of values supplied. If no getter has been set, it will be supplied iff any of the
403     * supplied values was null. If there is a null value, then the getter (supplied or derived)
404     * will be called to set those null values to the current value of the property
405     * on the target object.
406     * @param target The object on which the setter (and possibly getter) exist.
407     */
408    void setupSetterAndGetter(Object target) {
409        Class targetClass = target.getClass();
410        if (mSetter == null) {
411            setupSetter(targetClass);
412        }
413        for (Keyframe kf : mKeyframeSet.mKeyframes) {
414            if (!kf.hasValue()) {
415                if (mGetter == null) {
416                    setupGetter(targetClass);
417                }
418                try {
419                    kf.setValue(mGetter.invoke(target));
420                } catch (InvocationTargetException e) {
421                    Log.e("PropertyValuesHolder", e.toString());
422                } catch (IllegalAccessException e) {
423                    Log.e("PropertyValuesHolder", e.toString());
424                }
425            }
426        }
427    }
428
429    /**
430     * Utility function to set the value stored in a particular Keyframe. The value used is
431     * whatever the value is for the property name specified in the keyframe on the target object.
432     *
433     * @param target The target object from which the current value should be extracted.
434     * @param kf The keyframe which holds the property name and value.
435     */
436    private void setupValue(Object target, Keyframe kf) {
437        try {
438            if (mGetter == null) {
439                Class targetClass = target.getClass();
440                setupGetter(targetClass);
441            }
442            kf.setValue(mGetter.invoke(target));
443        } catch (InvocationTargetException e) {
444            Log.e("PropertyValuesHolder", e.toString());
445        } catch (IllegalAccessException e) {
446            Log.e("PropertyValuesHolder", e.toString());
447        }
448    }
449
450    /**
451     * This function is called by ObjectAnimator when setting the start values for an animation.
452     * The start values are set according to the current values in the target object. The
453     * property whose value is extracted is whatever is specified by the propertyName of this
454     * PropertyValuesHolder object.
455     *
456     * @param target The object which holds the start values that should be set.
457     */
458    void setupStartValue(Object target) {
459        setupValue(target, mKeyframeSet.mKeyframes.get(0));
460    }
461
462    /**
463     * This function is called by ObjectAnimator when setting the end values for an animation.
464     * The end values are set according to the current values in the target object. The
465     * property whose value is extracted is whatever is specified by the propertyName of this
466     * PropertyValuesHolder object.
467     *
468     * @param target The object which holds the start values that should be set.
469     */
470    void setupEndValue(Object target) {
471        setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
472    }
473
474    @Override
475    public PropertyValuesHolder clone() {
476        try {
477            PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
478            newPVH.mPropertyName = mPropertyName;
479            newPVH.mKeyframeSet = mKeyframeSet.clone();
480            newPVH.mEvaluator = mEvaluator;
481            return newPVH;
482        } catch (CloneNotSupportedException e) {
483            // won't reach here
484            return null;
485        }
486    }
487
488    /**
489     * Internal function to set the value on the target object, using the setter set up
490     * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
491     * to handle turning the value calculated by ValueAnimator into a value set on the object
492     * according to the name of the property.
493     * @param target The target object on which the value is set
494     */
495    void setAnimatedValue(Object target) {
496        if (mSetter != null) {
497            try {
498                mTmpValueArray[0] = getAnimatedValue();
499                mSetter.invoke(target, mTmpValueArray);
500            } catch (InvocationTargetException e) {
501                Log.e("PropertyValuesHolder", e.toString());
502            } catch (IllegalAccessException e) {
503                Log.e("PropertyValuesHolder", e.toString());
504            }
505        }
506    }
507
508    /**
509     * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
510     * to calculate animated values.
511     */
512    void init() {
513        if (mEvaluator == null) {
514            // We already handle int, float, long, double automatically, but not their Object
515            // equivalents
516            mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
517                    (mValueType == Float.class) ? sFloatEvaluator :
518                    null;
519        }
520        if (mEvaluator != null) {
521            // KeyframeSet knows how to evaluate the common types - only give it a custom
522            // evaulator if one has been set on this class
523            mKeyframeSet.setEvaluator(mEvaluator);
524        }
525    }
526
527    /**
528     * The TypeEvaluator will the automatically determined based on the type of values
529     * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
530     * desired. This may be important in cases where either the type of the values supplied
531     * do not match the way that they should be interpolated between, or if the values
532     * are of a custom type or one not currently understood by the animation system. Currently,
533     * only values of type float, double, and int (and their Object equivalents, Float, Double,
534     * and Integer) are  correctly interpolated; all other types require setting a TypeEvaluator.
535     * @param evaluator
536     */
537    public void setEvaluator(TypeEvaluator evaluator) {
538        mEvaluator = evaluator;
539        mKeyframeSet.setEvaluator(evaluator);
540    }
541
542    /**
543     * Function used to calculate the value according to the evaluator set up for
544     * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
545     *
546     * @param fraction The elapsed, interpolated fraction of the animation.
547     */
548    void calculateValue(float fraction) {
549        mAnimatedValue = mKeyframeSet.getValue(fraction);
550    }
551
552    /**
553     * Sets the <code>Method</code> that is called with the animated values calculated
554     * during the animation. Setting the setter method is an alternative to supplying a
555     * {@link #setPropertyName(String) propertyName} from which the method is derived. This
556     * approach is more direct, and is especially useful when a function must be called that does
557     * not correspond to the convention of <code>setName()</code>. For example, if a function
558     * called <code>offset()</code> is to be called with the animated values, there is no way
559     * to tell <code>ObjectAnimator</code> how to call that function simply through a property
560     * name, so a setter method should be supplied instead.
561     *
562     * <p>Note that the setter function must take the same parameter type as the
563     * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
564     * the setter function will fail.</p>
565     *
566     * @param setter The setter method that should be called with the animated values.
567     */
568    public void setSetter(Method setter) {
569        mSetter = setter;
570    }
571
572    /**
573     * Gets the <code>Method</code> that is called with the animated values calculated
574     * during the animation.
575     */
576    public Method getSetter() {
577        return mSetter;
578    }
579
580    /**
581     * Sets the <code>Method</code> that is called to get unsupplied <code>valueFrom</code> or
582     * <code>valueTo</code> properties. Setting the getter method is an alternative to supplying a
583     * {@link #setPropertyName(String) propertyName} from which the method is derived. This
584     * approach is more direct, and is especially useful when a function must be called that does
585     * not correspond to the convention of <code>setName()</code>. For example, if a function
586     * called <code>offset()</code> is to be called to get an initial value, there is no way
587     * to tell <code>ObjectAnimator</code> how to call that function simply through a property
588     * name, so a getter method should be supplied instead.
589     *
590     * <p>Note that the getter method is only called whether supplied here or derived
591     * from the property name, if one of <code>valueFrom</code> or <code>valueTo</code> are
592     * null. If both of those values are non-null, then there is no need to get one of the
593     * values and the getter is not called.
594     *
595     * <p>Note that the getter function must return the same parameter type as the
596     * <code>valueFrom</code> and <code>valueTo</code> properties (whichever of them are
597     * non-null), otherwise the call to the getter function will fail.</p>
598     *
599     * @param getter The getter method that should be called to get initial animation values.
600     */
601    public void setGetter(Method getter) {
602        mGetter = getter;
603    }
604
605    /**
606     * Gets the <code>Method</code> that is called to get unsupplied <code>valueFrom</code> or
607     * <code>valueTo</code> properties.
608     */
609    public Method getGetter() {
610        return mGetter;
611    }
612
613    /**
614     * Sets the name of the property that will be animated. This name is used to derive
615     * a setter function that will be called to set animated values.
616     * For example, a property name of <code>foo</code> will result
617     * in a call to the function <code>setFoo()</code> on the target object. If either
618     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
619     * also be derived and called.
620     *
621     * <p>Note that the setter function derived from this property name
622     * must take the same parameter type as the
623     * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
624     * the setter function will fail.</p>
625     *
626     * @param propertyName The name of the property being animated.
627     */
628    public void setPropertyName(String propertyName) {
629        mPropertyName = propertyName;
630    }
631
632    /**
633     * Gets the name of the property that will be animated. This name will be used to derive
634     * a setter function that will be called to set animated values.
635     * For example, a property name of <code>foo</code> will result
636     * in a call to the function <code>setFoo()</code> on the target object. If either
637     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
638     * also be derived and called.
639     */
640    public String getPropertyName() {
641        return mPropertyName;
642    }
643
644    /**
645     * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
646     * most recently calculated in calculateValue().
647     * @return
648     */
649    Object getAnimatedValue() {
650        return mAnimatedValue;
651    }
652
653    static class IntPropertyValuesHolder extends PropertyValuesHolder {
654
655        IntKeyframeSet mIntKeyframeSet;
656        int mIntAnimatedValue;
657
658        public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) {
659            super(propertyName);
660            mValueType = int.class;
661            mKeyframeSet = keyframeSet;
662            mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
663        }
664
665        public IntPropertyValuesHolder(String propertyName, int... values) {
666            super(propertyName);
667            setIntValues(values);
668        }
669
670        @Override
671        public void setIntValues(int... values) {
672            super.setIntValues(values);
673            mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
674        }
675
676        @Override
677        void calculateValue(float fraction) {
678            mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction);
679        }
680
681        @Override
682        Object getAnimatedValue() {
683            return mIntAnimatedValue;
684        }
685
686        @Override
687        public IntPropertyValuesHolder clone() {
688            IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
689            newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet;
690            return newPVH;
691        }
692
693        /**
694         * Internal function to set the value on the target object, using the setter set up
695         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
696         * to handle turning the value calculated by ValueAnimator into a value set on the object
697         * according to the name of the property.
698         * @param target The target object on which the value is set
699         */
700        @Override
701        void setAnimatedValue(Object target) {
702            if (mSetter != null) {
703                try {
704                    mTmpValueArray[0] = mIntAnimatedValue;
705                    mSetter.invoke(target, mTmpValueArray);
706                } catch (InvocationTargetException e) {
707                    Log.e("PropertyValuesHolder", e.toString());
708                } catch (IllegalAccessException e) {
709                    Log.e("PropertyValuesHolder", e.toString());
710                }
711            }
712        }
713    }
714
715    static class FloatPropertyValuesHolder extends PropertyValuesHolder {
716
717        FloatKeyframeSet mFloatKeyframeSet;
718        float mFloatAnimatedValue;
719
720        public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
721            super(propertyName);
722            mValueType = float.class;
723            mKeyframeSet = keyframeSet;
724            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
725        }
726
727        public FloatPropertyValuesHolder(String propertyName, float... values) {
728            super(propertyName);
729            setFloatValues(values);
730        }
731
732        @Override
733        public void setFloatValues(float... values) {
734            super.setFloatValues(values);
735            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
736        }
737
738        @Override
739        void calculateValue(float fraction) {
740            mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
741        }
742
743        @Override
744        Object getAnimatedValue() {
745            return mFloatAnimatedValue;
746        }
747
748        @Override
749        public FloatPropertyValuesHolder clone() {
750            FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
751            newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet;
752            return newPVH;
753        }
754
755        /**
756         * Internal function to set the value on the target object, using the setter set up
757         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
758         * to handle turning the value calculated by ValueAnimator into a value set on the object
759         * according to the name of the property.
760         * @param target The target object on which the value is set
761         */
762        @Override
763        void setAnimatedValue(Object target) {
764            if (mSetter != null) {
765                try {
766                    mTmpValueArray[0] = mFloatAnimatedValue;
767                    mSetter.invoke(target, mTmpValueArray);
768                } catch (InvocationTargetException e) {
769                    Log.e("PropertyValuesHolder", e.toString());
770                } catch (IllegalAccessException e) {
771                    Log.e("PropertyValuesHolder", e.toString());
772                }
773            }
774        }
775
776    }
777}