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