2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.actionbarsherlock.internal.nineoldandroids.animation;
19 //import android.util.FloatProperty;
20 //import android.util.IntProperty;
21 import android.util.Log;
22 //import android.util.Property;
24 import java.lang.reflect.InvocationTargetException;
25 import java.lang.reflect.Method;
26 import java.util.HashMap;
27 import java.util.concurrent.locks.ReentrantReadWriteLock;
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
35 @SuppressWarnings({"rawtypes", "unchecked"})
36 public class PropertyValuesHolder implements Cloneable {
39 * The name of the property associated with the values. This need not be a real property,
40 * unless this object is being used with ObjectAnimator. But this is the name by which
41 * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator.
48 //protected Property mProperty;
51 * The setter function, if needed. ObjectAnimator hands off this functionality to
52 * PropertyValuesHolder, since it holds all of the per-property information. This
53 * property is automatically
54 * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
56 Method mSetter = null;
59 * The getter function, if needed. ObjectAnimator hands off this functionality to
60 * PropertyValuesHolder, since it holds all of the per-property information. This
61 * property is automatically
62 * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
63 * The getter is only derived and used if one of the values is null.
65 private Method mGetter = null;
68 * The type of values supplied. This information is used both in deriving the setter/getter
69 * functions and in deriving the type of TypeEvaluator.
74 * The set of keyframes (time/value pairs) that define this animation.
76 KeyframeSet mKeyframeSet = null;
79 // type evaluators for the primitive types handled by this implementation
80 private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
81 private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
83 // We try several different types when searching for appropriate setter/getter functions.
84 // The caller may have supplied values in a type that does not match the setter/getter
85 // functions (such as the integers 0 and 1 to represent floating point values for alpha).
86 // Also, the use of generics in constructors means that we end up with the Object versions
87 // of primitive types (Float vs. float). But most likely, the setter/getter functions
88 // will take primitive types instead.
89 // So we supply an ordered array of other types to try before giving up.
90 private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
91 Double.class, Integer.class};
92 private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
93 Float.class, Double.class};
94 private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class,
95 Float.class, Integer.class};
97 // These maps hold all property entries for a particular class. This map
98 // is used to speed up property/setter/getter lookups for a given class/property
99 // combination. No need to use reflection on the combination more than once.
100 private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap =
101 new HashMap<Class, HashMap<String, Method>>();
102 private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap =
103 new HashMap<Class, HashMap<String, Method>>();
105 // This lock is used to ensure that only one thread is accessing the property maps
107 final ReentrantReadWriteLock mPropertyMapLock = new ReentrantReadWriteLock();
109 // Used to pass single value to varargs parameter in setter invocation
110 final Object[] mTmpValueArray = new Object[1];
113 * The type evaluator used to calculate the animated values. This evaluator is determined
114 * automatically based on the type of the start/end objects passed into the constructor,
115 * but the system only knows about the primitive types int and float. Any other
116 * type will need to set the evaluator to a custom evaluator for that type.
118 private TypeEvaluator mEvaluator;
121 * The value most recently calculated by calculateValue(). This is set during
122 * that function and might be retrieved later either by ValueAnimator.animatedValue() or
123 * by the property-setting logic in ObjectAnimator.animatedValue().
125 private Object mAnimatedValue;
128 * Internal utility constructor, used by the factory methods to set the property name.
129 * @param propertyName The name of the property for this holder.
131 private PropertyValuesHolder(String propertyName) {
132 mPropertyName = propertyName;
136 * Internal utility constructor, used by the factory methods to set the property.
137 * @param property The property for this holder.
139 //private PropertyValuesHolder(Property property) {
140 // mProperty = property;
141 // if (property != null) {
142 // mPropertyName = property.getName();
147 * Constructs and returns a PropertyValuesHolder with a given property name and
149 * @param propertyName The name of the property being animated.
150 * @param values The values that the named property will animate between.
151 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
153 public static PropertyValuesHolder ofInt(String propertyName, int... values) {
154 return new IntPropertyValuesHolder(propertyName, values);
158 * Constructs and returns a PropertyValuesHolder with a given property and
160 * @param property The property being animated. Should not be null.
161 * @param values The values that the property will animate between.
162 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
164 //public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
165 // return new IntPropertyValuesHolder(property, values);
169 * Constructs and returns a PropertyValuesHolder with a given property name and
170 * set of float values.
171 * @param propertyName The name of the property being animated.
172 * @param values The values that the named property will animate between.
173 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
175 public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
176 return new FloatPropertyValuesHolder(propertyName, values);
180 * Constructs and returns a PropertyValuesHolder with a given property and
181 * set of float values.
182 * @param property The property being animated. Should not be null.
183 * @param values The values that the property will animate between.
184 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
186 //public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
187 // return new FloatPropertyValuesHolder(property, values);
191 * Constructs and returns a PropertyValuesHolder with a given property name and
192 * set of Object values. This variant also takes a TypeEvaluator because the system
193 * cannot automatically interpolate between objects of unknown type.
195 * @param propertyName The name of the property being animated.
196 * @param evaluator A TypeEvaluator that will be called on each animation frame to
197 * provide the necessary interpolation between the Object values to derive the animated
199 * @param values The values that the named property will animate between.
200 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
202 public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
204 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
205 pvh.setObjectValues(values);
206 pvh.setEvaluator(evaluator);
211 * Constructs and returns a PropertyValuesHolder with a given property and
212 * set of Object values. This variant also takes a TypeEvaluator because the system
213 * cannot automatically interpolate between objects of unknown type.
215 * @param property The property being animated. Should not be null.
216 * @param evaluator A TypeEvaluator that will be called on each animation frame to
217 * provide the necessary interpolation between the Object values to derive the animated
219 * @param values The values that the property will animate between.
220 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
222 //public static <V> PropertyValuesHolder ofObject(Property property,
223 // TypeEvaluator<V> evaluator, V... values) {
224 // PropertyValuesHolder pvh = new PropertyValuesHolder(property);
225 // pvh.setObjectValues(values);
226 // pvh.setEvaluator(evaluator);
231 * Constructs and returns a PropertyValuesHolder object with the specified property name and set
232 * of values. These values can be of any type, but the type should be consistent so that
233 * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
235 * <p>If there is only one value, it is assumed to be the end value of an animation,
236 * and an initial value will be derived, if possible, by calling a getter function
237 * on the object. Also, if any value is null, the value will be filled in when the animation
238 * starts in the same way. This mechanism of automatically getting null values only works
239 * if the PropertyValuesHolder object is used in conjunction
240 * {@link ObjectAnimator}, and with a getter function
241 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
242 * no way of determining what the value should be.
243 * @param propertyName The name of the property associated with this set of values. This
244 * can be the actual property name to be used when using a ObjectAnimator object, or
245 * just a name used to get animated values, such as if this object is used with an
246 * ValueAnimator object.
247 * @param values The set of values to animate between.
249 public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
250 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
251 if (keyframeSet instanceof IntKeyframeSet) {
252 return new IntPropertyValuesHolder(propertyName, (IntKeyframeSet) keyframeSet);
253 } else if (keyframeSet instanceof FloatKeyframeSet) {
254 return new FloatPropertyValuesHolder(propertyName, (FloatKeyframeSet) keyframeSet);
257 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
258 pvh.mKeyframeSet = keyframeSet;
259 pvh.mValueType = values[0].getType();
265 * Constructs and returns a PropertyValuesHolder object with the specified property and set
266 * of values. These values can be of any type, but the type should be consistent so that
267 * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
269 * <p>If there is only one value, it is assumed to be the end value of an animation,
270 * and an initial value will be derived, if possible, by calling the property's
271 * {@link android.util.Property#get(Object)} function.
272 * Also, if any value is null, the value will be filled in when the animation
273 * starts in the same way. This mechanism of automatically getting null values only works
274 * if the PropertyValuesHolder object is used in conjunction with
275 * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has
276 * no way of determining what the value should be.
277 * @param property The property associated with this set of values. Should not be null.
278 * @param values The set of values to animate between.
280 //public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
281 // KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
282 // if (keyframeSet instanceof IntKeyframeSet) {
283 // return new IntPropertyValuesHolder(property, (IntKeyframeSet) keyframeSet);
284 // } else if (keyframeSet instanceof FloatKeyframeSet) {
285 // return new FloatPropertyValuesHolder(property, (FloatKeyframeSet) keyframeSet);
288 // PropertyValuesHolder pvh = new PropertyValuesHolder(property);
289 // pvh.mKeyframeSet = keyframeSet;
290 // pvh.mValueType = ((Keyframe)values[0]).getType();
296 * Set the animated values for this object to this set of ints.
297 * If there is only one value, it is assumed to be the end value of an animation,
298 * and an initial value will be derived, if possible, by calling a getter function
299 * on the object. Also, if any value is null, the value will be filled in when the animation
300 * starts in the same way. This mechanism of automatically getting null values only works
301 * if the PropertyValuesHolder object is used in conjunction
302 * {@link ObjectAnimator}, and with a getter function
303 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
304 * no way of determining what the value should be.
306 * @param values One or more values that the animation will animate between.
308 public void setIntValues(int... values) {
309 mValueType = int.class;
310 mKeyframeSet = KeyframeSet.ofInt(values);
314 * Set the animated values for this object to this set of floats.
315 * If there is only one value, it is assumed to be the end value of an animation,
316 * and an initial value will be derived, if possible, by calling a getter function
317 * on the object. Also, if any value is null, the value will be filled in when the animation
318 * starts in the same way. This mechanism of automatically getting null values only works
319 * if the PropertyValuesHolder object is used in conjunction
320 * {@link ObjectAnimator}, and with a getter function
321 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
322 * no way of determining what the value should be.
324 * @param values One or more values that the animation will animate between.
326 public void setFloatValues(float... values) {
327 mValueType = float.class;
328 mKeyframeSet = KeyframeSet.ofFloat(values);
332 * Set the animated values for this object to this set of Keyframes.
334 * @param values One or more values that the animation will animate between.
336 public void setKeyframes(Keyframe... values) {
337 int numKeyframes = values.length;
338 Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
339 mValueType = values[0].getType();
340 for (int i = 0; i < numKeyframes; ++i) {
341 keyframes[i] = values[i];
343 mKeyframeSet = new KeyframeSet(keyframes);
347 * Set the animated values for this object to this set of Objects.
348 * If there is only one value, it is assumed to be the end value of an animation,
349 * and an initial value will be derived, if possible, by calling a getter function
350 * on the object. Also, if any value is null, the value will be filled in when the animation
351 * starts in the same way. This mechanism of automatically getting null values only works
352 * if the PropertyValuesHolder object is used in conjunction
353 * {@link ObjectAnimator}, and with a getter function
354 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
355 * no way of determining what the value should be.
357 * @param values One or more values that the animation will animate between.
359 public void setObjectValues(Object... values) {
360 mValueType = values[0].getClass();
361 mKeyframeSet = KeyframeSet.ofObject(values);
365 * Determine the setter or getter function using the JavaBeans convention of setFoo or
366 * getFoo for a property named 'foo'. This function figures out what the name of the
367 * function should be and uses reflection to find the Method with that name on the
370 * @param targetClass The class to search for the method
371 * @param prefix "set" or "get", depending on whether we need a setter or getter.
372 * @param valueType The type of the parameter (in the case of a setter). This type
373 * is derived from the values set on this PropertyValuesHolder. This type is used as
374 * a first guess at the parameter type, but we check for methods with several different
375 * types to avoid problems with slight mis-matches between supplied values and actual
376 * value types used on the setter.
377 * @return Method the method associated with mPropertyName.
379 private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
380 // TODO: faster implementation...
381 Method returnVal = null;
382 String methodName = getMethodName(prefix, mPropertyName);
384 if (valueType == null) {
386 returnVal = targetClass.getMethod(methodName, args);
387 } catch (NoSuchMethodException e) {
388 Log.e("PropertyValuesHolder", targetClass.getSimpleName() + " - " +
389 "Couldn't find no-arg method for property " + mPropertyName + ": " + e);
393 Class typeVariants[];
394 if (mValueType.equals(Float.class)) {
395 typeVariants = FLOAT_VARIANTS;
396 } else if (mValueType.equals(Integer.class)) {
397 typeVariants = INTEGER_VARIANTS;
398 } else if (mValueType.equals(Double.class)) {
399 typeVariants = DOUBLE_VARIANTS;
401 typeVariants = new Class[1];
402 typeVariants[0] = mValueType;
404 for (Class typeVariant : typeVariants) {
405 args[0] = typeVariant;
407 returnVal = targetClass.getMethod(methodName, args);
408 // change the value type to suit
409 mValueType = typeVariant;
411 } catch (NoSuchMethodException e) {
412 // Swallow the error and keep trying other variants
415 // If we got here, then no appropriate function was found
416 Log.e("PropertyValuesHolder",
417 "Couldn't find " + prefix + "ter property " + mPropertyName +
418 " for " + targetClass.getSimpleName() +
419 " with value type "+ mValueType);
427 * Returns the setter or getter requested. This utility function checks whether the
428 * requested method exists in the propertyMapMap cache. If not, it calls another
429 * utility function to request the Method from the targetClass directly.
430 * @param targetClass The Class on which the requested method should exist.
431 * @param propertyMapMap The cache of setters/getters derived so far.
432 * @param prefix "set" or "get", for the setter or getter.
433 * @param valueType The type of parameter passed into the method (null for getter).
434 * @return Method the method associated with mPropertyName.
436 private Method setupSetterOrGetter(Class targetClass,
437 HashMap<Class, HashMap<String, Method>> propertyMapMap,
438 String prefix, Class valueType) {
439 Method setterOrGetter = null;
441 // Have to lock property map prior to reading it, to guard against
442 // another thread putting something in there after we've checked it
443 // but before we've added an entry to it
444 mPropertyMapLock.writeLock().lock();
445 HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
446 if (propertyMap != null) {
447 setterOrGetter = propertyMap.get(mPropertyName);
449 if (setterOrGetter == null) {
450 setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
451 if (propertyMap == null) {
452 propertyMap = new HashMap<String, Method>();
453 propertyMapMap.put(targetClass, propertyMap);
455 propertyMap.put(mPropertyName, setterOrGetter);
458 mPropertyMapLock.writeLock().unlock();
460 return setterOrGetter;
464 * Utility function to get the setter from targetClass
465 * @param targetClass The Class on which the requested method should exist.
467 void setupSetter(Class targetClass) {
468 mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);
472 * Utility function to get the getter from targetClass
474 private void setupGetter(Class targetClass) {
475 mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
479 * Internal function (called from ObjectAnimator) to set up the setter and getter
480 * prior to running the animation. If the setter has not been manually set for this
481 * object, it will be derived automatically given the property name, target object, and
482 * types of values supplied. If no getter has been set, it will be supplied iff any of the
483 * supplied values was null. If there is a null value, then the getter (supplied or derived)
484 * will be called to set those null values to the current value of the property
485 * on the target object.
486 * @param target The object on which the setter (and possibly getter) exist.
488 void setupSetterAndGetter(Object target) {
489 //if (mProperty != null) {
490 // // check to make sure that mProperty is on the class of target
492 // Object testValue = mProperty.get(target);
493 // for (Keyframe kf : mKeyframeSet.mKeyframes) {
494 // if (!kf.hasValue()) {
495 // kf.setValue(mProperty.get(target));
499 // } catch (ClassCastException e) {
500 // Log.e("PropertyValuesHolder","No such property (" + mProperty.getName() +
501 // ") on target object " + target + ". Trying reflection instead");
505 Class targetClass = target.getClass();
506 if (mSetter == null) {
507 setupSetter(targetClass);
509 for (Keyframe kf : mKeyframeSet.mKeyframes) {
510 if (!kf.hasValue()) {
511 if (mGetter == null) {
512 setupGetter(targetClass);
515 kf.setValue(mGetter.invoke(target));
516 } catch (InvocationTargetException e) {
517 Log.e("PropertyValuesHolder", e.toString());
518 } catch (IllegalAccessException e) {
519 Log.e("PropertyValuesHolder", e.toString());
526 * Utility function to set the value stored in a particular Keyframe. The value used is
527 * whatever the value is for the property name specified in the keyframe on the target object.
529 * @param target The target object from which the current value should be extracted.
530 * @param kf The keyframe which holds the property name and value.
532 private void setupValue(Object target, Keyframe kf) {
533 //if (mProperty != null) {
534 // kf.setValue(mProperty.get(target));
537 if (mGetter == null) {
538 Class targetClass = target.getClass();
539 setupGetter(targetClass);
541 kf.setValue(mGetter.invoke(target));
542 } catch (InvocationTargetException e) {
543 Log.e("PropertyValuesHolder", e.toString());
544 } catch (IllegalAccessException e) {
545 Log.e("PropertyValuesHolder", e.toString());
550 * This function is called by ObjectAnimator when setting the start values for an animation.
551 * The start values are set according to the current values in the target object. The
552 * property whose value is extracted is whatever is specified by the propertyName of this
553 * PropertyValuesHolder object.
555 * @param target The object which holds the start values that should be set.
557 void setupStartValue(Object target) {
558 setupValue(target, mKeyframeSet.mKeyframes.get(0));
562 * This function is called by ObjectAnimator when setting the end values for an animation.
563 * The end values are set according to the current values in the target object. The
564 * property whose value is extracted is whatever is specified by the propertyName of this
565 * PropertyValuesHolder object.
567 * @param target The object which holds the start values that should be set.
569 void setupEndValue(Object target) {
570 setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
574 public PropertyValuesHolder clone() {
576 PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
577 newPVH.mPropertyName = mPropertyName;
578 //newPVH.mProperty = mProperty;
579 newPVH.mKeyframeSet = mKeyframeSet.clone();
580 newPVH.mEvaluator = mEvaluator;
582 } catch (CloneNotSupportedException e) {
589 * Internal function to set the value on the target object, using the setter set up
590 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
591 * to handle turning the value calculated by ValueAnimator into a value set on the object
592 * according to the name of the property.
593 * @param target The target object on which the value is set
595 void setAnimatedValue(Object target) {
596 //if (mProperty != null) {
597 // mProperty.set(target, getAnimatedValue());
599 if (mSetter != null) {
601 mTmpValueArray[0] = getAnimatedValue();
602 mSetter.invoke(target, mTmpValueArray);
603 } catch (InvocationTargetException e) {
604 Log.e("PropertyValuesHolder", e.toString());
605 } catch (IllegalAccessException e) {
606 Log.e("PropertyValuesHolder", e.toString());
612 * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
613 * to calculate animated values.
616 if (mEvaluator == null) {
617 // We already handle int and float automatically, but not their Object
619 mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
620 (mValueType == Float.class) ? sFloatEvaluator :
623 if (mEvaluator != null) {
624 // KeyframeSet knows how to evaluate the common types - only give it a custom
625 // evaluator if one has been set on this class
626 mKeyframeSet.setEvaluator(mEvaluator);
631 * The TypeEvaluator will the automatically determined based on the type of values
632 * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
633 * desired. This may be important in cases where either the type of the values supplied
634 * do not match the way that they should be interpolated between, or if the values
635 * are of a custom type or one not currently understood by the animation system. Currently,
636 * only values of type float and int (and their Object equivalents: Float
637 * and Integer) are correctly interpolated; all other types require setting a TypeEvaluator.
640 public void setEvaluator(TypeEvaluator evaluator) {
641 mEvaluator = evaluator;
642 mKeyframeSet.setEvaluator(evaluator);
646 * Function used to calculate the value according to the evaluator set up for
647 * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
649 * @param fraction The elapsed, interpolated fraction of the animation.
651 void calculateValue(float fraction) {
652 mAnimatedValue = mKeyframeSet.getValue(fraction);
656 * Sets the name of the property that will be animated. This name is used to derive
657 * a setter function that will be called to set animated values.
658 * For example, a property name of <code>foo</code> will result
659 * in a call to the function <code>setFoo()</code> on the target object. If either
660 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
661 * also be derived and called.
663 * <p>Note that the setter function derived from this property name
664 * must take the same parameter type as the
665 * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
666 * the setter function will fail.</p>
668 * @param propertyName The name of the property being animated.
670 public void setPropertyName(String propertyName) {
671 mPropertyName = propertyName;
675 * Sets the property that will be animated.
677 * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
678 * must exist on the target object specified in that ObjectAnimator.</p>
680 * @param property The property being animated.
682 //public void setProperty(Property property) {
683 // mProperty = property;
687 * Gets the name of the property that will be animated. This name will be used to derive
688 * a setter function that will be called to set animated values.
689 * For example, a property name of <code>foo</code> will result
690 * in a call to the function <code>setFoo()</code> on the target object. If either
691 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
692 * also be derived and called.
694 public String getPropertyName() {
695 return mPropertyName;
699 * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
700 * most recently calculated in calculateValue().
703 Object getAnimatedValue() {
704 return mAnimatedValue;
708 public String toString() {
709 return mPropertyName + ": " + mKeyframeSet.toString();
713 * Utility method to derive a setter/getter method name from a property name, where the
714 * prefix is typically "set" or "get" and the first letter of the property name is
717 * @param prefix The precursor to the method name, before the property name begins, typically
719 * @param propertyName The name of the property that represents the bulk of the method name
720 * after the prefix. The first letter of this word will be capitalized in the resulting
722 * @return String the property name converted to a method name according to the conventions
725 static String getMethodName(String prefix, String propertyName) {
726 if (propertyName == null || propertyName.length() == 0) {
727 // shouldn't get here
730 char firstLetter = Character.toUpperCase(propertyName.charAt(0));
731 String theRest = propertyName.substring(1);
732 return prefix + firstLetter + theRest;
735 static class IntPropertyValuesHolder extends PropertyValuesHolder {
737 // Cache JNI functions to avoid looking them up twice
738 //private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
739 // new HashMap<Class, HashMap<String, Integer>>();
741 //private IntProperty mIntProperty;
743 IntKeyframeSet mIntKeyframeSet;
744 int mIntAnimatedValue;
746 public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) {
748 mValueType = int.class;
749 mKeyframeSet = keyframeSet;
750 mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
753 //public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) {
755 // mValueType = int.class;
756 // mKeyframeSet = keyframeSet;
757 // mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
758 // if (property instanceof IntProperty) {
759 // mIntProperty = (IntProperty) mProperty;
763 public IntPropertyValuesHolder(String propertyName, int... values) {
765 setIntValues(values);
768 //public IntPropertyValuesHolder(Property property, int... values) {
770 // setIntValues(values);
771 // if (property instanceof IntProperty) {
772 // mIntProperty = (IntProperty) mProperty;
777 public void setIntValues(int... values) {
778 super.setIntValues(values);
779 mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
783 void calculateValue(float fraction) {
784 mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction);
788 Object getAnimatedValue() {
789 return mIntAnimatedValue;
793 public IntPropertyValuesHolder clone() {
794 IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
795 newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet;
800 * Internal function to set the value on the target object, using the setter set up
801 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
802 * to handle turning the value calculated by ValueAnimator into a value set on the object
803 * according to the name of the property.
804 * @param target The target object on which the value is set
807 void setAnimatedValue(Object target) {
808 //if (mIntProperty != null) {
809 // mIntProperty.setValue(target, mIntAnimatedValue);
812 //if (mProperty != null) {
813 // mProperty.set(target, mIntAnimatedValue);
816 //if (mJniSetter != 0) {
817 // nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
820 if (mSetter != null) {
822 mTmpValueArray[0] = mIntAnimatedValue;
823 mSetter.invoke(target, mTmpValueArray);
824 } catch (InvocationTargetException e) {
825 Log.e("PropertyValuesHolder", e.toString());
826 } catch (IllegalAccessException e) {
827 Log.e("PropertyValuesHolder", e.toString());
833 void setupSetter(Class targetClass) {
834 //if (mProperty != null) {
837 // Check new static hashmap<propName, int> for setter method
839 // mPropertyMapLock.writeLock().lock();
840 // HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
841 // if (propertyMap != null) {
842 // Integer mJniSetterInteger = propertyMap.get(mPropertyName);
843 // if (mJniSetterInteger != null) {
844 // mJniSetter = mJniSetterInteger;
847 // if (mJniSetter == 0) {
848 // String methodName = getMethodName("set", mPropertyName);
849 // mJniSetter = nGetIntMethod(targetClass, methodName);
850 // if (mJniSetter != 0) {
851 // if (propertyMap == null) {
852 // propertyMap = new HashMap<String, Integer>();
853 // sJNISetterPropertyMap.put(targetClass, propertyMap);
855 // propertyMap.put(mPropertyName, mJniSetter);
858 //} catch (NoSuchMethodError e) {
859 // Log.d("PropertyValuesHolder",
860 // "Can't find native method using JNI, use reflection" + e);
862 // mPropertyMapLock.writeLock().unlock();
864 //if (mJniSetter == 0) {
865 // Couldn't find method through fast JNI approach - just use reflection
866 super.setupSetter(targetClass);
871 static class FloatPropertyValuesHolder extends PropertyValuesHolder {
873 // Cache JNI functions to avoid looking them up twice
874 //private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
875 // new HashMap<Class, HashMap<String, Integer>>();
877 //private FloatProperty mFloatProperty;
879 FloatKeyframeSet mFloatKeyframeSet;
880 float mFloatAnimatedValue;
882 public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
884 mValueType = float.class;
885 mKeyframeSet = keyframeSet;
886 mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
889 //public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) {
891 // mValueType = float.class;
892 // mKeyframeSet = keyframeSet;
893 // mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
894 // if (property instanceof FloatProperty) {
895 // mFloatProperty = (FloatProperty) mProperty;
899 public FloatPropertyValuesHolder(String propertyName, float... values) {
901 setFloatValues(values);
904 //public FloatPropertyValuesHolder(Property property, float... values) {
906 // setFloatValues(values);
907 // if (property instanceof FloatProperty) {
908 // mFloatProperty = (FloatProperty) mProperty;
913 public void setFloatValues(float... values) {
914 super.setFloatValues(values);
915 mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
919 void calculateValue(float fraction) {
920 mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
924 Object getAnimatedValue() {
925 return mFloatAnimatedValue;
929 public FloatPropertyValuesHolder clone() {
930 FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
931 newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet;
936 * Internal function to set the value on the target object, using the setter set up
937 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
938 * to handle turning the value calculated by ValueAnimator into a value set on the object
939 * according to the name of the property.
940 * @param target The target object on which the value is set
943 void setAnimatedValue(Object target) {
944 //if (mFloatProperty != null) {
945 // mFloatProperty.setValue(target, mFloatAnimatedValue);
948 //if (mProperty != null) {
949 // mProperty.set(target, mFloatAnimatedValue);
952 //if (mJniSetter != 0) {
953 // nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
956 if (mSetter != null) {
958 mTmpValueArray[0] = mFloatAnimatedValue;
959 mSetter.invoke(target, mTmpValueArray);
960 } catch (InvocationTargetException e) {
961 Log.e("PropertyValuesHolder", e.toString());
962 } catch (IllegalAccessException e) {
963 Log.e("PropertyValuesHolder", e.toString());
969 void setupSetter(Class targetClass) {
970 //if (mProperty != null) {
973 // Check new static hashmap<propName, int> for setter method
975 // mPropertyMapLock.writeLock().lock();
976 // HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
977 // if (propertyMap != null) {
978 // Integer mJniSetterInteger = propertyMap.get(mPropertyName);
979 // if (mJniSetterInteger != null) {
980 // mJniSetter = mJniSetterInteger;
983 // if (mJniSetter == 0) {
984 // String methodName = getMethodName("set", mPropertyName);
985 // mJniSetter = nGetFloatMethod(targetClass, methodName);
986 // if (mJniSetter != 0) {
987 // if (propertyMap == null) {
988 // propertyMap = new HashMap<String, Integer>();
989 // sJNISetterPropertyMap.put(targetClass, propertyMap);
991 // propertyMap.put(mPropertyName, mJniSetter);
994 //} catch (NoSuchMethodError e) {
995 // Log.d("PropertyValuesHolder",
996 // "Can't find native method using JNI, use reflection" + e);
998 // mPropertyMapLock.writeLock().unlock();
1000 //if (mJniSetter == 0) {
1001 // Couldn't find method through fast JNI approach - just use reflection
1002 super.setupSetter(targetClass);
1008 //native static private int nGetIntMethod(Class targetClass, String methodName);
1009 //native static private int nGetFloatMethod(Class targetClass, String methodName);
1010 //native static private void nCallIntMethod(Object target, int methodID, int arg);
1011 //native static private void nCallFloatMethod(Object target, int methodID, float arg);