blob: 21f6eda8616d78c810962218e6d26e16d7b42f05 [file] [log] [blame]
Chet Haased953d082010-08-16 17:44:28 -07001/*
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
Chet Haaseb39f0512011-05-24 14:36:40 -070019import android.util.FloatProperty;
20import android.util.IntProperty;
Chet Haased953d082010-08-16 17:44:28 -070021import android.util.Log;
Chet Haaseb39f0512011-05-24 14:36:40 -070022import android.util.Property;
Chet Haased953d082010-08-16 17:44:28 -070023
24import java.lang.reflect.InvocationTargetException;
25import java.lang.reflect.Method;
Chet Haased953d082010-08-16 17:44:28 -070026import java.util.HashMap;
27import java.util.concurrent.locks.ReentrantReadWriteLock;
28
29/**
Chet Haase21cd1382010-09-01 17:42:29 -070030 * 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
Chet Haasea18a86b2010-09-07 13:20:00 -070032 * animations with ValueAnimator or ObjectAnimator that operate on several different properties
Chet Haase21cd1382010-09-01 17:42:29 -070033 * in parallel.
Chet Haased953d082010-08-16 17:44:28 -070034 */
Chet Haase2794eb32010-10-12 16:29:28 -070035public class PropertyValuesHolder implements Cloneable {
Chet Haased953d082010-08-16 17:44:28 -070036
37 /**
38 * The name of the property associated with the values. This need not be a real property,
Chet Haasea18a86b2010-09-07 13:20:00 -070039 * 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.
Chet Haased953d082010-08-16 17:44:28 -070041 */
Chet Haase6e0ecb42010-11-03 19:41:18 -070042 String mPropertyName;
Chet Haased953d082010-08-16 17:44:28 -070043
44 /**
Chet Haaseb39f0512011-05-24 14:36:40 -070045 * @hide
46 */
47 protected Property mProperty;
48
49 /**
Chet Haasea18a86b2010-09-07 13:20:00 -070050 * The setter function, if needed. ObjectAnimator hands off this functionality to
Chet Haased953d082010-08-16 17:44:28 -070051 * PropertyValuesHolder, since it holds all of the per-property information. This
Chet Haase37f74ca2010-12-08 17:56:36 -080052 * property is automatically
Chet Haasea18a86b2010-09-07 13:20:00 -070053 * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
Chet Haased953d082010-08-16 17:44:28 -070054 */
Chet Haase7c608f22010-10-22 17:54:04 -070055 Method mSetter = null;
Chet Haased953d082010-08-16 17:44:28 -070056
57 /**
Chet Haasea18a86b2010-09-07 13:20:00 -070058 * The getter function, if needed. ObjectAnimator hands off this functionality to
Chet Haased953d082010-08-16 17:44:28 -070059 * PropertyValuesHolder, since it holds all of the per-property information. This
Chet Haase37f74ca2010-12-08 17:56:36 -080060 * property is automatically
Chet Haasea18a86b2010-09-07 13:20:00 -070061 * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
Chet Haased953d082010-08-16 17:44:28 -070062 * 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 */
Chet Haase7c608f22010-10-22 17:54:04 -070070 Class mValueType;
Chet Haased953d082010-08-16 17:44:28 -070071
72 /**
73 * The set of keyframes (time/value pairs) that define this animation.
74 */
Chet Haase7c608f22010-10-22 17:54:04 -070075 KeyframeSet mKeyframeSet = null;
Chet Haased953d082010-08-16 17:44:28 -070076
77
Chet Haaseb2ab04f2011-01-16 11:03:22 -080078 // type evaluators for the primitive types handled by this implementation
Chet Haased953d082010-08-16 17:44:28 -070079 private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
80 private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
Chet Haased953d082010-08-16 17:44:28 -070081
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.
Chet Haase6e0ecb42010-11-03 19:41:18 -0700106 final ReentrantReadWriteLock mPropertyMapLock = new ReentrantReadWriteLock();
Chet Haased953d082010-08-16 17:44:28 -0700107
108 // Used to pass single value to varargs parameter in setter invocation
Chet Haase6e0ecb42010-11-03 19:41:18 -0700109 final Object[] mTmpValueArray = new Object[1];
Chet Haased953d082010-08-16 17:44:28 -0700110
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,
Chet Haaseb2ab04f2011-01-16 11:03:22 -0800114 * but the system only knows about the primitive types int and float. Any other
Chet Haased953d082010-08-16 17:44:28 -0700115 * 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
Chet Haasea18a86b2010-09-07 13:20:00 -0700121 * that function and might be retrieved later either by ValueAnimator.animatedValue() or
122 * by the property-setting logic in ObjectAnimator.animatedValue().
Chet Haased953d082010-08-16 17:44:28 -0700123 */
124 private Object mAnimatedValue;
125
126 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700127 * Internal utility constructor, used by the factory methods to set the property name.
128 * @param propertyName The name of the property for this holder.
Chet Haased953d082010-08-16 17:44:28 -0700129 */
Chet Haase2794eb32010-10-12 16:29:28 -0700130 private PropertyValuesHolder(String propertyName) {
131 mPropertyName = propertyName;
Chet Haased953d082010-08-16 17:44:28 -0700132 }
133
134 /**
Chet Haaseb39f0512011-05-24 14:36:40 -0700135 * Internal utility constructor, used by the factory methods to set the property.
136 * @param property The property for this holder.
137 */
138 private PropertyValuesHolder(Property property) {
139 mProperty = property;
140 if (property != null) {
141 mPropertyName = property.getName();
142 }
143 }
144
145 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700146 * 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 */
152 public static PropertyValuesHolder ofInt(String propertyName, int... values) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700153 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 */
163 public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
164 return new IntPropertyValuesHolder(property, values);
Chet Haase2794eb32010-10-12 16:29:28 -0700165 }
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 */
174 public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700175 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 */
185 public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
186 return new FloatPropertyValuesHolder(property, values);
Chet Haase2794eb32010-10-12 16:29:28 -0700187 }
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
Chet Haaseb39f0512011-05-24 14:36:40 -0700192 * cannot automatically interpolate between objects of unknown type.
Chet Haase2794eb32010-10-12 16:29:28 -0700193 *
194 * @param propertyName The name of the property being animated.
195 * @param evaluator A TypeEvaluator that will be called on each animation frame to
Chet Haaseb39f0512011-05-24 14:36:40 -0700196 * provide the necessary interpolation between the Object values to derive the animated
Chet Haase2794eb32010-10-12 16:29:28 -0700197 * value.
198 * @param values The values that the named property will animate between.
199 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
200 */
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 /**
Chet Haaseb39f0512011-05-24 14:36:40 -0700210 * 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 */
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 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700230 * 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
Chet Haased953d082010-08-16 17:44:28 -0700232 * 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
Chet Haase1a8e4042010-12-07 11:29:39 -0800239 * {@link ObjectAnimator}, and with a getter function
240 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
Chet Haased953d082010-08-16 17:44:28 -0700241 * no way of determining what the value should be.
242 * @param propertyName The name of the property associated with this set of values. This
Chet Haasea18a86b2010-09-07 13:20:00 -0700243 * can be the actual property name to be used when using a ObjectAnimator object, or
Chet Haased953d082010-08-16 17:44:28 -0700244 * just a name used to get animated values, such as if this object is used with an
Chet Haasea18a86b2010-09-07 13:20:00 -0700245 * ValueAnimator object.
Chet Haased953d082010-08-16 17:44:28 -0700246 * @param values The set of values to animate between.
247 */
Chet Haase2794eb32010-10-12 16:29:28 -0700248 public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
Chet Haase7c608f22010-10-22 17:54:04 -0700249 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 }
Chet Haased953d082010-08-16 17:44:28 -0700261 }
262
263 /**
Chet Haaseb39f0512011-05-24 14:36:40 -0700264 * 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 */
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 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700295 * Set the animated values for this object to this set of ints.
Chet Haased953d082010-08-16 17:44:28 -0700296 * 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
Chet Haase1a8e4042010-12-07 11:29:39 -0800301 * {@link ObjectAnimator}, and with a getter function
302 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
Chet Haased953d082010-08-16 17:44:28 -0700303 * no way of determining what the value should be.
Chet Haase2794eb32010-10-12 16:29:28 -0700304 *
305 * @param values One or more values that the animation will animate between.
Chet Haased953d082010-08-16 17:44:28 -0700306 */
Chet Haase2794eb32010-10-12 16:29:28 -0700307 public void setIntValues(int... values) {
308 mValueType = int.class;
309 mKeyframeSet = KeyframeSet.ofInt(values);
310 }
Chet Haased953d082010-08-16 17:44:28 -0700311
Chet Haase2794eb32010-10-12 16:29:28 -0700312 /**
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
Chet Haase1a8e4042010-12-07 11:29:39 -0800319 * {@link ObjectAnimator}, and with a getter function
320 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
Chet Haase2794eb32010-10-12 16:29:28 -0700321 * no way of determining what the value should be.
322 *
323 * @param values One or more values that the animation will animate between.
324 */
325 public void setFloatValues(float... values) {
326 mValueType = float.class;
327 mKeyframeSet = KeyframeSet.ofFloat(values);
328 }
329
330 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700331 * 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 */
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];
Chet Haased953d082010-08-16 17:44:28 -0700341 }
342 mKeyframeSet = new KeyframeSet(keyframes);
343 }
344
Chet Haase2794eb32010-10-12 16:29:28 -0700345 /**
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
Chet Haase1a8e4042010-12-07 11:29:39 -0800352 * {@link ObjectAnimator}, and with a getter function
353 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
Chet Haase2794eb32010-10-12 16:29:28 -0700354 * no way of determining what the value should be.
355 *
356 * @param values One or more values that the animation will animate between.
357 */
358 public void setObjectValues(Object... values) {
359 mValueType = values[0].getClass();
360 mKeyframeSet = KeyframeSet.ofObject(values);
361 }
Chet Haased953d082010-08-16 17:44:28 -0700362
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 */
378 private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
379 // TODO: faster implementation...
380 Method returnVal = null;
Chet Haase6e0ecb42010-11-03 19:41:18 -0700381 String methodName = getMethodName(prefix, mPropertyName);
Chet Haased953d082010-08-16 17:44:28 -0700382 Class args[] = null;
383 if (valueType == null) {
384 try {
385 returnVal = targetClass.getMethod(methodName, args);
386 } catch (NoSuchMethodException e) {
Chet Haasedb4101c2012-05-03 14:48:07 -0700387 // Swallow the error, log it later
Chet Haased953d082010-08-16 17:44:28 -0700388 }
389 } else {
390 args = new Class[1];
391 Class typeVariants[];
392 if (mValueType.equals(Float.class)) {
393 typeVariants = FLOAT_VARIANTS;
394 } else if (mValueType.equals(Integer.class)) {
395 typeVariants = INTEGER_VARIANTS;
396 } else if (mValueType.equals(Double.class)) {
397 typeVariants = DOUBLE_VARIANTS;
398 } else {
399 typeVariants = new Class[1];
400 typeVariants[0] = mValueType;
401 }
402 for (Class typeVariant : typeVariants) {
403 args[0] = typeVariant;
404 try {
405 returnVal = targetClass.getMethod(methodName, args);
Chet Haase21cd1382010-09-01 17:42:29 -0700406 // change the value type to suit
407 mValueType = typeVariant;
Chet Haased953d082010-08-16 17:44:28 -0700408 return returnVal;
409 } catch (NoSuchMethodException e) {
410 // Swallow the error and keep trying other variants
411 }
412 }
Chet Haase602e4d32010-08-16 08:57:23 -0700413 // If we got here, then no appropriate function was found
Chet Haasedb4101c2012-05-03 14:48:07 -0700414 }
415
416 if (returnVal == null) {
417 Log.w("PropertyValuesHolder", "Method " +
418 getMethodName(prefix, mPropertyName) + "() with type " + mValueType +
419 " not found on target class " + targetClass);
Chet Haased953d082010-08-16 17:44:28 -0700420 }
Chet Haase602e4d32010-08-16 08:57:23 -0700421
Chet Haased953d082010-08-16 17:44:28 -0700422 return returnVal;
423 }
424
425
426 /**
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.
435 */
436 private Method setupSetterOrGetter(Class targetClass,
437 HashMap<Class, HashMap<String, Method>> propertyMapMap,
438 String prefix, Class valueType) {
439 Method setterOrGetter = null;
440 try {
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
Chet Haase6e0ecb42010-11-03 19:41:18 -0700444 mPropertyMapLock.writeLock().lock();
Chet Haased953d082010-08-16 17:44:28 -0700445 HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
446 if (propertyMap != null) {
447 setterOrGetter = propertyMap.get(mPropertyName);
448 }
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);
454 }
455 propertyMap.put(mPropertyName, setterOrGetter);
456 }
457 } finally {
Chet Haase6e0ecb42010-11-03 19:41:18 -0700458 mPropertyMapLock.writeLock().unlock();
Chet Haased953d082010-08-16 17:44:28 -0700459 }
460 return setterOrGetter;
461 }
462
463 /**
464 * Utility function to get the setter from targetClass
465 * @param targetClass The Class on which the requested method should exist.
466 */
Chet Haase6e0ecb42010-11-03 19:41:18 -0700467 void setupSetter(Class targetClass) {
Chet Haased953d082010-08-16 17:44:28 -0700468 mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);
469 }
470
471 /**
472 * Utility function to get the getter from targetClass
473 */
474 private void setupGetter(Class targetClass) {
475 mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
476 }
477
478 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700479 * Internal function (called from ObjectAnimator) to set up the setter and getter
Chet Haased953d082010-08-16 17:44:28 -0700480 * 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.
487 */
488 void setupSetterAndGetter(Object target) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700489 if (mProperty != null) {
490 // check to make sure that mProperty is on the class of target
491 try {
492 Object testValue = mProperty.get(target);
493 for (Keyframe kf : mKeyframeSet.mKeyframes) {
494 if (!kf.hasValue()) {
495 kf.setValue(mProperty.get(target));
496 }
497 }
498 return;
499 } catch (ClassCastException e) {
Chet Haasedb4101c2012-05-03 14:48:07 -0700500 Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
Chet Haaseb39f0512011-05-24 14:36:40 -0700501 ") on target object " + target + ". Trying reflection instead");
502 mProperty = null;
503 }
504 }
Chet Haased953d082010-08-16 17:44:28 -0700505 Class targetClass = target.getClass();
506 if (mSetter == null) {
507 setupSetter(targetClass);
508 }
509 for (Keyframe kf : mKeyframeSet.mKeyframes) {
Chet Haase7c608f22010-10-22 17:54:04 -0700510 if (!kf.hasValue()) {
Chet Haased953d082010-08-16 17:44:28 -0700511 if (mGetter == null) {
512 setupGetter(targetClass);
Chet Haasedb4101c2012-05-03 14:48:07 -0700513 if (mGetter == null) {
514 // Already logged the error - just return to avoid NPE
515 return;
516 }
Chet Haased953d082010-08-16 17:44:28 -0700517 }
518 try {
Chet Haase2794eb32010-10-12 16:29:28 -0700519 kf.setValue(mGetter.invoke(target));
Chet Haased953d082010-08-16 17:44:28 -0700520 } catch (InvocationTargetException e) {
521 Log.e("PropertyValuesHolder", e.toString());
522 } catch (IllegalAccessException e) {
523 Log.e("PropertyValuesHolder", e.toString());
524 }
525 }
526 }
527 }
528
529 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700530 * Utility function to set the value stored in a particular Keyframe. The value used is
531 * whatever the value is for the property name specified in the keyframe on the target object.
532 *
533 * @param target The target object from which the current value should be extracted.
534 * @param kf The keyframe which holds the property name and value.
535 */
536 private void setupValue(Object target, Keyframe kf) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700537 if (mProperty != null) {
538 kf.setValue(mProperty.get(target));
539 }
Chet Haase21cd1382010-09-01 17:42:29 -0700540 try {
541 if (mGetter == null) {
542 Class targetClass = target.getClass();
543 setupGetter(targetClass);
Chet Haasedb4101c2012-05-03 14:48:07 -0700544 if (mGetter == null) {
545 // Already logged the error - just return to avoid NPE
546 return;
547 }
Chet Haase21cd1382010-09-01 17:42:29 -0700548 }
Chet Haase2794eb32010-10-12 16:29:28 -0700549 kf.setValue(mGetter.invoke(target));
Chet Haase21cd1382010-09-01 17:42:29 -0700550 } catch (InvocationTargetException e) {
551 Log.e("PropertyValuesHolder", e.toString());
552 } catch (IllegalAccessException e) {
553 Log.e("PropertyValuesHolder", e.toString());
554 }
555 }
556
557 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700558 * This function is called by ObjectAnimator when setting the start values for an animation.
Chet Haase21cd1382010-09-01 17:42:29 -0700559 * The start values are set according to the current values in the target object. The
560 * property whose value is extracted is whatever is specified by the propertyName of this
561 * PropertyValuesHolder object.
562 *
563 * @param target The object which holds the start values that should be set.
564 */
565 void setupStartValue(Object target) {
566 setupValue(target, mKeyframeSet.mKeyframes.get(0));
567 }
568
569 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700570 * This function is called by ObjectAnimator when setting the end values for an animation.
Chet Haase21cd1382010-09-01 17:42:29 -0700571 * The end values are set according to the current values in the target object. The
572 * property whose value is extracted is whatever is specified by the propertyName of this
573 * PropertyValuesHolder object.
574 *
575 * @param target The object which holds the start values that should be set.
576 */
577 void setupEndValue(Object target) {
578 setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
579 }
580
581 @Override
582 public PropertyValuesHolder clone() {
Chet Haase7c608f22010-10-22 17:54:04 -0700583 try {
584 PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
585 newPVH.mPropertyName = mPropertyName;
Chet Haaseb39f0512011-05-24 14:36:40 -0700586 newPVH.mProperty = mProperty;
Chet Haase7c608f22010-10-22 17:54:04 -0700587 newPVH.mKeyframeSet = mKeyframeSet.clone();
588 newPVH.mEvaluator = mEvaluator;
589 return newPVH;
590 } catch (CloneNotSupportedException e) {
591 // won't reach here
592 return null;
Chet Haase21cd1382010-09-01 17:42:29 -0700593 }
Chet Haase21cd1382010-09-01 17:42:29 -0700594 }
Chet Haase7c608f22010-10-22 17:54:04 -0700595
Chet Haase21cd1382010-09-01 17:42:29 -0700596 /**
Chet Haased953d082010-08-16 17:44:28 -0700597 * Internal function to set the value on the target object, using the setter set up
Chet Haasea18a86b2010-09-07 13:20:00 -0700598 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
599 * to handle turning the value calculated by ValueAnimator into a value set on the object
Chet Haased953d082010-08-16 17:44:28 -0700600 * according to the name of the property.
601 * @param target The target object on which the value is set
602 */
603 void setAnimatedValue(Object target) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700604 if (mProperty != null) {
605 mProperty.set(target, getAnimatedValue());
606 }
Chet Haased953d082010-08-16 17:44:28 -0700607 if (mSetter != null) {
608 try {
Chet Haase7c608f22010-10-22 17:54:04 -0700609 mTmpValueArray[0] = getAnimatedValue();
Chet Haased953d082010-08-16 17:44:28 -0700610 mSetter.invoke(target, mTmpValueArray);
611 } catch (InvocationTargetException e) {
612 Log.e("PropertyValuesHolder", e.toString());
613 } catch (IllegalAccessException e) {
614 Log.e("PropertyValuesHolder", e.toString());
615 }
616 }
617 }
618
619 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700620 * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
Chet Haased953d082010-08-16 17:44:28 -0700621 * to calculate animated values.
622 */
623 void init() {
624 if (mEvaluator == null) {
Chet Haaseb2ab04f2011-01-16 11:03:22 -0800625 // We already handle int and float automatically, but not their Object
Chet Haase7c608f22010-10-22 17:54:04 -0700626 // equivalents
627 mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
628 (mValueType == Float.class) ? sFloatEvaluator :
629 null;
630 }
631 if (mEvaluator != null) {
632 // KeyframeSet knows how to evaluate the common types - only give it a custom
Chet Haaseb2ab04f2011-01-16 11:03:22 -0800633 // evaluator if one has been set on this class
Chet Haase7c608f22010-10-22 17:54:04 -0700634 mKeyframeSet.setEvaluator(mEvaluator);
Chet Haased953d082010-08-16 17:44:28 -0700635 }
636 }
637
638 /**
Chet Haased82c8ac2013-08-26 14:20:16 -0700639 * The TypeEvaluator will be automatically determined based on the type of values
Chet Haased953d082010-08-16 17:44:28 -0700640 * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
641 * desired. This may be important in cases where either the type of the values supplied
642 * do not match the way that they should be interpolated between, or if the values
643 * are of a custom type or one not currently understood by the animation system. Currently,
Chet Haaseb2ab04f2011-01-16 11:03:22 -0800644 * only values of type float and int (and their Object equivalents: Float
Chet Haased953d082010-08-16 17:44:28 -0700645 * and Integer) are correctly interpolated; all other types require setting a TypeEvaluator.
646 * @param evaluator
647 */
Chet Haase7c608f22010-10-22 17:54:04 -0700648 public void setEvaluator(TypeEvaluator evaluator) {
Chet Haased953d082010-08-16 17:44:28 -0700649 mEvaluator = evaluator;
Chet Haase7c608f22010-10-22 17:54:04 -0700650 mKeyframeSet.setEvaluator(evaluator);
Chet Haased953d082010-08-16 17:44:28 -0700651 }
652
653 /**
654 * Function used to calculate the value according to the evaluator set up for
Chet Haasea18a86b2010-09-07 13:20:00 -0700655 * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
Chet Haased953d082010-08-16 17:44:28 -0700656 *
657 * @param fraction The elapsed, interpolated fraction of the animation.
Chet Haased953d082010-08-16 17:44:28 -0700658 */
Chet Haase7c608f22010-10-22 17:54:04 -0700659 void calculateValue(float fraction) {
660 mAnimatedValue = mKeyframeSet.getValue(fraction);
Chet Haased953d082010-08-16 17:44:28 -0700661 }
662
663 /**
Chet Haased953d082010-08-16 17:44:28 -0700664 * Sets the name of the property that will be animated. This name is used to derive
665 * a setter function that will be called to set animated values.
666 * For example, a property name of <code>foo</code> will result
667 * in a call to the function <code>setFoo()</code> on the target object. If either
668 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
669 * also be derived and called.
670 *
671 * <p>Note that the setter function derived from this property name
672 * must take the same parameter type as the
673 * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
674 * the setter function will fail.</p>
675 *
676 * @param propertyName The name of the property being animated.
677 */
678 public void setPropertyName(String propertyName) {
679 mPropertyName = propertyName;
680 }
681
682 /**
Chet Haaseb39f0512011-05-24 14:36:40 -0700683 * Sets the property that will be animated.
684 *
685 * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
686 * must exist on the target object specified in that ObjectAnimator.</p>
687 *
688 * @param property The property being animated.
689 */
690 public void setProperty(Property property) {
691 mProperty = property;
692 }
693
694 /**
Chet Haased953d082010-08-16 17:44:28 -0700695 * Gets the name of the property that will be animated. This name will be used to derive
696 * a setter function that will be called to set animated values.
697 * For example, a property name of <code>foo</code> will result
698 * in a call to the function <code>setFoo()</code> on the target object. If either
699 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
700 * also be derived and called.
701 */
702 public String getPropertyName() {
703 return mPropertyName;
704 }
705
706 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700707 * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
Chet Haased953d082010-08-16 17:44:28 -0700708 * most recently calculated in calculateValue().
709 * @return
710 */
711 Object getAnimatedValue() {
712 return mAnimatedValue;
713 }
Chet Haase7c608f22010-10-22 17:54:04 -0700714
Chet Haasee9140a72011-02-16 16:23:29 -0800715 @Override
716 public String toString() {
717 return mPropertyName + ": " + mKeyframeSet.toString();
718 }
719
Chet Haase6e0ecb42010-11-03 19:41:18 -0700720 /**
721 * Utility method to derive a setter/getter method name from a property name, where the
722 * prefix is typically "set" or "get" and the first letter of the property name is
723 * capitalized.
724 *
725 * @param prefix The precursor to the method name, before the property name begins, typically
726 * "set" or "get".
727 * @param propertyName The name of the property that represents the bulk of the method name
728 * after the prefix. The first letter of this word will be capitalized in the resulting
729 * method name.
730 * @return String the property name converted to a method name according to the conventions
731 * specified above.
732 */
733 static String getMethodName(String prefix, String propertyName) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700734 if (propertyName == null || propertyName.length() == 0) {
735 // shouldn't get here
736 return prefix;
737 }
738 char firstLetter = Character.toUpperCase(propertyName.charAt(0));
Chet Haase6e0ecb42010-11-03 19:41:18 -0700739 String theRest = propertyName.substring(1);
Chet Haase6e0ecb42010-11-03 19:41:18 -0700740 return prefix + firstLetter + theRest;
741 }
742
Chet Haase7c608f22010-10-22 17:54:04 -0700743 static class IntPropertyValuesHolder extends PropertyValuesHolder {
744
Chet Haaseb39f0512011-05-24 14:36:40 -0700745 // Cache JNI functions to avoid looking them up twice
Ashok Bhat0141e882014-01-17 16:44:27 +0000746 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
747 new HashMap<Class, HashMap<String, Long>>();
748 long mJniSetter;
Chet Haaseb39f0512011-05-24 14:36:40 -0700749 private IntProperty mIntProperty;
Chet Haase6e0ecb42010-11-03 19:41:18 -0700750
Chet Haase7c608f22010-10-22 17:54:04 -0700751 IntKeyframeSet mIntKeyframeSet;
752 int mIntAnimatedValue;
753
754 public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) {
755 super(propertyName);
756 mValueType = int.class;
757 mKeyframeSet = keyframeSet;
758 mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
759 }
760
Chet Haaseb39f0512011-05-24 14:36:40 -0700761 public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) {
762 super(property);
763 mValueType = int.class;
764 mKeyframeSet = keyframeSet;
765 mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
766 if (property instanceof IntProperty) {
767 mIntProperty = (IntProperty) mProperty;
768 }
769 }
770
Chet Haase7c608f22010-10-22 17:54:04 -0700771 public IntPropertyValuesHolder(String propertyName, int... values) {
772 super(propertyName);
773 setIntValues(values);
Chet Haase691ac262010-11-03 22:38:32 -0700774 }
775
Chet Haaseb39f0512011-05-24 14:36:40 -0700776 public IntPropertyValuesHolder(Property property, int... values) {
777 super(property);
778 setIntValues(values);
779 if (property instanceof IntProperty) {
780 mIntProperty = (IntProperty) mProperty;
781 }
782 }
783
Chet Haase691ac262010-11-03 22:38:32 -0700784 @Override
785 public void setIntValues(int... values) {
786 super.setIntValues(values);
Chet Haase7c608f22010-10-22 17:54:04 -0700787 mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
788 }
789
790 @Override
791 void calculateValue(float fraction) {
792 mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction);
793 }
794
795 @Override
796 Object getAnimatedValue() {
797 return mIntAnimatedValue;
798 }
799
800 @Override
801 public IntPropertyValuesHolder clone() {
802 IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
803 newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet;
804 return newPVH;
805 }
806
807 /**
808 * Internal function to set the value on the target object, using the setter set up
809 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
810 * to handle turning the value calculated by ValueAnimator into a value set on the object
811 * according to the name of the property.
812 * @param target The target object on which the value is set
813 */
814 @Override
815 void setAnimatedValue(Object target) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700816 if (mIntProperty != null) {
817 mIntProperty.setValue(target, mIntAnimatedValue);
818 return;
819 }
820 if (mProperty != null) {
821 mProperty.set(target, mIntAnimatedValue);
822 return;
823 }
Chet Haase6e0ecb42010-11-03 19:41:18 -0700824 if (mJniSetter != 0) {
825 nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
826 return;
827 }
Chet Haase7c608f22010-10-22 17:54:04 -0700828 if (mSetter != null) {
829 try {
830 mTmpValueArray[0] = mIntAnimatedValue;
831 mSetter.invoke(target, mTmpValueArray);
832 } catch (InvocationTargetException e) {
833 Log.e("PropertyValuesHolder", e.toString());
834 } catch (IllegalAccessException e) {
835 Log.e("PropertyValuesHolder", e.toString());
836 }
837 }
838 }
Chet Haase6e0ecb42010-11-03 19:41:18 -0700839
840 @Override
841 void setupSetter(Class targetClass) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700842 if (mProperty != null) {
843 return;
844 }
Chet Haase6e0ecb42010-11-03 19:41:18 -0700845 // Check new static hashmap<propName, int> for setter method
846 try {
847 mPropertyMapLock.writeLock().lock();
Ashok Bhat0141e882014-01-17 16:44:27 +0000848 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
Chet Haase6e0ecb42010-11-03 19:41:18 -0700849 if (propertyMap != null) {
Ashok Bhat0141e882014-01-17 16:44:27 +0000850 Long jniSetter = propertyMap.get(mPropertyName);
851 if (jniSetter != null) {
852 mJniSetter = jniSetter;
Chet Haase6e0ecb42010-11-03 19:41:18 -0700853 }
854 }
855 if (mJniSetter == 0) {
856 String methodName = getMethodName("set", mPropertyName);
857 mJniSetter = nGetIntMethod(targetClass, methodName);
858 if (mJniSetter != 0) {
859 if (propertyMap == null) {
Ashok Bhat0141e882014-01-17 16:44:27 +0000860 propertyMap = new HashMap<String, Long>();
Chet Haase6e0ecb42010-11-03 19:41:18 -0700861 sJNISetterPropertyMap.put(targetClass, propertyMap);
862 }
863 propertyMap.put(mPropertyName, mJniSetter);
864 }
865 }
866 } catch (NoSuchMethodError e) {
Chet Haasedb4101c2012-05-03 14:48:07 -0700867 // Couldn't find it via JNI - try reflection next. Probably means the method
868 // doesn't exist, or the type is wrong. An error will be logged later if
869 // reflection fails as well.
Chet Haase6e0ecb42010-11-03 19:41:18 -0700870 } finally {
871 mPropertyMapLock.writeLock().unlock();
872 }
873 if (mJniSetter == 0) {
874 // Couldn't find method through fast JNI approach - just use reflection
875 super.setupSetter(targetClass);
876 }
877 }
Chet Haase7c608f22010-10-22 17:54:04 -0700878 }
879
880 static class FloatPropertyValuesHolder extends PropertyValuesHolder {
881
Chet Haaseb39f0512011-05-24 14:36:40 -0700882 // Cache JNI functions to avoid looking them up twice
Ashok Bhat0141e882014-01-17 16:44:27 +0000883 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
884 new HashMap<Class, HashMap<String, Long>>();
885 long mJniSetter;
Chet Haaseb39f0512011-05-24 14:36:40 -0700886 private FloatProperty mFloatProperty;
Chet Haase6e0ecb42010-11-03 19:41:18 -0700887
Chet Haase7c608f22010-10-22 17:54:04 -0700888 FloatKeyframeSet mFloatKeyframeSet;
889 float mFloatAnimatedValue;
890
891 public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
892 super(propertyName);
893 mValueType = float.class;
894 mKeyframeSet = keyframeSet;
895 mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
896 }
897
Chet Haaseb39f0512011-05-24 14:36:40 -0700898 public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) {
899 super(property);
900 mValueType = float.class;
901 mKeyframeSet = keyframeSet;
902 mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
903 if (property instanceof FloatProperty) {
904 mFloatProperty = (FloatProperty) mProperty;
905 }
906 }
907
Chet Haase7c608f22010-10-22 17:54:04 -0700908 public FloatPropertyValuesHolder(String propertyName, float... values) {
909 super(propertyName);
910 setFloatValues(values);
Chet Haase691ac262010-11-03 22:38:32 -0700911 }
912
Chet Haaseb39f0512011-05-24 14:36:40 -0700913 public FloatPropertyValuesHolder(Property property, float... values) {
914 super(property);
915 setFloatValues(values);
916 if (property instanceof FloatProperty) {
917 mFloatProperty = (FloatProperty) mProperty;
918 }
919 }
920
Chet Haase691ac262010-11-03 22:38:32 -0700921 @Override
922 public void setFloatValues(float... values) {
923 super.setFloatValues(values);
Chet Haase7c608f22010-10-22 17:54:04 -0700924 mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
925 }
926
927 @Override
928 void calculateValue(float fraction) {
929 mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
930 }
931
932 @Override
933 Object getAnimatedValue() {
934 return mFloatAnimatedValue;
935 }
936
937 @Override
938 public FloatPropertyValuesHolder clone() {
939 FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
940 newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet;
941 return newPVH;
942 }
943
944 /**
945 * Internal function to set the value on the target object, using the setter set up
946 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
947 * to handle turning the value calculated by ValueAnimator into a value set on the object
948 * according to the name of the property.
949 * @param target The target object on which the value is set
950 */
951 @Override
952 void setAnimatedValue(Object target) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700953 if (mFloatProperty != null) {
954 mFloatProperty.setValue(target, mFloatAnimatedValue);
955 return;
956 }
957 if (mProperty != null) {
958 mProperty.set(target, mFloatAnimatedValue);
959 return;
960 }
Chet Haase6e0ecb42010-11-03 19:41:18 -0700961 if (mJniSetter != 0) {
962 nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
963 return;
964 }
Chet Haase7c608f22010-10-22 17:54:04 -0700965 if (mSetter != null) {
966 try {
967 mTmpValueArray[0] = mFloatAnimatedValue;
968 mSetter.invoke(target, mTmpValueArray);
969 } catch (InvocationTargetException e) {
970 Log.e("PropertyValuesHolder", e.toString());
971 } catch (IllegalAccessException e) {
972 Log.e("PropertyValuesHolder", e.toString());
973 }
974 }
975 }
976
Chet Haase6e0ecb42010-11-03 19:41:18 -0700977 @Override
978 void setupSetter(Class targetClass) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700979 if (mProperty != null) {
980 return;
981 }
Chet Haase6e0ecb42010-11-03 19:41:18 -0700982 // Check new static hashmap<propName, int> for setter method
983 try {
984 mPropertyMapLock.writeLock().lock();
Ashok Bhat0141e882014-01-17 16:44:27 +0000985 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
Chet Haase6e0ecb42010-11-03 19:41:18 -0700986 if (propertyMap != null) {
Ashok Bhat0141e882014-01-17 16:44:27 +0000987 Long jniSetter = propertyMap.get(mPropertyName);
988 if (jniSetter != null) {
989 mJniSetter = jniSetter;
Chet Haase6e0ecb42010-11-03 19:41:18 -0700990 }
991 }
992 if (mJniSetter == 0) {
993 String methodName = getMethodName("set", mPropertyName);
994 mJniSetter = nGetFloatMethod(targetClass, methodName);
995 if (mJniSetter != 0) {
996 if (propertyMap == null) {
Ashok Bhat0141e882014-01-17 16:44:27 +0000997 propertyMap = new HashMap<String, Long>();
Chet Haase6e0ecb42010-11-03 19:41:18 -0700998 sJNISetterPropertyMap.put(targetClass, propertyMap);
999 }
1000 propertyMap.put(mPropertyName, mJniSetter);
1001 }
1002 }
1003 } catch (NoSuchMethodError e) {
Chet Haasedb4101c2012-05-03 14:48:07 -07001004 // Couldn't find it via JNI - try reflection next. Probably means the method
1005 // doesn't exist, or the type is wrong. An error will be logged later if
1006 // reflection fails as well.
Chet Haase6e0ecb42010-11-03 19:41:18 -07001007 } finally {
1008 mPropertyMapLock.writeLock().unlock();
1009 }
1010 if (mJniSetter == 0) {
1011 // Couldn't find method through fast JNI approach - just use reflection
1012 super.setupSetter(targetClass);
1013 }
1014 }
1015
Chet Haase7c608f22010-10-22 17:54:04 -07001016 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001017
Ashok Bhat0141e882014-01-17 16:44:27 +00001018 native static private long nGetIntMethod(Class targetClass, String methodName);
1019 native static private long nGetFloatMethod(Class targetClass, String methodName);
1020 native static private void nCallIntMethod(Object target, long methodID, int arg);
1021 native static private void nCallFloatMethod(Object target, long methodID, float arg);
1022}