blob: 8fce80aa758b556bdcb6c00b18e40832e3ba145d [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
George Mountc96c7b22013-08-23 13:31:31 -070019import android.graphics.Path;
20import android.graphics.PointF;
21import android.util.FloatMath;
Chet Haaseb39f0512011-05-24 14:36:40 -070022import android.util.FloatProperty;
23import android.util.IntProperty;
Chet Haased953d082010-08-16 17:44:28 -070024import android.util.Log;
Chet Haaseb39f0512011-05-24 14:36:40 -070025import android.util.Property;
Chet Haased953d082010-08-16 17:44:28 -070026
27import java.lang.reflect.InvocationTargetException;
28import java.lang.reflect.Method;
Chet Haased953d082010-08-16 17:44:28 -070029import java.util.HashMap;
30import java.util.concurrent.locks.ReentrantReadWriteLock;
31
32/**
Chet Haase21cd1382010-09-01 17:42:29 -070033 * This class holds information about a property and the values that that property
34 * should take on during an animation. PropertyValuesHolder objects can be used to create
Chet Haasea18a86b2010-09-07 13:20:00 -070035 * animations with ValueAnimator or ObjectAnimator that operate on several different properties
Chet Haase21cd1382010-09-01 17:42:29 -070036 * in parallel.
Chet Haased953d082010-08-16 17:44:28 -070037 */
Chet Haase2794eb32010-10-12 16:29:28 -070038public class PropertyValuesHolder implements Cloneable {
Chet Haased953d082010-08-16 17:44:28 -070039
40 /**
41 * The name of the property associated with the values. This need not be a real property,
Chet Haasea18a86b2010-09-07 13:20:00 -070042 * unless this object is being used with ObjectAnimator. But this is the name by which
43 * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator.
Chet Haased953d082010-08-16 17:44:28 -070044 */
Chet Haase6e0ecb42010-11-03 19:41:18 -070045 String mPropertyName;
Chet Haased953d082010-08-16 17:44:28 -070046
47 /**
Chet Haaseb39f0512011-05-24 14:36:40 -070048 * @hide
49 */
50 protected Property mProperty;
51
52 /**
Chet Haasea18a86b2010-09-07 13:20:00 -070053 * The setter function, if needed. ObjectAnimator hands off this functionality to
Chet Haased953d082010-08-16 17:44:28 -070054 * PropertyValuesHolder, since it holds all of the per-property information. This
Chet Haase37f74ca2010-12-08 17:56:36 -080055 * property is automatically
Chet Haasea18a86b2010-09-07 13:20:00 -070056 * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
Chet Haased953d082010-08-16 17:44:28 -070057 */
Chet Haase7c608f22010-10-22 17:54:04 -070058 Method mSetter = null;
Chet Haased953d082010-08-16 17:44:28 -070059
60 /**
Chet Haasea18a86b2010-09-07 13:20:00 -070061 * The getter function, if needed. ObjectAnimator hands off this functionality to
Chet Haased953d082010-08-16 17:44:28 -070062 * PropertyValuesHolder, since it holds all of the per-property information. This
Chet Haase37f74ca2010-12-08 17:56:36 -080063 * property is automatically
Chet Haasea18a86b2010-09-07 13:20:00 -070064 * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
Chet Haased953d082010-08-16 17:44:28 -070065 * The getter is only derived and used if one of the values is null.
66 */
67 private Method mGetter = null;
68
69 /**
70 * The type of values supplied. This information is used both in deriving the setter/getter
71 * functions and in deriving the type of TypeEvaluator.
72 */
Chet Haase7c608f22010-10-22 17:54:04 -070073 Class mValueType;
Chet Haased953d082010-08-16 17:44:28 -070074
75 /**
76 * The set of keyframes (time/value pairs) that define this animation.
77 */
Chet Haase7c608f22010-10-22 17:54:04 -070078 KeyframeSet mKeyframeSet = null;
Chet Haased953d082010-08-16 17:44:28 -070079
80
Chet Haaseb2ab04f2011-01-16 11:03:22 -080081 // type evaluators for the primitive types handled by this implementation
Chet Haased953d082010-08-16 17:44:28 -070082 private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
83 private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
Chet Haased953d082010-08-16 17:44:28 -070084
85 // We try several different types when searching for appropriate setter/getter functions.
86 // The caller may have supplied values in a type that does not match the setter/getter
87 // functions (such as the integers 0 and 1 to represent floating point values for alpha).
88 // Also, the use of generics in constructors means that we end up with the Object versions
89 // of primitive types (Float vs. float). But most likely, the setter/getter functions
90 // will take primitive types instead.
91 // So we supply an ordered array of other types to try before giving up.
92 private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
93 Double.class, Integer.class};
94 private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
95 Float.class, Double.class};
96 private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class,
97 Float.class, Integer.class};
98
99 // These maps hold all property entries for a particular class. This map
100 // is used to speed up property/setter/getter lookups for a given class/property
101 // combination. No need to use reflection on the combination more than once.
102 private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap =
103 new HashMap<Class, HashMap<String, Method>>();
104 private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap =
105 new HashMap<Class, HashMap<String, Method>>();
106
107 // This lock is used to ensure that only one thread is accessing the property maps
108 // at a time.
Chet Haase6e0ecb42010-11-03 19:41:18 -0700109 final ReentrantReadWriteLock mPropertyMapLock = new ReentrantReadWriteLock();
Chet Haased953d082010-08-16 17:44:28 -0700110
111 // Used to pass single value to varargs parameter in setter invocation
Chet Haase6e0ecb42010-11-03 19:41:18 -0700112 final Object[] mTmpValueArray = new Object[1];
Chet Haased953d082010-08-16 17:44:28 -0700113
114 /**
115 * The type evaluator used to calculate the animated values. This evaluator is determined
116 * automatically based on the type of the start/end objects passed into the constructor,
Chet Haaseb2ab04f2011-01-16 11:03:22 -0800117 * but the system only knows about the primitive types int and float. Any other
Chet Haased953d082010-08-16 17:44:28 -0700118 * type will need to set the evaluator to a custom evaluator for that type.
119 */
120 private TypeEvaluator mEvaluator;
121
122 /**
123 * The value most recently calculated by calculateValue(). This is set during
Chet Haasea18a86b2010-09-07 13:20:00 -0700124 * that function and might be retrieved later either by ValueAnimator.animatedValue() or
125 * by the property-setting logic in ObjectAnimator.animatedValue().
Chet Haased953d082010-08-16 17:44:28 -0700126 */
127 private Object mAnimatedValue;
128
129 /**
George Mount16d2c9c2013-09-17 09:07:48 -0700130 * Converts from the source Object type to the setter Object type.
131 */
132 private TypeConverter mConverter;
133
134 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700135 * Internal utility constructor, used by the factory methods to set the property name.
136 * @param propertyName The name of the property for this holder.
Chet Haased953d082010-08-16 17:44:28 -0700137 */
Chet Haase2794eb32010-10-12 16:29:28 -0700138 private PropertyValuesHolder(String propertyName) {
139 mPropertyName = propertyName;
Chet Haased953d082010-08-16 17:44:28 -0700140 }
141
142 /**
Chet Haaseb39f0512011-05-24 14:36:40 -0700143 * Internal utility constructor, used by the factory methods to set the property.
144 * @param property The property for this holder.
145 */
146 private PropertyValuesHolder(Property property) {
147 mProperty = property;
148 if (property != null) {
149 mPropertyName = property.getName();
150 }
151 }
152
153 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700154 * Constructs and returns a PropertyValuesHolder with a given property name and
155 * set of int values.
156 * @param propertyName The name of the property being animated.
157 * @param values The values that the named property will animate between.
158 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
159 */
160 public static PropertyValuesHolder ofInt(String propertyName, int... values) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700161 return new IntPropertyValuesHolder(propertyName, values);
162 }
163
164 /**
165 * Constructs and returns a PropertyValuesHolder with a given property and
166 * set of int values.
167 * @param property The property being animated. Should not be null.
168 * @param values The values that the property will animate between.
169 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
170 */
171 public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
172 return new IntPropertyValuesHolder(property, values);
Chet Haase2794eb32010-10-12 16:29:28 -0700173 }
174
175 /**
176 * Constructs and returns a PropertyValuesHolder with a given property name and
George Mount4eed5292013-08-30 13:56:01 -0700177 * set of <code>int[]</code> values. At least two <code>int[]</code> values must be supplied,
178 * a start and end value. If more values are supplied, the values will be animated from the
179 * start, through all intermediate values to the end value. When used with ObjectAnimator,
180 * the elements of the array represent the parameters of the setter function.
181 *
George Mountc96c7b22013-08-23 13:31:31 -0700182 * @param propertyName The name of the property being animated. Can also be the
183 * case-sensitive name of the entire setter method. Should not be null.
George Mount4eed5292013-08-30 13:56:01 -0700184 * @param values The values that the property will animate between.
185 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
186 * @see IntArrayEvaluator#IntArrayEvaluator(int[])
187 * @see ObjectAnimator#ofMultiInt(Object, String, TypeConverter, TypeEvaluator, Object[])
188 */
189 public static PropertyValuesHolder ofMultiInt(String propertyName, int[][] values) {
190 if (values.length < 2) {
191 throw new IllegalArgumentException("At least 2 values must be supplied");
192 }
193 int numParameters = 0;
194 for (int i = 0; i < values.length; i++) {
195 if (values[i] == null) {
196 throw new IllegalArgumentException("values must not be null");
197 }
198 int length = values[i].length;
199 if (i == 0) {
200 numParameters = length;
201 } else if (length != numParameters) {
202 throw new IllegalArgumentException("Values must all have the same length");
203 }
204 }
205 IntArrayEvaluator evaluator = new IntArrayEvaluator(new int[numParameters]);
206 return new MultiIntValuesHolder(propertyName, null, evaluator, (Object[]) values);
207 }
208
209 /**
George Mountc96c7b22013-08-23 13:31:31 -0700210 * Constructs and returns a PropertyValuesHolder with a given property name to use
211 * as a multi-int setter. The values are animated along the path, with the first
212 * parameter of the setter set to the x coordinate and the second set to the y coordinate.
213 *
214 * @param propertyName The name of the property being animated. Can also be the
215 * case-sensitive name of the entire setter method. Should not be null.
216 * The setter must take exactly two <code>int</code> parameters.
217 * @param path The Path along which the values should be animated.
218 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
219 * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...)
220 */
221 public static PropertyValuesHolder ofMultiInt(String propertyName, Path path) {
222 Keyframe[] keyframes = createKeyframes(path);
223 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(keyframes);
224 TypeEvaluator<PointF> evaluator = new PointFEvaluator(new PointF());
225 PointFToIntArray converter = new PointFToIntArray();
226 return new MultiIntValuesHolder(propertyName, converter, evaluator, keyframeSet);
227 }
228
229 /**
George Mount4eed5292013-08-30 13:56:01 -0700230 * Constructs and returns a PropertyValuesHolder with a given property and
231 * set of Object values for use with ObjectAnimator multi-value setters. The Object
232 * values are converted to <code>int[]</code> using the converter.
233 *
234 * @param propertyName The property being animated or complete name of the setter.
235 * Should not be null.
236 * @param converter Used to convert the animated value to setter parameters.
237 * @param evaluator A TypeEvaluator that will be called on each animation frame to
238 * provide the necessary interpolation between the Object values to derive the animated
239 * value.
240 * @param values The values that the property will animate between.
241 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
242 * @see ObjectAnimator#ofMultiInt(Object, String, TypeConverter, TypeEvaluator, Object[])
243 * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...)
244 */
245 public static <V> PropertyValuesHolder ofMultiInt(String propertyName,
246 TypeConverter<V, int[]> converter, TypeEvaluator<V> evaluator, V... values) {
247 return new MultiIntValuesHolder(propertyName, converter, evaluator, values);
248 }
249
250 /**
251 * Constructs and returns a PropertyValuesHolder object with the specified property name or
252 * setter name for use in a multi-int setter function using ObjectAnimator. The values can be
253 * of any type, but the type should be consistent so that the supplied
254 * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The
255 * <code>converter</code> converts the values to parameters in the setter function.
256 *
257 * <p>At least two values must be supplied, a start and an end value.</p>
258 *
259 * @param propertyName The name of the property to associate with the set of values. This
260 * may also be the complete name of a setter function.
261 * @param converter Converts <code>values</code> into int parameters for the setter.
262 * Can be null if the Keyframes have int[] values.
263 * @param evaluator Used to interpolate between values.
264 * @param values The values at specific fractional times to evaluate between
265 * @return A PropertyValuesHolder for a multi-int parameter setter.
266 */
267 public static <T> PropertyValuesHolder ofMultiInt(String propertyName,
268 TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, Keyframe... values) {
269 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
270 return new MultiIntValuesHolder(propertyName, converter, evaluator, keyframeSet);
271 }
272
273 /**
274 * Constructs and returns a PropertyValuesHolder with a given property name and
Chet Haase2794eb32010-10-12 16:29:28 -0700275 * set of float values.
276 * @param propertyName The name of the property being animated.
277 * @param values The values that the named property will animate between.
278 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
279 */
280 public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700281 return new FloatPropertyValuesHolder(propertyName, values);
282 }
283
284 /**
285 * Constructs and returns a PropertyValuesHolder with a given property and
286 * set of float values.
287 * @param property The property being animated. Should not be null.
288 * @param values The values that the property will animate between.
289 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
290 */
291 public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
292 return new FloatPropertyValuesHolder(property, values);
Chet Haase2794eb32010-10-12 16:29:28 -0700293 }
294
295 /**
296 * Constructs and returns a PropertyValuesHolder with a given property name and
George Mount4eed5292013-08-30 13:56:01 -0700297 * set of <code>float[]</code> values. At least two <code>float[]</code> values must be supplied,
298 * a start and end value. If more values are supplied, the values will be animated from the
299 * start, through all intermediate values to the end value. When used with ObjectAnimator,
300 * the elements of the array represent the parameters of the setter function.
301 *
George Mountc96c7b22013-08-23 13:31:31 -0700302 * @param propertyName The name of the property being animated. Can also be the
303 * case-sensitive name of the entire setter method. Should not be null.
George Mount4eed5292013-08-30 13:56:01 -0700304 * @param values The values that the property will animate between.
305 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
306 * @see FloatArrayEvaluator#FloatArrayEvaluator(float[])
307 * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[])
308 */
309 public static PropertyValuesHolder ofMultiFloat(String propertyName, float[][] values) {
310 if (values.length < 2) {
311 throw new IllegalArgumentException("At least 2 values must be supplied");
312 }
313 int numParameters = 0;
314 for (int i = 0; i < values.length; i++) {
315 if (values[i] == null) {
316 throw new IllegalArgumentException("values must not be null");
317 }
318 int length = values[i].length;
319 if (i == 0) {
320 numParameters = length;
321 } else if (length != numParameters) {
322 throw new IllegalArgumentException("Values must all have the same length");
323 }
324 }
325 FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[numParameters]);
326 return new MultiFloatValuesHolder(propertyName, null, evaluator, (Object[]) values);
327 }
328
329 /**
George Mountc96c7b22013-08-23 13:31:31 -0700330 * Constructs and returns a PropertyValuesHolder with a given property name to use
331 * as a multi-float setter. The values are animated along the path, with the first
332 * parameter of the setter set to the x coordinate and the second set to the y coordinate.
333 *
334 * @param propertyName The name of the property being animated. Can also be the
335 * case-sensitive name of the entire setter method. Should not be null.
336 * The setter must take exactly two <code>float</code> parameters.
337 * @param path The Path along which the values should be animated.
338 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
339 * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...)
340 */
341 public static PropertyValuesHolder ofMultiFloat(String propertyName, Path path) {
342 Keyframe[] keyframes = createKeyframes(path);
343 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(keyframes);
344 TypeEvaluator<PointF> evaluator = new PointFEvaluator(new PointF());
345 PointFToFloatArray converter = new PointFToFloatArray();
346 return new MultiFloatValuesHolder(propertyName, converter, evaluator, keyframeSet);
347 }
348
349 /**
George Mount4eed5292013-08-30 13:56:01 -0700350 * Constructs and returns a PropertyValuesHolder with a given property and
351 * set of Object values for use with ObjectAnimator multi-value setters. The Object
352 * values are converted to <code>float[]</code> using the converter.
353 *
354 * @param propertyName The property being animated or complete name of the setter.
355 * Should not be null.
356 * @param converter Used to convert the animated value to setter parameters.
357 * @param evaluator A TypeEvaluator that will be called on each animation frame to
358 * provide the necessary interpolation between the Object values to derive the animated
359 * value.
360 * @param values The values that the property will animate between.
361 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
362 * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[])
363 */
364 public static <V> PropertyValuesHolder ofMultiFloat(String propertyName,
365 TypeConverter<V, float[]> converter, TypeEvaluator<V> evaluator, V... values) {
366 return new MultiFloatValuesHolder(propertyName, converter, evaluator, values);
367 }
368
369 /**
370 * Constructs and returns a PropertyValuesHolder object with the specified property name or
371 * setter name for use in a multi-float setter function using ObjectAnimator. The values can be
372 * of any type, but the type should be consistent so that the supplied
373 * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The
374 * <code>converter</code> converts the values to parameters in the setter function.
375 *
376 * <p>At least two values must be supplied, a start and an end value.</p>
377 *
378 * @param propertyName The name of the property to associate with the set of values. This
379 * may also be the complete name of a setter function.
380 * @param converter Converts <code>values</code> into float parameters for the setter.
381 * Can be null if the Keyframes have float[] values.
382 * @param evaluator Used to interpolate between values.
383 * @param values The values at specific fractional times to evaluate between
384 * @return A PropertyValuesHolder for a multi-float parameter setter.
385 */
386 public static <T> PropertyValuesHolder ofMultiFloat(String propertyName,
387 TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, Keyframe... values) {
388 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
389 return new MultiFloatValuesHolder(propertyName, converter, evaluator, keyframeSet);
390 }
391
392 /**
393 * Constructs and returns a PropertyValuesHolder with a given property name and
Chet Haase2794eb32010-10-12 16:29:28 -0700394 * set of Object values. This variant also takes a TypeEvaluator because the system
Chet Haaseb39f0512011-05-24 14:36:40 -0700395 * cannot automatically interpolate between objects of unknown type.
Chet Haase2794eb32010-10-12 16:29:28 -0700396 *
397 * @param propertyName The name of the property being animated.
398 * @param evaluator A TypeEvaluator that will be called on each animation frame to
Chet Haaseb39f0512011-05-24 14:36:40 -0700399 * provide the necessary interpolation between the Object values to derive the animated
Chet Haase2794eb32010-10-12 16:29:28 -0700400 * value.
401 * @param values The values that the named property will animate between.
402 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
403 */
404 public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
405 Object... values) {
406 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
407 pvh.setObjectValues(values);
408 pvh.setEvaluator(evaluator);
409 return pvh;
410 }
411
412 /**
George Mountc96c7b22013-08-23 13:31:31 -0700413 * Constructs and returns a PropertyValuesHolder with a given property name and
414 * a Path along which the values should be animated. This variant supports a
415 * <code>TypeConverter</code> to convert from <code>PointF</code> to the target
416 * type.
417 *
418 * @param propertyName The name of the property being animated.
419 * @param converter Converts a PointF to the type associated with the setter. May be
420 * null if conversion is unnecessary.
421 * @param path The Path along which the values should be animated.
422 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
423 */
424 public static PropertyValuesHolder ofObject(String propertyName,
425 TypeConverter<PointF, ?> converter, Path path) {
426 Keyframe[] keyframes = createKeyframes(path);
427 PropertyValuesHolder pvh = ofKeyframe(propertyName, keyframes);
428 pvh.setEvaluator(new PointFEvaluator(new PointF()));
429 pvh.setConverter(converter);
430 return pvh;
431 }
432
433 /**
Chet Haaseb39f0512011-05-24 14:36:40 -0700434 * Constructs and returns a PropertyValuesHolder with a given property and
435 * set of Object values. This variant also takes a TypeEvaluator because the system
436 * cannot automatically interpolate between objects of unknown type.
437 *
438 * @param property The property being animated. Should not be null.
439 * @param evaluator A TypeEvaluator that will be called on each animation frame to
440 * provide the necessary interpolation between the Object values to derive the animated
441 * value.
442 * @param values The values that the property will animate between.
443 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
444 */
445 public static <V> PropertyValuesHolder ofObject(Property property,
446 TypeEvaluator<V> evaluator, V... values) {
447 PropertyValuesHolder pvh = new PropertyValuesHolder(property);
448 pvh.setObjectValues(values);
449 pvh.setEvaluator(evaluator);
450 return pvh;
451 }
452
453 /**
George Mount16d2c9c2013-09-17 09:07:48 -0700454 * Constructs and returns a PropertyValuesHolder with a given property and
455 * set of Object values. This variant also takes a TypeEvaluator because the system
456 * cannot automatically interpolate between objects of unknown type. This variant also
457 * takes a <code>TypeConverter</code> to convert from animated values to the type
458 * of the property. If only one value is supplied, the <code>TypeConverter</code>
459 * must implement {@link TypeConverter#convertBack(Object)} to retrieve the current
460 * value.
461 *
462 * @param property The property being animated. Should not be null.
463 * @param converter Converts the animated object to the Property type.
464 * @param evaluator A TypeEvaluator that will be called on each animation frame to
465 * provide the necessary interpolation between the Object values to derive the animated
466 * value.
467 * @param values The values that the property will animate between.
468 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
469 * @see #setConverter(TypeConverter)
470 * @see TypeConverter
471 */
472 public static <T, V> PropertyValuesHolder ofObject(Property<?, V> property,
473 TypeConverter<T, V> converter, TypeEvaluator<T> evaluator, T... values) {
474 PropertyValuesHolder pvh = new PropertyValuesHolder(property);
475 pvh.setConverter(converter);
476 pvh.setObjectValues(values);
477 pvh.setEvaluator(evaluator);
478 return pvh;
479 }
480
481 /**
George Mountc96c7b22013-08-23 13:31:31 -0700482 * Constructs and returns a PropertyValuesHolder with a given property and
483 * a Path along which the values should be animated. This variant supports a
484 * <code>TypeConverter</code> to convert from <code>PointF</code> to the target
485 * type.
486 *
487 * @param property The property being animated. Should not be null.
488 * @param converter Converts a PointF to the type associated with the setter. May be
489 * null if conversion is unnecessary.
490 * @param path The Path along which the values should be animated.
491 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
492 */
493 public static <V> PropertyValuesHolder ofObject(Property<?, V> property,
494 TypeConverter<PointF, V> converter, Path path) {
495 Keyframe[] keyframes = createKeyframes(path);
496 PropertyValuesHolder pvh = ofKeyframe(property, keyframes);
497 pvh.setEvaluator(new PointFEvaluator(new PointF()));
498 pvh.setConverter(converter);
499 return pvh;
500 }
501
502 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700503 * Constructs and returns a PropertyValuesHolder object with the specified property name and set
504 * of values. These values can be of any type, but the type should be consistent so that
Chet Haased953d082010-08-16 17:44:28 -0700505 * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
506 * the common type.
507 * <p>If there is only one value, it is assumed to be the end value of an animation,
508 * and an initial value will be derived, if possible, by calling a getter function
509 * on the object. Also, if any value is null, the value will be filled in when the animation
510 * starts in the same way. This mechanism of automatically getting null values only works
511 * if the PropertyValuesHolder object is used in conjunction
Chet Haase1a8e4042010-12-07 11:29:39 -0800512 * {@link ObjectAnimator}, and with a getter function
513 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
Chet Haased953d082010-08-16 17:44:28 -0700514 * no way of determining what the value should be.
515 * @param propertyName The name of the property associated with this set of values. This
Chet Haasea18a86b2010-09-07 13:20:00 -0700516 * can be the actual property name to be used when using a ObjectAnimator object, or
Chet Haased953d082010-08-16 17:44:28 -0700517 * just a name used to get animated values, such as if this object is used with an
Chet Haasea18a86b2010-09-07 13:20:00 -0700518 * ValueAnimator object.
Chet Haased953d082010-08-16 17:44:28 -0700519 * @param values The set of values to animate between.
520 */
Chet Haase2794eb32010-10-12 16:29:28 -0700521 public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
Chet Haase7c608f22010-10-22 17:54:04 -0700522 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
523 if (keyframeSet instanceof IntKeyframeSet) {
524 return new IntPropertyValuesHolder(propertyName, (IntKeyframeSet) keyframeSet);
525 } else if (keyframeSet instanceof FloatKeyframeSet) {
526 return new FloatPropertyValuesHolder(propertyName, (FloatKeyframeSet) keyframeSet);
527 }
528 else {
529 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
530 pvh.mKeyframeSet = keyframeSet;
531 pvh.mValueType = ((Keyframe)values[0]).getType();
532 return pvh;
533 }
Chet Haased953d082010-08-16 17:44:28 -0700534 }
535
536 /**
Chet Haaseb39f0512011-05-24 14:36:40 -0700537 * Constructs and returns a PropertyValuesHolder object with the specified property and set
538 * of values. These values can be of any type, but the type should be consistent so that
539 * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
540 * the common type.
541 * <p>If there is only one value, it is assumed to be the end value of an animation,
542 * and an initial value will be derived, if possible, by calling the property's
543 * {@link android.util.Property#get(Object)} function.
544 * Also, if any value is null, the value will be filled in when the animation
545 * starts in the same way. This mechanism of automatically getting null values only works
546 * if the PropertyValuesHolder object is used in conjunction with
547 * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has
548 * no way of determining what the value should be.
549 * @param property The property associated with this set of values. Should not be null.
550 * @param values The set of values to animate between.
551 */
552 public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
553 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
554 if (keyframeSet instanceof IntKeyframeSet) {
555 return new IntPropertyValuesHolder(property, (IntKeyframeSet) keyframeSet);
556 } else if (keyframeSet instanceof FloatKeyframeSet) {
557 return new FloatPropertyValuesHolder(property, (FloatKeyframeSet) keyframeSet);
558 }
559 else {
560 PropertyValuesHolder pvh = new PropertyValuesHolder(property);
561 pvh.mKeyframeSet = keyframeSet;
562 pvh.mValueType = ((Keyframe)values[0]).getType();
563 return pvh;
564 }
565 }
566
567 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700568 * Set the animated values for this object to this set of ints.
Chet Haased953d082010-08-16 17:44:28 -0700569 * If there is only one value, it is assumed to be the end value of an animation,
570 * and an initial value will be derived, if possible, by calling a getter function
571 * on the object. Also, if any value is null, the value will be filled in when the animation
572 * starts in the same way. This mechanism of automatically getting null values only works
573 * if the PropertyValuesHolder object is used in conjunction
Chet Haase1a8e4042010-12-07 11:29:39 -0800574 * {@link ObjectAnimator}, and with a getter function
575 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
Chet Haased953d082010-08-16 17:44:28 -0700576 * no way of determining what the value should be.
Chet Haase2794eb32010-10-12 16:29:28 -0700577 *
578 * @param values One or more values that the animation will animate between.
Chet Haased953d082010-08-16 17:44:28 -0700579 */
Chet Haase2794eb32010-10-12 16:29:28 -0700580 public void setIntValues(int... values) {
581 mValueType = int.class;
582 mKeyframeSet = KeyframeSet.ofInt(values);
583 }
Chet Haased953d082010-08-16 17:44:28 -0700584
Chet Haase2794eb32010-10-12 16:29:28 -0700585 /**
586 * Set the animated values for this object to this set of floats.
587 * If there is only one value, it is assumed to be the end value of an animation,
588 * and an initial value will be derived, if possible, by calling a getter function
589 * on the object. Also, if any value is null, the value will be filled in when the animation
590 * starts in the same way. This mechanism of automatically getting null values only works
591 * if the PropertyValuesHolder object is used in conjunction
Chet Haase1a8e4042010-12-07 11:29:39 -0800592 * {@link ObjectAnimator}, and with a getter function
593 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
Chet Haase2794eb32010-10-12 16:29:28 -0700594 * no way of determining what the value should be.
595 *
596 * @param values One or more values that the animation will animate between.
597 */
598 public void setFloatValues(float... values) {
599 mValueType = float.class;
600 mKeyframeSet = KeyframeSet.ofFloat(values);
601 }
602
603 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700604 * Set the animated values for this object to this set of Keyframes.
605 *
606 * @param values One or more values that the animation will animate between.
607 */
608 public void setKeyframes(Keyframe... values) {
609 int numKeyframes = values.length;
610 Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
611 mValueType = ((Keyframe)values[0]).getType();
612 for (int i = 0; i < numKeyframes; ++i) {
613 keyframes[i] = (Keyframe)values[i];
Chet Haased953d082010-08-16 17:44:28 -0700614 }
615 mKeyframeSet = new KeyframeSet(keyframes);
616 }
617
Chet Haase2794eb32010-10-12 16:29:28 -0700618 /**
619 * Set the animated values for this object to this set of Objects.
620 * If there is only one value, it is assumed to be the end value of an animation,
621 * and an initial value will be derived, if possible, by calling a getter function
622 * on the object. Also, if any value is null, the value will be filled in when the animation
623 * starts in the same way. This mechanism of automatically getting null values only works
624 * if the PropertyValuesHolder object is used in conjunction
Chet Haase1a8e4042010-12-07 11:29:39 -0800625 * {@link ObjectAnimator}, and with a getter function
626 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
Chet Haase2794eb32010-10-12 16:29:28 -0700627 * no way of determining what the value should be.
628 *
629 * @param values One or more values that the animation will animate between.
630 */
631 public void setObjectValues(Object... values) {
632 mValueType = values[0].getClass();
633 mKeyframeSet = KeyframeSet.ofObject(values);
634 }
Chet Haased953d082010-08-16 17:44:28 -0700635
636 /**
George Mount16d2c9c2013-09-17 09:07:48 -0700637 * Sets the converter to convert from the values type to the setter's parameter type.
638 * @param converter The converter to use to convert values.
639 */
640 public void setConverter(TypeConverter converter) {
641 mConverter = converter;
642 }
643
644 /**
Chet Haased953d082010-08-16 17:44:28 -0700645 * Determine the setter or getter function using the JavaBeans convention of setFoo or
646 * getFoo for a property named 'foo'. This function figures out what the name of the
647 * function should be and uses reflection to find the Method with that name on the
648 * target object.
649 *
650 * @param targetClass The class to search for the method
651 * @param prefix "set" or "get", depending on whether we need a setter or getter.
652 * @param valueType The type of the parameter (in the case of a setter). This type
653 * is derived from the values set on this PropertyValuesHolder. This type is used as
654 * a first guess at the parameter type, but we check for methods with several different
655 * types to avoid problems with slight mis-matches between supplied values and actual
656 * value types used on the setter.
657 * @return Method the method associated with mPropertyName.
658 */
659 private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
660 // TODO: faster implementation...
661 Method returnVal = null;
Chet Haase6e0ecb42010-11-03 19:41:18 -0700662 String methodName = getMethodName(prefix, mPropertyName);
Chet Haased953d082010-08-16 17:44:28 -0700663 Class args[] = null;
664 if (valueType == null) {
665 try {
666 returnVal = targetClass.getMethod(methodName, args);
667 } catch (NoSuchMethodException e) {
Chet Haasedb4101c2012-05-03 14:48:07 -0700668 // Swallow the error, log it later
Chet Haased953d082010-08-16 17:44:28 -0700669 }
670 } else {
671 args = new Class[1];
672 Class typeVariants[];
George Mount16d2c9c2013-09-17 09:07:48 -0700673 if (valueType.equals(Float.class)) {
Chet Haased953d082010-08-16 17:44:28 -0700674 typeVariants = FLOAT_VARIANTS;
George Mount16d2c9c2013-09-17 09:07:48 -0700675 } else if (valueType.equals(Integer.class)) {
Chet Haased953d082010-08-16 17:44:28 -0700676 typeVariants = INTEGER_VARIANTS;
George Mount16d2c9c2013-09-17 09:07:48 -0700677 } else if (valueType.equals(Double.class)) {
Chet Haased953d082010-08-16 17:44:28 -0700678 typeVariants = DOUBLE_VARIANTS;
679 } else {
680 typeVariants = new Class[1];
George Mount16d2c9c2013-09-17 09:07:48 -0700681 typeVariants[0] = valueType;
Chet Haased953d082010-08-16 17:44:28 -0700682 }
683 for (Class typeVariant : typeVariants) {
684 args[0] = typeVariant;
685 try {
686 returnVal = targetClass.getMethod(methodName, args);
George Mount16d2c9c2013-09-17 09:07:48 -0700687 if (mConverter == null) {
688 // change the value type to suit
689 mValueType = typeVariant;
690 }
Chet Haased953d082010-08-16 17:44:28 -0700691 return returnVal;
692 } catch (NoSuchMethodException e) {
693 // Swallow the error and keep trying other variants
694 }
695 }
Chet Haase602e4d32010-08-16 08:57:23 -0700696 // If we got here, then no appropriate function was found
Chet Haasedb4101c2012-05-03 14:48:07 -0700697 }
698
699 if (returnVal == null) {
700 Log.w("PropertyValuesHolder", "Method " +
George Mount16d2c9c2013-09-17 09:07:48 -0700701 getMethodName(prefix, mPropertyName) + "() with type " + valueType +
Chet Haasedb4101c2012-05-03 14:48:07 -0700702 " not found on target class " + targetClass);
Chet Haased953d082010-08-16 17:44:28 -0700703 }
Chet Haase602e4d32010-08-16 08:57:23 -0700704
Chet Haased953d082010-08-16 17:44:28 -0700705 return returnVal;
706 }
707
708
709 /**
710 * Returns the setter or getter requested. This utility function checks whether the
711 * requested method exists in the propertyMapMap cache. If not, it calls another
712 * utility function to request the Method from the targetClass directly.
713 * @param targetClass The Class on which the requested method should exist.
714 * @param propertyMapMap The cache of setters/getters derived so far.
715 * @param prefix "set" or "get", for the setter or getter.
716 * @param valueType The type of parameter passed into the method (null for getter).
717 * @return Method the method associated with mPropertyName.
718 */
719 private Method setupSetterOrGetter(Class targetClass,
720 HashMap<Class, HashMap<String, Method>> propertyMapMap,
721 String prefix, Class valueType) {
722 Method setterOrGetter = null;
723 try {
724 // Have to lock property map prior to reading it, to guard against
725 // another thread putting something in there after we've checked it
726 // but before we've added an entry to it
Chet Haase6e0ecb42010-11-03 19:41:18 -0700727 mPropertyMapLock.writeLock().lock();
Chet Haased953d082010-08-16 17:44:28 -0700728 HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
729 if (propertyMap != null) {
730 setterOrGetter = propertyMap.get(mPropertyName);
731 }
732 if (setterOrGetter == null) {
733 setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
734 if (propertyMap == null) {
735 propertyMap = new HashMap<String, Method>();
736 propertyMapMap.put(targetClass, propertyMap);
737 }
738 propertyMap.put(mPropertyName, setterOrGetter);
739 }
740 } finally {
Chet Haase6e0ecb42010-11-03 19:41:18 -0700741 mPropertyMapLock.writeLock().unlock();
Chet Haased953d082010-08-16 17:44:28 -0700742 }
743 return setterOrGetter;
744 }
745
746 /**
747 * Utility function to get the setter from targetClass
748 * @param targetClass The Class on which the requested method should exist.
749 */
Chet Haase6e0ecb42010-11-03 19:41:18 -0700750 void setupSetter(Class targetClass) {
George Mount16d2c9c2013-09-17 09:07:48 -0700751 Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
752 mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
Chet Haased953d082010-08-16 17:44:28 -0700753 }
754
755 /**
756 * Utility function to get the getter from targetClass
757 */
758 private void setupGetter(Class targetClass) {
759 mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
760 }
761
762 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700763 * Internal function (called from ObjectAnimator) to set up the setter and getter
Chet Haased953d082010-08-16 17:44:28 -0700764 * prior to running the animation. If the setter has not been manually set for this
765 * object, it will be derived automatically given the property name, target object, and
766 * types of values supplied. If no getter has been set, it will be supplied iff any of the
767 * supplied values was null. If there is a null value, then the getter (supplied or derived)
768 * will be called to set those null values to the current value of the property
769 * on the target object.
770 * @param target The object on which the setter (and possibly getter) exist.
771 */
772 void setupSetterAndGetter(Object target) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700773 if (mProperty != null) {
774 // check to make sure that mProperty is on the class of target
775 try {
George Mount16d2c9c2013-09-17 09:07:48 -0700776 Object testValue = null;
Chet Haaseb39f0512011-05-24 14:36:40 -0700777 for (Keyframe kf : mKeyframeSet.mKeyframes) {
778 if (!kf.hasValue()) {
George Mount16d2c9c2013-09-17 09:07:48 -0700779 if (testValue == null) {
780 testValue = convertBack(mProperty.get(target));
781 }
782 kf.setValue(testValue);
Chet Haaseb39f0512011-05-24 14:36:40 -0700783 }
784 }
785 return;
786 } catch (ClassCastException e) {
Chet Haasedb4101c2012-05-03 14:48:07 -0700787 Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
Chet Haaseb39f0512011-05-24 14:36:40 -0700788 ") on target object " + target + ". Trying reflection instead");
789 mProperty = null;
790 }
791 }
Chet Haased953d082010-08-16 17:44:28 -0700792 Class targetClass = target.getClass();
793 if (mSetter == null) {
794 setupSetter(targetClass);
795 }
796 for (Keyframe kf : mKeyframeSet.mKeyframes) {
Chet Haase7c608f22010-10-22 17:54:04 -0700797 if (!kf.hasValue()) {
Chet Haased953d082010-08-16 17:44:28 -0700798 if (mGetter == null) {
799 setupGetter(targetClass);
Chet Haasedb4101c2012-05-03 14:48:07 -0700800 if (mGetter == null) {
801 // Already logged the error - just return to avoid NPE
802 return;
803 }
Chet Haased953d082010-08-16 17:44:28 -0700804 }
805 try {
George Mount16d2c9c2013-09-17 09:07:48 -0700806 Object value = convertBack(mGetter.invoke(target));
807 kf.setValue(value);
Chet Haased953d082010-08-16 17:44:28 -0700808 } catch (InvocationTargetException e) {
809 Log.e("PropertyValuesHolder", e.toString());
810 } catch (IllegalAccessException e) {
811 Log.e("PropertyValuesHolder", e.toString());
812 }
813 }
814 }
815 }
816
George Mount16d2c9c2013-09-17 09:07:48 -0700817 private Object convertBack(Object value) {
818 if (mConverter != null) {
819 value = mConverter.convertBack(value);
820 if (value == null) {
821 throw new IllegalArgumentException("Converter "
822 + mConverter.getClass().getName()
823 + " must implement convertBack and not return null.");
824 }
825 }
826 return value;
827 }
828
Chet Haased953d082010-08-16 17:44:28 -0700829 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700830 * Utility function to set the value stored in a particular Keyframe. The value used is
831 * whatever the value is for the property name specified in the keyframe on the target object.
832 *
833 * @param target The target object from which the current value should be extracted.
834 * @param kf The keyframe which holds the property name and value.
835 */
836 private void setupValue(Object target, Keyframe kf) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700837 if (mProperty != null) {
George Mount16d2c9c2013-09-17 09:07:48 -0700838 Object value = convertBack(mProperty.get(target));
839 kf.setValue(value);
Chet Haaseb39f0512011-05-24 14:36:40 -0700840 }
Chet Haase21cd1382010-09-01 17:42:29 -0700841 try {
842 if (mGetter == null) {
843 Class targetClass = target.getClass();
844 setupGetter(targetClass);
Chet Haasedb4101c2012-05-03 14:48:07 -0700845 if (mGetter == null) {
846 // Already logged the error - just return to avoid NPE
847 return;
848 }
Chet Haase21cd1382010-09-01 17:42:29 -0700849 }
George Mount16d2c9c2013-09-17 09:07:48 -0700850 Object value = convertBack(mGetter.invoke(target));
851 kf.setValue(value);
Chet Haase21cd1382010-09-01 17:42:29 -0700852 } catch (InvocationTargetException e) {
853 Log.e("PropertyValuesHolder", e.toString());
854 } catch (IllegalAccessException e) {
855 Log.e("PropertyValuesHolder", e.toString());
856 }
857 }
858
859 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700860 * This function is called by ObjectAnimator when setting the start values for an animation.
Chet Haase21cd1382010-09-01 17:42:29 -0700861 * The start values are set according to the current values in the target object. The
862 * property whose value is extracted is whatever is specified by the propertyName of this
863 * PropertyValuesHolder object.
864 *
865 * @param target The object which holds the start values that should be set.
866 */
867 void setupStartValue(Object target) {
868 setupValue(target, mKeyframeSet.mKeyframes.get(0));
869 }
870
871 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700872 * This function is called by ObjectAnimator when setting the end values for an animation.
Chet Haase21cd1382010-09-01 17:42:29 -0700873 * The end values are set according to the current values in the target object. The
874 * property whose value is extracted is whatever is specified by the propertyName of this
875 * PropertyValuesHolder object.
876 *
877 * @param target The object which holds the start values that should be set.
878 */
879 void setupEndValue(Object target) {
880 setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
881 }
882
883 @Override
884 public PropertyValuesHolder clone() {
Chet Haase7c608f22010-10-22 17:54:04 -0700885 try {
886 PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
887 newPVH.mPropertyName = mPropertyName;
Chet Haaseb39f0512011-05-24 14:36:40 -0700888 newPVH.mProperty = mProperty;
Chet Haase7c608f22010-10-22 17:54:04 -0700889 newPVH.mKeyframeSet = mKeyframeSet.clone();
890 newPVH.mEvaluator = mEvaluator;
891 return newPVH;
892 } catch (CloneNotSupportedException e) {
893 // won't reach here
894 return null;
Chet Haase21cd1382010-09-01 17:42:29 -0700895 }
Chet Haase21cd1382010-09-01 17:42:29 -0700896 }
Chet Haase7c608f22010-10-22 17:54:04 -0700897
Chet Haase21cd1382010-09-01 17:42:29 -0700898 /**
Chet Haased953d082010-08-16 17:44:28 -0700899 * Internal function to set the value on the target object, using the setter set up
Chet Haasea18a86b2010-09-07 13:20:00 -0700900 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
901 * to handle turning the value calculated by ValueAnimator into a value set on the object
Chet Haased953d082010-08-16 17:44:28 -0700902 * according to the name of the property.
903 * @param target The target object on which the value is set
904 */
905 void setAnimatedValue(Object target) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700906 if (mProperty != null) {
907 mProperty.set(target, getAnimatedValue());
908 }
Chet Haased953d082010-08-16 17:44:28 -0700909 if (mSetter != null) {
910 try {
Chet Haase7c608f22010-10-22 17:54:04 -0700911 mTmpValueArray[0] = getAnimatedValue();
Chet Haased953d082010-08-16 17:44:28 -0700912 mSetter.invoke(target, mTmpValueArray);
913 } catch (InvocationTargetException e) {
914 Log.e("PropertyValuesHolder", e.toString());
915 } catch (IllegalAccessException e) {
916 Log.e("PropertyValuesHolder", e.toString());
917 }
918 }
919 }
920
921 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700922 * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
Chet Haased953d082010-08-16 17:44:28 -0700923 * to calculate animated values.
924 */
925 void init() {
926 if (mEvaluator == null) {
Chet Haaseb2ab04f2011-01-16 11:03:22 -0800927 // We already handle int and float automatically, but not their Object
Chet Haase7c608f22010-10-22 17:54:04 -0700928 // equivalents
929 mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
930 (mValueType == Float.class) ? sFloatEvaluator :
931 null;
932 }
933 if (mEvaluator != null) {
934 // KeyframeSet knows how to evaluate the common types - only give it a custom
Chet Haaseb2ab04f2011-01-16 11:03:22 -0800935 // evaluator if one has been set on this class
Chet Haase7c608f22010-10-22 17:54:04 -0700936 mKeyframeSet.setEvaluator(mEvaluator);
Chet Haased953d082010-08-16 17:44:28 -0700937 }
938 }
939
940 /**
Chet Haased82c8ac2013-08-26 14:20:16 -0700941 * The TypeEvaluator will be automatically determined based on the type of values
Chet Haased953d082010-08-16 17:44:28 -0700942 * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
943 * desired. This may be important in cases where either the type of the values supplied
944 * do not match the way that they should be interpolated between, or if the values
945 * are of a custom type or one not currently understood by the animation system. Currently,
Chet Haaseb2ab04f2011-01-16 11:03:22 -0800946 * only values of type float and int (and their Object equivalents: Float
Chet Haased953d082010-08-16 17:44:28 -0700947 * and Integer) are correctly interpolated; all other types require setting a TypeEvaluator.
948 * @param evaluator
949 */
Chet Haase7c608f22010-10-22 17:54:04 -0700950 public void setEvaluator(TypeEvaluator evaluator) {
Chet Haased953d082010-08-16 17:44:28 -0700951 mEvaluator = evaluator;
Chet Haase7c608f22010-10-22 17:54:04 -0700952 mKeyframeSet.setEvaluator(evaluator);
Chet Haased953d082010-08-16 17:44:28 -0700953 }
954
955 /**
956 * Function used to calculate the value according to the evaluator set up for
Chet Haasea18a86b2010-09-07 13:20:00 -0700957 * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
Chet Haased953d082010-08-16 17:44:28 -0700958 *
959 * @param fraction The elapsed, interpolated fraction of the animation.
Chet Haased953d082010-08-16 17:44:28 -0700960 */
Chet Haase7c608f22010-10-22 17:54:04 -0700961 void calculateValue(float fraction) {
George Mount16d2c9c2013-09-17 09:07:48 -0700962 Object value = mKeyframeSet.getValue(fraction);
963 mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
Chet Haased953d082010-08-16 17:44:28 -0700964 }
965
966 /**
Chet Haased953d082010-08-16 17:44:28 -0700967 * Sets the name of the property that will be animated. This name is used to derive
968 * a setter function that will be called to set animated values.
969 * For example, a property name of <code>foo</code> will result
970 * in a call to the function <code>setFoo()</code> on the target object. If either
971 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
972 * also be derived and called.
973 *
974 * <p>Note that the setter function derived from this property name
975 * must take the same parameter type as the
976 * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
977 * the setter function will fail.</p>
978 *
979 * @param propertyName The name of the property being animated.
980 */
981 public void setPropertyName(String propertyName) {
982 mPropertyName = propertyName;
983 }
984
985 /**
Chet Haaseb39f0512011-05-24 14:36:40 -0700986 * Sets the property that will be animated.
987 *
988 * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
989 * must exist on the target object specified in that ObjectAnimator.</p>
990 *
991 * @param property The property being animated.
992 */
993 public void setProperty(Property property) {
994 mProperty = property;
995 }
996
997 /**
Chet Haased953d082010-08-16 17:44:28 -0700998 * Gets the name of the property that will be animated. This name will be used to derive
999 * a setter function that will be called to set animated values.
1000 * For example, a property name of <code>foo</code> will result
1001 * in a call to the function <code>setFoo()</code> on the target object. If either
1002 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
1003 * also be derived and called.
1004 */
1005 public String getPropertyName() {
1006 return mPropertyName;
1007 }
1008
1009 /**
Chet Haasea18a86b2010-09-07 13:20:00 -07001010 * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
Chet Haased953d082010-08-16 17:44:28 -07001011 * most recently calculated in calculateValue().
1012 * @return
1013 */
1014 Object getAnimatedValue() {
1015 return mAnimatedValue;
1016 }
Chet Haase7c608f22010-10-22 17:54:04 -07001017
Chet Haasee9140a72011-02-16 16:23:29 -08001018 @Override
1019 public String toString() {
1020 return mPropertyName + ": " + mKeyframeSet.toString();
1021 }
1022
Chet Haase6e0ecb42010-11-03 19:41:18 -07001023 /**
1024 * Utility method to derive a setter/getter method name from a property name, where the
1025 * prefix is typically "set" or "get" and the first letter of the property name is
1026 * capitalized.
1027 *
1028 * @param prefix The precursor to the method name, before the property name begins, typically
1029 * "set" or "get".
1030 * @param propertyName The name of the property that represents the bulk of the method name
1031 * after the prefix. The first letter of this word will be capitalized in the resulting
1032 * method name.
1033 * @return String the property name converted to a method name according to the conventions
1034 * specified above.
1035 */
1036 static String getMethodName(String prefix, String propertyName) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001037 if (propertyName == null || propertyName.length() == 0) {
1038 // shouldn't get here
1039 return prefix;
1040 }
1041 char firstLetter = Character.toUpperCase(propertyName.charAt(0));
Chet Haase6e0ecb42010-11-03 19:41:18 -07001042 String theRest = propertyName.substring(1);
Chet Haase6e0ecb42010-11-03 19:41:18 -07001043 return prefix + firstLetter + theRest;
1044 }
1045
Chet Haase7c608f22010-10-22 17:54:04 -07001046 static class IntPropertyValuesHolder extends PropertyValuesHolder {
1047
Chet Haaseb39f0512011-05-24 14:36:40 -07001048 // Cache JNI functions to avoid looking them up twice
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001049 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1050 new HashMap<Class, HashMap<String, Long>>();
1051 long mJniSetter;
Chet Haaseb39f0512011-05-24 14:36:40 -07001052 private IntProperty mIntProperty;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001053
Chet Haase7c608f22010-10-22 17:54:04 -07001054 IntKeyframeSet mIntKeyframeSet;
1055 int mIntAnimatedValue;
1056
1057 public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) {
1058 super(propertyName);
1059 mValueType = int.class;
1060 mKeyframeSet = keyframeSet;
1061 mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
1062 }
1063
Chet Haaseb39f0512011-05-24 14:36:40 -07001064 public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) {
1065 super(property);
1066 mValueType = int.class;
1067 mKeyframeSet = keyframeSet;
1068 mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
1069 if (property instanceof IntProperty) {
1070 mIntProperty = (IntProperty) mProperty;
1071 }
1072 }
1073
Chet Haase7c608f22010-10-22 17:54:04 -07001074 public IntPropertyValuesHolder(String propertyName, int... values) {
1075 super(propertyName);
1076 setIntValues(values);
Chet Haase691ac262010-11-03 22:38:32 -07001077 }
1078
Chet Haaseb39f0512011-05-24 14:36:40 -07001079 public IntPropertyValuesHolder(Property property, int... values) {
1080 super(property);
1081 setIntValues(values);
1082 if (property instanceof IntProperty) {
1083 mIntProperty = (IntProperty) mProperty;
1084 }
1085 }
1086
Chet Haase691ac262010-11-03 22:38:32 -07001087 @Override
1088 public void setIntValues(int... values) {
1089 super.setIntValues(values);
Chet Haase7c608f22010-10-22 17:54:04 -07001090 mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
1091 }
1092
1093 @Override
1094 void calculateValue(float fraction) {
1095 mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction);
1096 }
1097
1098 @Override
1099 Object getAnimatedValue() {
1100 return mIntAnimatedValue;
1101 }
1102
1103 @Override
1104 public IntPropertyValuesHolder clone() {
1105 IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
1106 newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet;
1107 return newPVH;
1108 }
1109
1110 /**
1111 * Internal function to set the value on the target object, using the setter set up
1112 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1113 * to handle turning the value calculated by ValueAnimator into a value set on the object
1114 * according to the name of the property.
1115 * @param target The target object on which the value is set
1116 */
1117 @Override
1118 void setAnimatedValue(Object target) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001119 if (mIntProperty != null) {
1120 mIntProperty.setValue(target, mIntAnimatedValue);
1121 return;
1122 }
1123 if (mProperty != null) {
1124 mProperty.set(target, mIntAnimatedValue);
1125 return;
1126 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001127 if (mJniSetter != 0) {
1128 nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
1129 return;
1130 }
Chet Haase7c608f22010-10-22 17:54:04 -07001131 if (mSetter != null) {
1132 try {
1133 mTmpValueArray[0] = mIntAnimatedValue;
1134 mSetter.invoke(target, mTmpValueArray);
1135 } catch (InvocationTargetException e) {
1136 Log.e("PropertyValuesHolder", e.toString());
1137 } catch (IllegalAccessException e) {
1138 Log.e("PropertyValuesHolder", e.toString());
1139 }
1140 }
1141 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001142
1143 @Override
1144 void setupSetter(Class targetClass) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001145 if (mProperty != null) {
1146 return;
1147 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001148 // Check new static hashmap<propName, int> for setter method
1149 try {
1150 mPropertyMapLock.writeLock().lock();
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001151 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
Chet Haase6e0ecb42010-11-03 19:41:18 -07001152 if (propertyMap != null) {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001153 Long jniSetter = propertyMap.get(mPropertyName);
1154 if (jniSetter != null) {
1155 mJniSetter = jniSetter;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001156 }
1157 }
1158 if (mJniSetter == 0) {
1159 String methodName = getMethodName("set", mPropertyName);
1160 mJniSetter = nGetIntMethod(targetClass, methodName);
1161 if (mJniSetter != 0) {
1162 if (propertyMap == null) {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001163 propertyMap = new HashMap<String, Long>();
Chet Haase6e0ecb42010-11-03 19:41:18 -07001164 sJNISetterPropertyMap.put(targetClass, propertyMap);
1165 }
1166 propertyMap.put(mPropertyName, mJniSetter);
1167 }
1168 }
1169 } catch (NoSuchMethodError e) {
Chet Haasedb4101c2012-05-03 14:48:07 -07001170 // Couldn't find it via JNI - try reflection next. Probably means the method
1171 // doesn't exist, or the type is wrong. An error will be logged later if
1172 // reflection fails as well.
Chet Haase6e0ecb42010-11-03 19:41:18 -07001173 } finally {
1174 mPropertyMapLock.writeLock().unlock();
1175 }
1176 if (mJniSetter == 0) {
1177 // Couldn't find method through fast JNI approach - just use reflection
1178 super.setupSetter(targetClass);
1179 }
1180 }
Chet Haase7c608f22010-10-22 17:54:04 -07001181 }
1182
1183 static class FloatPropertyValuesHolder extends PropertyValuesHolder {
1184
Chet Haaseb39f0512011-05-24 14:36:40 -07001185 // Cache JNI functions to avoid looking them up twice
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001186 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1187 new HashMap<Class, HashMap<String, Long>>();
1188 long mJniSetter;
Chet Haaseb39f0512011-05-24 14:36:40 -07001189 private FloatProperty mFloatProperty;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001190
Chet Haase7c608f22010-10-22 17:54:04 -07001191 FloatKeyframeSet mFloatKeyframeSet;
1192 float mFloatAnimatedValue;
1193
1194 public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
1195 super(propertyName);
1196 mValueType = float.class;
1197 mKeyframeSet = keyframeSet;
1198 mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
1199 }
1200
Chet Haaseb39f0512011-05-24 14:36:40 -07001201 public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) {
1202 super(property);
1203 mValueType = float.class;
1204 mKeyframeSet = keyframeSet;
1205 mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
1206 if (property instanceof FloatProperty) {
1207 mFloatProperty = (FloatProperty) mProperty;
1208 }
1209 }
1210
Chet Haase7c608f22010-10-22 17:54:04 -07001211 public FloatPropertyValuesHolder(String propertyName, float... values) {
1212 super(propertyName);
1213 setFloatValues(values);
Chet Haase691ac262010-11-03 22:38:32 -07001214 }
1215
Chet Haaseb39f0512011-05-24 14:36:40 -07001216 public FloatPropertyValuesHolder(Property property, float... values) {
1217 super(property);
1218 setFloatValues(values);
1219 if (property instanceof FloatProperty) {
1220 mFloatProperty = (FloatProperty) mProperty;
1221 }
1222 }
1223
Chet Haase691ac262010-11-03 22:38:32 -07001224 @Override
1225 public void setFloatValues(float... values) {
1226 super.setFloatValues(values);
Chet Haase7c608f22010-10-22 17:54:04 -07001227 mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
1228 }
1229
1230 @Override
1231 void calculateValue(float fraction) {
1232 mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
1233 }
1234
1235 @Override
1236 Object getAnimatedValue() {
1237 return mFloatAnimatedValue;
1238 }
1239
1240 @Override
1241 public FloatPropertyValuesHolder clone() {
1242 FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
1243 newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet;
1244 return newPVH;
1245 }
1246
1247 /**
1248 * Internal function to set the value on the target object, using the setter set up
1249 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1250 * to handle turning the value calculated by ValueAnimator into a value set on the object
1251 * according to the name of the property.
1252 * @param target The target object on which the value is set
1253 */
1254 @Override
1255 void setAnimatedValue(Object target) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001256 if (mFloatProperty != null) {
1257 mFloatProperty.setValue(target, mFloatAnimatedValue);
1258 return;
1259 }
1260 if (mProperty != null) {
1261 mProperty.set(target, mFloatAnimatedValue);
1262 return;
1263 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001264 if (mJniSetter != 0) {
1265 nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
1266 return;
1267 }
Chet Haase7c608f22010-10-22 17:54:04 -07001268 if (mSetter != null) {
1269 try {
1270 mTmpValueArray[0] = mFloatAnimatedValue;
1271 mSetter.invoke(target, mTmpValueArray);
1272 } catch (InvocationTargetException e) {
1273 Log.e("PropertyValuesHolder", e.toString());
1274 } catch (IllegalAccessException e) {
1275 Log.e("PropertyValuesHolder", e.toString());
1276 }
1277 }
1278 }
1279
Chet Haase6e0ecb42010-11-03 19:41:18 -07001280 @Override
1281 void setupSetter(Class targetClass) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001282 if (mProperty != null) {
1283 return;
1284 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001285 // Check new static hashmap<propName, int> for setter method
1286 try {
1287 mPropertyMapLock.writeLock().lock();
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001288 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
Chet Haase6e0ecb42010-11-03 19:41:18 -07001289 if (propertyMap != null) {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001290 Long jniSetter = propertyMap.get(mPropertyName);
1291 if (jniSetter != null) {
1292 mJniSetter = jniSetter;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001293 }
1294 }
1295 if (mJniSetter == 0) {
1296 String methodName = getMethodName("set", mPropertyName);
1297 mJniSetter = nGetFloatMethod(targetClass, methodName);
1298 if (mJniSetter != 0) {
1299 if (propertyMap == null) {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001300 propertyMap = new HashMap<String, Long>();
Chet Haase6e0ecb42010-11-03 19:41:18 -07001301 sJNISetterPropertyMap.put(targetClass, propertyMap);
1302 }
1303 propertyMap.put(mPropertyName, mJniSetter);
1304 }
1305 }
1306 } catch (NoSuchMethodError e) {
Chet Haasedb4101c2012-05-03 14:48:07 -07001307 // Couldn't find it via JNI - try reflection next. Probably means the method
1308 // doesn't exist, or the type is wrong. An error will be logged later if
1309 // reflection fails as well.
Chet Haase6e0ecb42010-11-03 19:41:18 -07001310 } finally {
1311 mPropertyMapLock.writeLock().unlock();
1312 }
1313 if (mJniSetter == 0) {
1314 // Couldn't find method through fast JNI approach - just use reflection
1315 super.setupSetter(targetClass);
1316 }
1317 }
1318
Chet Haase7c608f22010-10-22 17:54:04 -07001319 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001320
George Mount4eed5292013-08-30 13:56:01 -07001321 static class MultiFloatValuesHolder extends PropertyValuesHolder {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001322 private long mJniSetter;
1323 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1324 new HashMap<Class, HashMap<String, Long>>();
George Mount4eed5292013-08-30 13:56:01 -07001325
1326 public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
1327 TypeEvaluator evaluator, Object... values) {
1328 super(propertyName);
1329 setConverter(converter);
1330 setObjectValues(values);
1331 setEvaluator(evaluator);
1332 }
1333
1334 public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
1335 TypeEvaluator evaluator, KeyframeSet keyframeSet) {
1336 super(propertyName);
1337 setConverter(converter);
1338 mKeyframeSet = keyframeSet;
1339 setEvaluator(evaluator);
1340 }
1341
1342 /**
1343 * Internal function to set the value on the target object, using the setter set up
1344 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1345 * to handle turning the value calculated by ValueAnimator into a value set on the object
1346 * according to the name of the property.
1347 *
1348 * @param target The target object on which the value is set
1349 */
1350 @Override
1351 void setAnimatedValue(Object target) {
1352 float[] values = (float[]) getAnimatedValue();
1353 int numParameters = values.length;
1354 if (mJniSetter != 0) {
1355 switch (numParameters) {
1356 case 1:
1357 nCallFloatMethod(target, mJniSetter, values[0]);
1358 break;
1359 case 2:
1360 nCallTwoFloatMethod(target, mJniSetter, values[0], values[1]);
1361 break;
1362 case 4:
1363 nCallFourFloatMethod(target, mJniSetter, values[0], values[1],
1364 values[2], values[3]);
1365 break;
1366 default: {
1367 nCallMultipleFloatMethod(target, mJniSetter, values);
1368 break;
1369 }
1370 }
1371 }
1372 }
1373
1374 /**
1375 * Internal function (called from ObjectAnimator) to set up the setter and getter
1376 * prior to running the animation. No getter can be used for multiple parameters.
1377 *
1378 * @param target The object on which the setter exists.
1379 */
1380 @Override
1381 void setupSetterAndGetter(Object target) {
1382 setupSetter(target.getClass());
1383 }
1384
1385 @Override
1386 void setupSetter(Class targetClass) {
1387 if (mJniSetter != 0) {
1388 return;
1389 }
1390 try {
1391 mPropertyMapLock.writeLock().lock();
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001392 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
George Mount4eed5292013-08-30 13:56:01 -07001393 if (propertyMap != null) {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001394 Long jniSetterLong = propertyMap.get(mPropertyName);
1395 if (jniSetterLong != null) {
1396 mJniSetter = jniSetterLong;
George Mount4eed5292013-08-30 13:56:01 -07001397 }
1398 }
1399 if (mJniSetter == 0) {
1400 String methodName = getMethodName("set", mPropertyName);
1401 calculateValue(0f);
1402 float[] values = (float[]) getAnimatedValue();
1403 int numParams = values.length;
1404 try {
1405 mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams);
1406 } catch (NoSuchMethodError e) {
1407 // try without the 'set' prefix
1408 mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName, numParams);
1409 }
1410 if (mJniSetter != 0) {
1411 if (propertyMap == null) {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001412 propertyMap = new HashMap<String, Long>();
George Mount4eed5292013-08-30 13:56:01 -07001413 sJNISetterPropertyMap.put(targetClass, propertyMap);
1414 }
1415 propertyMap.put(mPropertyName, mJniSetter);
1416 }
1417 }
1418 } finally {
1419 mPropertyMapLock.writeLock().unlock();
1420 }
1421 }
1422 }
1423
1424 static class MultiIntValuesHolder extends PropertyValuesHolder {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001425 private long mJniSetter;
1426 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1427 new HashMap<Class, HashMap<String, Long>>();
George Mount4eed5292013-08-30 13:56:01 -07001428
1429 public MultiIntValuesHolder(String propertyName, TypeConverter converter,
1430 TypeEvaluator evaluator, Object... values) {
1431 super(propertyName);
1432 setConverter(converter);
1433 setObjectValues(values);
1434 setEvaluator(evaluator);
1435 }
1436
1437 public MultiIntValuesHolder(String propertyName, TypeConverter converter,
1438 TypeEvaluator evaluator, KeyframeSet keyframeSet) {
1439 super(propertyName);
1440 setConverter(converter);
1441 mKeyframeSet = keyframeSet;
1442 setEvaluator(evaluator);
1443 }
1444
1445 /**
1446 * Internal function to set the value on the target object, using the setter set up
1447 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1448 * to handle turning the value calculated by ValueAnimator into a value set on the object
1449 * according to the name of the property.
1450 *
1451 * @param target The target object on which the value is set
1452 */
1453 @Override
1454 void setAnimatedValue(Object target) {
1455 int[] values = (int[]) getAnimatedValue();
1456 int numParameters = values.length;
1457 if (mJniSetter != 0) {
1458 switch (numParameters) {
1459 case 1:
1460 nCallIntMethod(target, mJniSetter, values[0]);
1461 break;
1462 case 2:
1463 nCallTwoIntMethod(target, mJniSetter, values[0], values[1]);
1464 break;
1465 case 4:
1466 nCallFourIntMethod(target, mJniSetter, values[0], values[1],
1467 values[2], values[3]);
1468 break;
1469 default: {
1470 nCallMultipleIntMethod(target, mJniSetter, values);
1471 break;
1472 }
1473 }
1474 }
1475 }
1476
1477 /**
1478 * Internal function (called from ObjectAnimator) to set up the setter and getter
1479 * prior to running the animation. No getter can be used for multiple parameters.
1480 *
1481 * @param target The object on which the setter exists.
1482 */
1483 @Override
1484 void setupSetterAndGetter(Object target) {
1485 setupSetter(target.getClass());
1486 }
1487
1488 @Override
1489 void setupSetter(Class targetClass) {
1490 if (mJniSetter != 0) {
1491 return;
1492 }
1493 try {
1494 mPropertyMapLock.writeLock().lock();
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001495 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
George Mount4eed5292013-08-30 13:56:01 -07001496 if (propertyMap != null) {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001497 Long jniSetterLong = propertyMap.get(mPropertyName);
1498 if (jniSetterLong != null) {
1499 mJniSetter = jniSetterLong;
George Mount4eed5292013-08-30 13:56:01 -07001500 }
1501 }
1502 if (mJniSetter == 0) {
1503 String methodName = getMethodName("set", mPropertyName);
1504 calculateValue(0f);
1505 int[] values = (int[]) getAnimatedValue();
1506 int numParams = values.length;
1507 try {
1508 mJniSetter = nGetMultipleIntMethod(targetClass, methodName, numParams);
1509 } catch (NoSuchMethodError e) {
1510 // try without the 'set' prefix
1511 mJniSetter = nGetMultipleIntMethod(targetClass, mPropertyName, numParams);
1512 }
1513 if (mJniSetter != 0) {
1514 if (propertyMap == null) {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001515 propertyMap = new HashMap<String, Long>();
George Mount4eed5292013-08-30 13:56:01 -07001516 sJNISetterPropertyMap.put(targetClass, propertyMap);
1517 }
1518 propertyMap.put(mPropertyName, mJniSetter);
1519 }
1520 }
1521 } finally {
1522 mPropertyMapLock.writeLock().unlock();
1523 }
1524 }
1525 }
1526
George Mountc96c7b22013-08-23 13:31:31 -07001527 /* Path interpolation relies on approximating the Path as a series of line segments.
1528 The line segments are recursively divided until there is less than 1/2 pixel error
1529 between the lines and the curve. Each point of the line segment is converted
1530 to a Keyframe and a linear interpolation between Keyframes creates a good approximation
1531 of the curve.
1532
1533 The fraction for each Keyframe is the length along the Path to the point, divided by
1534 the total Path length. Two points may have the same fraction in the case of a move
1535 command causing a disjoint Path.
1536
1537 The value for each Keyframe is either the point as a PointF or one of the x or y
1538 coordinates as an int or float. In the latter case, two Keyframes are generated for
1539 each point that have the same fraction. */
1540
1541 /**
1542 * Returns separate Keyframes arrays for the x and y coordinates along a Path. If
1543 * isInt is true, the Keyframes will be IntKeyframes, otherwise they will be FloatKeyframes.
1544 * The element at index 0 are the x coordinate Keyframes and element at index 1 are the
1545 * y coordinate Keyframes. The returned values can be linearly interpolated and get less
1546 * than 1/2 pixel error.
1547 */
1548 static Keyframe[][] createKeyframes(Path path, boolean isInt) {
1549 if (path == null || path.isEmpty()) {
1550 throw new IllegalArgumentException("The path must not be null or empty");
1551 }
1552 float[] pointComponents = path.approximate(0.5f);
1553
1554 int numPoints = pointComponents.length / 3;
1555
1556 Keyframe[][] keyframes = new Keyframe[2][];
1557 keyframes[0] = new Keyframe[numPoints];
1558 keyframes[1] = new Keyframe[numPoints];
1559 int componentIndex = 0;
1560 for (int i = 0; i < numPoints; i++) {
1561 float fraction = pointComponents[componentIndex++];
1562 float x = pointComponents[componentIndex++];
1563 float y = pointComponents[componentIndex++];
1564 if (isInt) {
1565 keyframes[0][i] = Keyframe.ofInt(fraction, Math.round(x));
1566 keyframes[1][i] = Keyframe.ofInt(fraction, Math.round(y));
1567 } else {
1568 keyframes[0][i] = Keyframe.ofFloat(fraction, x);
1569 keyframes[1][i] = Keyframe.ofFloat(fraction, y);
1570 }
1571 }
1572 return keyframes;
1573 }
1574
1575 /**
1576 * Returns PointF Keyframes for a Path. The resulting points can be linearly interpolated
1577 * with less than 1/2 pixel in error.
1578 */
1579 private static Keyframe[] createKeyframes(Path path) {
1580 if (path == null || path.isEmpty()) {
1581 throw new IllegalArgumentException("The path must not be null or empty");
1582 }
1583 float[] pointComponents = path.approximate(0.5f);
1584
1585 int numPoints = pointComponents.length / 3;
1586
1587 Keyframe[] keyframes = new Keyframe[numPoints];
1588 int componentIndex = 0;
1589 for (int i = 0; i < numPoints; i++) {
1590 float fraction = pointComponents[componentIndex++];
1591 float x = pointComponents[componentIndex++];
1592 float y = pointComponents[componentIndex++];
1593 keyframes[i] = Keyframe.ofObject(fraction, new PointF(x, y));
1594 }
1595 return keyframes;
1596 }
1597
1598 /**
1599 * Convert from PointF to float[] for multi-float setters along a Path.
1600 */
1601 private static class PointFToFloatArray extends TypeConverter<PointF, float[]> {
1602 private float[] mCoordinates = new float[2];
1603
1604 public PointFToFloatArray() {
1605 super(PointF.class, float[].class);
1606 }
1607
1608 @Override
1609 public float[] convert(PointF value) {
1610 mCoordinates[0] = value.x;
1611 mCoordinates[1] = value.y;
1612 return mCoordinates;
1613 }
1614 };
1615
1616 /**
1617 * Convert from PointF to int[] for multi-int setters along a Path.
1618 */
1619 private static class PointFToIntArray extends TypeConverter<PointF, int[]> {
1620 private int[] mCoordinates = new int[2];
1621
1622 public PointFToIntArray() {
1623 super(PointF.class, int[].class);
1624 }
1625
1626 @Override
1627 public int[] convert(PointF value) {
1628 mCoordinates[0] = Math.round(value.x);
1629 mCoordinates[1] = Math.round(value.y);
1630 return mCoordinates;
1631 }
1632 };
1633
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001634 native static private long nGetIntMethod(Class targetClass, String methodName);
1635 native static private long nGetFloatMethod(Class targetClass, String methodName);
1636 native static private long nGetMultipleIntMethod(Class targetClass, String methodName,
George Mount4eed5292013-08-30 13:56:01 -07001637 int numParams);
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001638 native static private long nGetMultipleFloatMethod(Class targetClass, String methodName,
George Mount4eed5292013-08-30 13:56:01 -07001639 int numParams);
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001640 native static private void nCallIntMethod(Object target, long methodID, int arg);
1641 native static private void nCallFloatMethod(Object target, long methodID, float arg);
1642 native static private void nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2);
1643 native static private void nCallFourIntMethod(Object target, long methodID, int arg1, int arg2,
George Mount4eed5292013-08-30 13:56:01 -07001644 int arg3, int arg4);
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001645 native static private void nCallMultipleIntMethod(Object target, long methodID, int[] args);
1646 native static private void nCallTwoFloatMethod(Object target, long methodID, float arg1,
George Mount4eed5292013-08-30 13:56:01 -07001647 float arg2);
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001648 native static private void nCallFourFloatMethod(Object target, long methodID, float arg1,
George Mount4eed5292013-08-30 13:56:01 -07001649 float arg2, float arg3, float arg4);
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001650 native static private void nCallMultipleFloatMethod(Object target, long methodID, float[] args);
George Mountc96c7b22013-08-23 13:31:31 -07001651}