blob: 6ba5b968dfe78e741933e2ece55e764bfd5b640c [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;
Chet Haaseb39f0512011-05-24 14:36:40 -070021import android.util.FloatProperty;
22import android.util.IntProperty;
Chet Haased953d082010-08-16 17:44:28 -070023import android.util.Log;
Doris Liu766431a2016-02-04 22:17:11 +000024import android.util.PathParser;
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;
Yigit Boyard422dc32014-09-25 12:23:35 -070030import java.util.List;
Chet Haased953d082010-08-16 17:44:28 -070031
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 */
George Mount984011f2014-08-21 14:28:01 -070078 Keyframes mKeyframes = 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
Chet Haased953d082010-08-16 17:44:28 -0700107 // Used to pass single value to varargs parameter in setter invocation
Chet Haase6e0ecb42010-11-03 19:41:18 -0700108 final Object[] mTmpValueArray = new Object[1];
Chet Haased953d082010-08-16 17:44:28 -0700109
110 /**
111 * The type evaluator used to calculate the animated values. This evaluator is determined
112 * automatically based on the type of the start/end objects passed into the constructor,
Chet Haaseb2ab04f2011-01-16 11:03:22 -0800113 * but the system only knows about the primitive types int and float. Any other
Chet Haased953d082010-08-16 17:44:28 -0700114 * type will need to set the evaluator to a custom evaluator for that type.
115 */
116 private TypeEvaluator mEvaluator;
117
118 /**
119 * The value most recently calculated by calculateValue(). This is set during
Chet Haasea18a86b2010-09-07 13:20:00 -0700120 * that function and might be retrieved later either by ValueAnimator.animatedValue() or
121 * by the property-setting logic in ObjectAnimator.animatedValue().
Chet Haased953d082010-08-16 17:44:28 -0700122 */
123 private Object mAnimatedValue;
124
125 /**
George Mount16d2c9c2013-09-17 09:07:48 -0700126 * Converts from the source Object type to the setter Object type.
127 */
128 private TypeConverter mConverter;
129
130 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700131 * Internal utility constructor, used by the factory methods to set the property name.
132 * @param propertyName The name of the property for this holder.
Chet Haased953d082010-08-16 17:44:28 -0700133 */
Chet Haase2794eb32010-10-12 16:29:28 -0700134 private PropertyValuesHolder(String propertyName) {
135 mPropertyName = propertyName;
Chet Haased953d082010-08-16 17:44:28 -0700136 }
137
138 /**
Chet Haaseb39f0512011-05-24 14:36:40 -0700139 * Internal utility constructor, used by the factory methods to set the property.
140 * @param property The property for this holder.
141 */
142 private PropertyValuesHolder(Property property) {
143 mProperty = property;
144 if (property != null) {
145 mPropertyName = property.getName();
146 }
147 }
148
149 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700150 * Constructs and returns a PropertyValuesHolder with a given property name and
151 * set of int values.
152 * @param propertyName The name of the property being animated.
153 * @param values The values that the named property will animate between.
154 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
155 */
156 public static PropertyValuesHolder ofInt(String propertyName, int... values) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700157 return new IntPropertyValuesHolder(propertyName, values);
158 }
159
160 /**
161 * Constructs and returns a PropertyValuesHolder with a given property and
162 * set of int values.
163 * @param property The property being animated. Should not be null.
164 * @param values The values that the property will animate between.
165 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
166 */
167 public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
168 return new IntPropertyValuesHolder(property, values);
Chet Haase2794eb32010-10-12 16:29:28 -0700169 }
170
171 /**
172 * Constructs and returns a PropertyValuesHolder with a given property name and
George Mount4eed5292013-08-30 13:56:01 -0700173 * set of <code>int[]</code> values. At least two <code>int[]</code> values must be supplied,
174 * a start and end value. If more values are supplied, the values will be animated from the
175 * start, through all intermediate values to the end value. When used with ObjectAnimator,
176 * the elements of the array represent the parameters of the setter function.
177 *
George Mountc96c7b22013-08-23 13:31:31 -0700178 * @param propertyName The name of the property being animated. Can also be the
179 * case-sensitive name of the entire setter method. Should not be null.
George Mount4eed5292013-08-30 13:56:01 -0700180 * @param values The values that the property will animate between.
181 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
182 * @see IntArrayEvaluator#IntArrayEvaluator(int[])
183 * @see ObjectAnimator#ofMultiInt(Object, String, TypeConverter, TypeEvaluator, Object[])
184 */
185 public static PropertyValuesHolder ofMultiInt(String propertyName, int[][] values) {
186 if (values.length < 2) {
187 throw new IllegalArgumentException("At least 2 values must be supplied");
188 }
189 int numParameters = 0;
190 for (int i = 0; i < values.length; i++) {
191 if (values[i] == null) {
192 throw new IllegalArgumentException("values must not be null");
193 }
194 int length = values[i].length;
195 if (i == 0) {
196 numParameters = length;
197 } else if (length != numParameters) {
198 throw new IllegalArgumentException("Values must all have the same length");
199 }
200 }
201 IntArrayEvaluator evaluator = new IntArrayEvaluator(new int[numParameters]);
202 return new MultiIntValuesHolder(propertyName, null, evaluator, (Object[]) values);
203 }
204
205 /**
George Mountc96c7b22013-08-23 13:31:31 -0700206 * Constructs and returns a PropertyValuesHolder with a given property name to use
207 * as a multi-int setter. The values are animated along the path, with the first
208 * parameter of the setter set to the x coordinate and the second set to the y coordinate.
209 *
210 * @param propertyName The name of the property being animated. Can also be the
211 * case-sensitive name of the entire setter method. Should not be null.
212 * The setter must take exactly two <code>int</code> parameters.
213 * @param path The Path along which the values should be animated.
214 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
215 * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...)
216 */
217 public static PropertyValuesHolder ofMultiInt(String propertyName, Path path) {
George Mount984011f2014-08-21 14:28:01 -0700218 Keyframes keyframes = KeyframeSet.ofPath(path);
George Mountc96c7b22013-08-23 13:31:31 -0700219 PointFToIntArray converter = new PointFToIntArray();
George Mount984011f2014-08-21 14:28:01 -0700220 return new MultiIntValuesHolder(propertyName, converter, null, keyframes);
George Mountc96c7b22013-08-23 13:31:31 -0700221 }
222
223 /**
George Mount4eed5292013-08-30 13:56:01 -0700224 * Constructs and returns a PropertyValuesHolder with a given property and
225 * set of Object values for use with ObjectAnimator multi-value setters. The Object
226 * values are converted to <code>int[]</code> using the converter.
227 *
228 * @param propertyName The property being animated or complete name of the setter.
229 * Should not be null.
230 * @param converter Used to convert the animated value to setter parameters.
231 * @param evaluator A TypeEvaluator that will be called on each animation frame to
232 * provide the necessary interpolation between the Object values to derive the animated
233 * value.
234 * @param values The values that the property will animate between.
235 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
236 * @see ObjectAnimator#ofMultiInt(Object, String, TypeConverter, TypeEvaluator, Object[])
237 * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...)
238 */
239 public static <V> PropertyValuesHolder ofMultiInt(String propertyName,
240 TypeConverter<V, int[]> converter, TypeEvaluator<V> evaluator, V... values) {
241 return new MultiIntValuesHolder(propertyName, converter, evaluator, values);
242 }
243
244 /**
245 * Constructs and returns a PropertyValuesHolder object with the specified property name or
246 * setter name for use in a multi-int setter function using ObjectAnimator. The values can be
247 * of any type, but the type should be consistent so that the supplied
248 * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The
249 * <code>converter</code> converts the values to parameters in the setter function.
250 *
251 * <p>At least two values must be supplied, a start and an end value.</p>
252 *
253 * @param propertyName The name of the property to associate with the set of values. This
254 * may also be the complete name of a setter function.
255 * @param converter Converts <code>values</code> into int parameters for the setter.
256 * Can be null if the Keyframes have int[] values.
257 * @param evaluator Used to interpolate between values.
258 * @param values The values at specific fractional times to evaluate between
259 * @return A PropertyValuesHolder for a multi-int parameter setter.
260 */
261 public static <T> PropertyValuesHolder ofMultiInt(String propertyName,
262 TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, Keyframe... values) {
263 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
264 return new MultiIntValuesHolder(propertyName, converter, evaluator, keyframeSet);
265 }
266
267 /**
268 * Constructs and returns a PropertyValuesHolder with a given property name and
Chet Haase2794eb32010-10-12 16:29:28 -0700269 * set of float values.
270 * @param propertyName The name of the property being animated.
271 * @param values The values that the named property will animate between.
272 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
273 */
274 public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700275 return new FloatPropertyValuesHolder(propertyName, values);
276 }
277
278 /**
279 * Constructs and returns a PropertyValuesHolder with a given property and
280 * set of float values.
281 * @param property The property being animated. Should not be null.
282 * @param values The values that the property will animate between.
283 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
284 */
285 public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
286 return new FloatPropertyValuesHolder(property, values);
Chet Haase2794eb32010-10-12 16:29:28 -0700287 }
288
289 /**
290 * Constructs and returns a PropertyValuesHolder with a given property name and
George Mount4eed5292013-08-30 13:56:01 -0700291 * set of <code>float[]</code> values. At least two <code>float[]</code> values must be supplied,
292 * a start and end value. If more values are supplied, the values will be animated from the
293 * start, through all intermediate values to the end value. When used with ObjectAnimator,
294 * the elements of the array represent the parameters of the setter function.
295 *
George Mountc96c7b22013-08-23 13:31:31 -0700296 * @param propertyName The name of the property being animated. Can also be the
297 * case-sensitive name of the entire setter method. Should not be null.
George Mount4eed5292013-08-30 13:56:01 -0700298 * @param values The values that the property will animate between.
299 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
300 * @see FloatArrayEvaluator#FloatArrayEvaluator(float[])
301 * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[])
302 */
303 public static PropertyValuesHolder ofMultiFloat(String propertyName, float[][] values) {
304 if (values.length < 2) {
305 throw new IllegalArgumentException("At least 2 values must be supplied");
306 }
307 int numParameters = 0;
308 for (int i = 0; i < values.length; i++) {
309 if (values[i] == null) {
310 throw new IllegalArgumentException("values must not be null");
311 }
312 int length = values[i].length;
313 if (i == 0) {
314 numParameters = length;
315 } else if (length != numParameters) {
316 throw new IllegalArgumentException("Values must all have the same length");
317 }
318 }
319 FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[numParameters]);
320 return new MultiFloatValuesHolder(propertyName, null, evaluator, (Object[]) values);
321 }
322
323 /**
George Mountc96c7b22013-08-23 13:31:31 -0700324 * Constructs and returns a PropertyValuesHolder with a given property name to use
325 * as a multi-float setter. The values are animated along the path, with the first
326 * parameter of the setter set to the x coordinate and the second set to the y coordinate.
327 *
328 * @param propertyName The name of the property being animated. Can also be the
329 * case-sensitive name of the entire setter method. Should not be null.
330 * The setter must take exactly two <code>float</code> parameters.
331 * @param path The Path along which the values should be animated.
332 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
333 * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...)
334 */
335 public static PropertyValuesHolder ofMultiFloat(String propertyName, Path path) {
George Mount984011f2014-08-21 14:28:01 -0700336 Keyframes keyframes = KeyframeSet.ofPath(path);
George Mountc96c7b22013-08-23 13:31:31 -0700337 PointFToFloatArray converter = new PointFToFloatArray();
George Mount984011f2014-08-21 14:28:01 -0700338 return new MultiFloatValuesHolder(propertyName, converter, null, keyframes);
George Mountc96c7b22013-08-23 13:31:31 -0700339 }
340
341 /**
George Mount4eed5292013-08-30 13:56:01 -0700342 * Constructs and returns a PropertyValuesHolder with a given property and
343 * set of Object values for use with ObjectAnimator multi-value setters. The Object
344 * values are converted to <code>float[]</code> using the converter.
345 *
346 * @param propertyName The property being animated or complete name of the setter.
347 * Should not be null.
348 * @param converter Used to convert the animated value to setter parameters.
349 * @param evaluator A TypeEvaluator that will be called on each animation frame to
350 * provide the necessary interpolation between the Object values to derive the animated
351 * value.
352 * @param values The values that the property will animate between.
353 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
354 * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[])
355 */
356 public static <V> PropertyValuesHolder ofMultiFloat(String propertyName,
357 TypeConverter<V, float[]> converter, TypeEvaluator<V> evaluator, V... values) {
358 return new MultiFloatValuesHolder(propertyName, converter, evaluator, values);
359 }
360
361 /**
362 * Constructs and returns a PropertyValuesHolder object with the specified property name or
363 * setter name for use in a multi-float setter function using ObjectAnimator. The values can be
364 * of any type, but the type should be consistent so that the supplied
365 * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The
366 * <code>converter</code> converts the values to parameters in the setter function.
367 *
368 * <p>At least two values must be supplied, a start and an end value.</p>
369 *
370 * @param propertyName The name of the property to associate with the set of values. This
371 * may also be the complete name of a setter function.
372 * @param converter Converts <code>values</code> into float parameters for the setter.
373 * Can be null if the Keyframes have float[] values.
374 * @param evaluator Used to interpolate between values.
375 * @param values The values at specific fractional times to evaluate between
376 * @return A PropertyValuesHolder for a multi-float parameter setter.
377 */
378 public static <T> PropertyValuesHolder ofMultiFloat(String propertyName,
379 TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, Keyframe... values) {
380 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
381 return new MultiFloatValuesHolder(propertyName, converter, evaluator, keyframeSet);
382 }
383
384 /**
385 * Constructs and returns a PropertyValuesHolder with a given property name and
Chet Haase2794eb32010-10-12 16:29:28 -0700386 * set of Object values. This variant also takes a TypeEvaluator because the system
Chet Haaseb39f0512011-05-24 14:36:40 -0700387 * cannot automatically interpolate between objects of unknown type.
Chet Haase2794eb32010-10-12 16:29:28 -0700388 *
389 * @param propertyName The name of the property being animated.
390 * @param evaluator A TypeEvaluator that will be called on each animation frame to
Chet Haaseb39f0512011-05-24 14:36:40 -0700391 * provide the necessary interpolation between the Object values to derive the animated
Chet Haase2794eb32010-10-12 16:29:28 -0700392 * value.
393 * @param values The values that the named property will animate between.
394 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
395 */
396 public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
397 Object... values) {
398 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
399 pvh.setObjectValues(values);
400 pvh.setEvaluator(evaluator);
401 return pvh;
402 }
403
404 /**
George Mountc96c7b22013-08-23 13:31:31 -0700405 * Constructs and returns a PropertyValuesHolder with a given property name and
406 * a Path along which the values should be animated. This variant supports a
407 * <code>TypeConverter</code> to convert from <code>PointF</code> to the target
408 * type.
409 *
George Mount984011f2014-08-21 14:28:01 -0700410 * <p>The PointF passed to <code>converter</code> or <code>property</code>, if
411 * <code>converter</code> is <code>null</code>, is reused on each animation frame and should
412 * not be stored by the setter or TypeConverter.</p>
413 *
George Mountc96c7b22013-08-23 13:31:31 -0700414 * @param propertyName The name of the property being animated.
415 * @param converter Converts a PointF to the type associated with the setter. May be
416 * null if conversion is unnecessary.
417 * @param path The Path along which the values should be animated.
418 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
419 */
420 public static PropertyValuesHolder ofObject(String propertyName,
421 TypeConverter<PointF, ?> converter, Path path) {
George Mount984011f2014-08-21 14:28:01 -0700422 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
423 pvh.mKeyframes = KeyframeSet.ofPath(path);
424 pvh.mValueType = PointF.class;
George Mountc96c7b22013-08-23 13:31:31 -0700425 pvh.setConverter(converter);
426 return pvh;
427 }
428
429 /**
Chet Haaseb39f0512011-05-24 14:36:40 -0700430 * Constructs and returns a PropertyValuesHolder with a given property and
431 * set of Object values. This variant also takes a TypeEvaluator because the system
432 * cannot automatically interpolate between objects of unknown type.
433 *
434 * @param property The property being animated. Should not be null.
435 * @param evaluator A TypeEvaluator that will be called on each animation frame to
436 * provide the necessary interpolation between the Object values to derive the animated
437 * value.
438 * @param values The values that the property will animate between.
439 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
440 */
441 public static <V> PropertyValuesHolder ofObject(Property property,
442 TypeEvaluator<V> evaluator, V... values) {
443 PropertyValuesHolder pvh = new PropertyValuesHolder(property);
444 pvh.setObjectValues(values);
445 pvh.setEvaluator(evaluator);
446 return pvh;
447 }
448
449 /**
George Mount16d2c9c2013-09-17 09:07:48 -0700450 * Constructs and returns a PropertyValuesHolder with a given property and
451 * set of Object values. This variant also takes a TypeEvaluator because the system
452 * cannot automatically interpolate between objects of unknown type. This variant also
453 * takes a <code>TypeConverter</code> to convert from animated values to the type
454 * of the property. If only one value is supplied, the <code>TypeConverter</code>
George Mount42516d12014-05-19 15:49:29 -0700455 * must be a {@link android.animation.BidirectionalTypeConverter} to retrieve the current
George Mount16d2c9c2013-09-17 09:07:48 -0700456 * value.
457 *
458 * @param property The property being animated. Should not be null.
459 * @param converter Converts the animated object to the Property type.
460 * @param evaluator A TypeEvaluator that will be called on each animation frame to
461 * provide the necessary interpolation between the Object values to derive the animated
462 * value.
463 * @param values The values that the property will animate between.
464 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
465 * @see #setConverter(TypeConverter)
466 * @see TypeConverter
467 */
468 public static <T, V> PropertyValuesHolder ofObject(Property<?, V> property,
469 TypeConverter<T, V> converter, TypeEvaluator<T> evaluator, T... values) {
470 PropertyValuesHolder pvh = new PropertyValuesHolder(property);
471 pvh.setConverter(converter);
472 pvh.setObjectValues(values);
473 pvh.setEvaluator(evaluator);
474 return pvh;
475 }
476
477 /**
George Mountc96c7b22013-08-23 13:31:31 -0700478 * Constructs and returns a PropertyValuesHolder with a given property and
479 * a Path along which the values should be animated. This variant supports a
480 * <code>TypeConverter</code> to convert from <code>PointF</code> to the target
481 * type.
482 *
George Mount984011f2014-08-21 14:28:01 -0700483 * <p>The PointF passed to <code>converter</code> or <code>property</code>, if
484 * <code>converter</code> is <code>null</code>, is reused on each animation frame and should
485 * not be stored by the setter or TypeConverter.</p>
486 *
George Mountc96c7b22013-08-23 13:31:31 -0700487 * @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) {
George Mount984011f2014-08-21 14:28:01 -0700495 PropertyValuesHolder pvh = new PropertyValuesHolder(property);
496 pvh.mKeyframes = KeyframeSet.ofPath(path);
497 pvh.mValueType = PointF.class;
George Mountc96c7b22013-08-23 13:31:31 -0700498 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);
George Mount984011f2014-08-21 14:28:01 -0700523 return ofKeyframes(propertyName, keyframeSet);
Chet Haased953d082010-08-16 17:44:28 -0700524 }
525
526 /**
Chet Haaseb39f0512011-05-24 14:36:40 -0700527 * Constructs and returns a PropertyValuesHolder object with the specified property and set
528 * of values. These values can be of any type, but the type should be consistent so that
529 * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
530 * the common type.
531 * <p>If there is only one value, it is assumed to be the end value of an animation,
532 * and an initial value will be derived, if possible, by calling the property's
533 * {@link android.util.Property#get(Object)} function.
534 * Also, if any value is null, the value will be filled in when the animation
535 * starts in the same way. This mechanism of automatically getting null values only works
536 * if the PropertyValuesHolder object is used in conjunction with
537 * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has
538 * no way of determining what the value should be.
539 * @param property The property associated with this set of values. Should not be null.
540 * @param values The set of values to animate between.
541 */
542 public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
543 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
George Mount984011f2014-08-21 14:28:01 -0700544 return ofKeyframes(property, keyframeSet);
545 }
546
547 static PropertyValuesHolder ofKeyframes(String propertyName, Keyframes keyframes) {
548 if (keyframes instanceof Keyframes.IntKeyframes) {
549 return new IntPropertyValuesHolder(propertyName, (Keyframes.IntKeyframes) keyframes);
550 } else if (keyframes instanceof Keyframes.FloatKeyframes) {
551 return new FloatPropertyValuesHolder(propertyName,
552 (Keyframes.FloatKeyframes) keyframes);
553 } else {
554 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
555 pvh.mKeyframes = keyframes;
556 pvh.mValueType = keyframes.getType();
557 return pvh;
Chet Haaseb39f0512011-05-24 14:36:40 -0700558 }
George Mount984011f2014-08-21 14:28:01 -0700559 }
560
561 static PropertyValuesHolder ofKeyframes(Property property, Keyframes keyframes) {
562 if (keyframes instanceof Keyframes.IntKeyframes) {
563 return new IntPropertyValuesHolder(property, (Keyframes.IntKeyframes) keyframes);
564 } else if (keyframes instanceof Keyframes.FloatKeyframes) {
565 return new FloatPropertyValuesHolder(property, (Keyframes.FloatKeyframes) keyframes);
566 } else {
Chet Haaseb39f0512011-05-24 14:36:40 -0700567 PropertyValuesHolder pvh = new PropertyValuesHolder(property);
George Mount984011f2014-08-21 14:28:01 -0700568 pvh.mKeyframes = keyframes;
569 pvh.mValueType = keyframes.getType();
Chet Haaseb39f0512011-05-24 14:36:40 -0700570 return pvh;
571 }
572 }
573
574 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700575 * Set the animated values for this object to this set of ints.
Chet Haased953d082010-08-16 17:44:28 -0700576 * If there is only one value, it is assumed to be the end value of an animation,
577 * and an initial value will be derived, if possible, by calling a getter function
578 * on the object. Also, if any value is null, the value will be filled in when the animation
579 * starts in the same way. This mechanism of automatically getting null values only works
580 * if the PropertyValuesHolder object is used in conjunction
Chet Haase1a8e4042010-12-07 11:29:39 -0800581 * {@link ObjectAnimator}, and with a getter function
582 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
Chet Haased953d082010-08-16 17:44:28 -0700583 * no way of determining what the value should be.
Chet Haase2794eb32010-10-12 16:29:28 -0700584 *
585 * @param values One or more values that the animation will animate between.
Chet Haased953d082010-08-16 17:44:28 -0700586 */
Chet Haase2794eb32010-10-12 16:29:28 -0700587 public void setIntValues(int... values) {
588 mValueType = int.class;
George Mount984011f2014-08-21 14:28:01 -0700589 mKeyframes = KeyframeSet.ofInt(values);
Chet Haase2794eb32010-10-12 16:29:28 -0700590 }
Chet Haased953d082010-08-16 17:44:28 -0700591
Chet Haase2794eb32010-10-12 16:29:28 -0700592 /**
593 * Set the animated values for this object to this set of floats.
594 * If there is only one value, it is assumed to be the end value of an animation,
595 * and an initial value will be derived, if possible, by calling a getter function
596 * on the object. Also, if any value is null, the value will be filled in when the animation
597 * starts in the same way. This mechanism of automatically getting null values only works
598 * if the PropertyValuesHolder object is used in conjunction
Chet Haase1a8e4042010-12-07 11:29:39 -0800599 * {@link ObjectAnimator}, and with a getter function
600 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
Chet Haase2794eb32010-10-12 16:29:28 -0700601 * no way of determining what the value should be.
602 *
603 * @param values One or more values that the animation will animate between.
604 */
605 public void setFloatValues(float... values) {
606 mValueType = float.class;
George Mount984011f2014-08-21 14:28:01 -0700607 mKeyframes = KeyframeSet.ofFloat(values);
Chet Haase2794eb32010-10-12 16:29:28 -0700608 }
609
610 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700611 * Set the animated values for this object to this set of Keyframes.
612 *
613 * @param values One or more values that the animation will animate between.
614 */
615 public void setKeyframes(Keyframe... values) {
616 int numKeyframes = values.length;
617 Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
618 mValueType = ((Keyframe)values[0]).getType();
619 for (int i = 0; i < numKeyframes; ++i) {
620 keyframes[i] = (Keyframe)values[i];
Chet Haased953d082010-08-16 17:44:28 -0700621 }
George Mount984011f2014-08-21 14:28:01 -0700622 mKeyframes = new KeyframeSet(keyframes);
Chet Haased953d082010-08-16 17:44:28 -0700623 }
624
Chet Haase2794eb32010-10-12 16:29:28 -0700625 /**
626 * Set the animated values for this object to this set of Objects.
627 * If there is only one value, it is assumed to be the end value of an animation,
628 * and an initial value will be derived, if possible, by calling a getter function
629 * on the object. Also, if any value is null, the value will be filled in when the animation
630 * starts in the same way. This mechanism of automatically getting null values only works
631 * if the PropertyValuesHolder object is used in conjunction
Chet Haase1a8e4042010-12-07 11:29:39 -0800632 * {@link ObjectAnimator}, and with a getter function
633 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
Chet Haase2794eb32010-10-12 16:29:28 -0700634 * no way of determining what the value should be.
635 *
636 * @param values One or more values that the animation will animate between.
637 */
638 public void setObjectValues(Object... values) {
639 mValueType = values[0].getClass();
George Mount984011f2014-08-21 14:28:01 -0700640 mKeyframes = KeyframeSet.ofObject(values);
Chet Haase4ae3e6a2014-07-14 18:28:28 -0700641 if (mEvaluator != null) {
George Mount984011f2014-08-21 14:28:01 -0700642 mKeyframes.setEvaluator(mEvaluator);
Chet Haase4ae3e6a2014-07-14 18:28:28 -0700643 }
Chet Haase2794eb32010-10-12 16:29:28 -0700644 }
Chet Haased953d082010-08-16 17:44:28 -0700645
646 /**
George Mount16d2c9c2013-09-17 09:07:48 -0700647 * Sets the converter to convert from the values type to the setter's parameter type.
George Mount42516d12014-05-19 15:49:29 -0700648 * If only one value is supplied, <var>converter</var> must be a
649 * {@link android.animation.BidirectionalTypeConverter}.
George Mount16d2c9c2013-09-17 09:07:48 -0700650 * @param converter The converter to use to convert values.
651 */
652 public void setConverter(TypeConverter converter) {
653 mConverter = converter;
654 }
655
656 /**
Chet Haased953d082010-08-16 17:44:28 -0700657 * Determine the setter or getter function using the JavaBeans convention of setFoo or
658 * getFoo for a property named 'foo'. This function figures out what the name of the
659 * function should be and uses reflection to find the Method with that name on the
660 * target object.
661 *
662 * @param targetClass The class to search for the method
663 * @param prefix "set" or "get", depending on whether we need a setter or getter.
664 * @param valueType The type of the parameter (in the case of a setter). This type
665 * is derived from the values set on this PropertyValuesHolder. This type is used as
666 * a first guess at the parameter type, but we check for methods with several different
667 * types to avoid problems with slight mis-matches between supplied values and actual
668 * value types used on the setter.
669 * @return Method the method associated with mPropertyName.
670 */
671 private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
672 // TODO: faster implementation...
673 Method returnVal = null;
Chet Haase6e0ecb42010-11-03 19:41:18 -0700674 String methodName = getMethodName(prefix, mPropertyName);
Chet Haased953d082010-08-16 17:44:28 -0700675 Class args[] = null;
676 if (valueType == null) {
677 try {
678 returnVal = targetClass.getMethod(methodName, args);
679 } catch (NoSuchMethodException e) {
Chet Haasedb4101c2012-05-03 14:48:07 -0700680 // Swallow the error, log it later
Chet Haased953d082010-08-16 17:44:28 -0700681 }
682 } else {
683 args = new Class[1];
684 Class typeVariants[];
George Mount16d2c9c2013-09-17 09:07:48 -0700685 if (valueType.equals(Float.class)) {
Chet Haased953d082010-08-16 17:44:28 -0700686 typeVariants = FLOAT_VARIANTS;
George Mount16d2c9c2013-09-17 09:07:48 -0700687 } else if (valueType.equals(Integer.class)) {
Chet Haased953d082010-08-16 17:44:28 -0700688 typeVariants = INTEGER_VARIANTS;
George Mount16d2c9c2013-09-17 09:07:48 -0700689 } else if (valueType.equals(Double.class)) {
Chet Haased953d082010-08-16 17:44:28 -0700690 typeVariants = DOUBLE_VARIANTS;
691 } else {
692 typeVariants = new Class[1];
George Mount16d2c9c2013-09-17 09:07:48 -0700693 typeVariants[0] = valueType;
Chet Haased953d082010-08-16 17:44:28 -0700694 }
695 for (Class typeVariant : typeVariants) {
696 args[0] = typeVariant;
697 try {
698 returnVal = targetClass.getMethod(methodName, args);
George Mount16d2c9c2013-09-17 09:07:48 -0700699 if (mConverter == null) {
700 // change the value type to suit
701 mValueType = typeVariant;
702 }
Chet Haased953d082010-08-16 17:44:28 -0700703 return returnVal;
704 } catch (NoSuchMethodException e) {
705 // Swallow the error and keep trying other variants
706 }
707 }
Chet Haase602e4d32010-08-16 08:57:23 -0700708 // If we got here, then no appropriate function was found
Chet Haasedb4101c2012-05-03 14:48:07 -0700709 }
710
711 if (returnVal == null) {
712 Log.w("PropertyValuesHolder", "Method " +
George Mount16d2c9c2013-09-17 09:07:48 -0700713 getMethodName(prefix, mPropertyName) + "() with type " + valueType +
Chet Haasedb4101c2012-05-03 14:48:07 -0700714 " not found on target class " + targetClass);
Chet Haased953d082010-08-16 17:44:28 -0700715 }
Chet Haase602e4d32010-08-16 08:57:23 -0700716
Chet Haased953d082010-08-16 17:44:28 -0700717 return returnVal;
718 }
719
720
721 /**
722 * Returns the setter or getter requested. This utility function checks whether the
723 * requested method exists in the propertyMapMap cache. If not, it calls another
724 * utility function to request the Method from the targetClass directly.
725 * @param targetClass The Class on which the requested method should exist.
726 * @param propertyMapMap The cache of setters/getters derived so far.
727 * @param prefix "set" or "get", for the setter or getter.
728 * @param valueType The type of parameter passed into the method (null for getter).
729 * @return Method the method associated with mPropertyName.
730 */
731 private Method setupSetterOrGetter(Class targetClass,
732 HashMap<Class, HashMap<String, Method>> propertyMapMap,
733 String prefix, Class valueType) {
734 Method setterOrGetter = null;
George Mount691487d2014-11-10 16:46:31 -0800735 synchronized(propertyMapMap) {
Chet Haased953d082010-08-16 17:44:28 -0700736 // Have to lock property map prior to reading it, to guard against
737 // another thread putting something in there after we've checked it
738 // but before we've added an entry to it
Chet Haased953d082010-08-16 17:44:28 -0700739 HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
George Mount691487d2014-11-10 16:46:31 -0800740 boolean wasInMap = false;
Chet Haased953d082010-08-16 17:44:28 -0700741 if (propertyMap != null) {
George Mount691487d2014-11-10 16:46:31 -0800742 wasInMap = propertyMap.containsKey(mPropertyName);
743 if (wasInMap) {
744 setterOrGetter = propertyMap.get(mPropertyName);
745 }
Chet Haased953d082010-08-16 17:44:28 -0700746 }
George Mount691487d2014-11-10 16:46:31 -0800747 if (!wasInMap) {
Chet Haased953d082010-08-16 17:44:28 -0700748 setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
749 if (propertyMap == null) {
750 propertyMap = new HashMap<String, Method>();
751 propertyMapMap.put(targetClass, propertyMap);
752 }
753 propertyMap.put(mPropertyName, setterOrGetter);
754 }
Chet Haased953d082010-08-16 17:44:28 -0700755 }
756 return setterOrGetter;
757 }
758
759 /**
760 * Utility function to get the setter from targetClass
761 * @param targetClass The Class on which the requested method should exist.
762 */
Chet Haase6e0ecb42010-11-03 19:41:18 -0700763 void setupSetter(Class targetClass) {
George Mount16d2c9c2013-09-17 09:07:48 -0700764 Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
765 mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
Chet Haased953d082010-08-16 17:44:28 -0700766 }
767
768 /**
769 * Utility function to get the getter from targetClass
770 */
771 private void setupGetter(Class targetClass) {
772 mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
773 }
774
775 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700776 * Internal function (called from ObjectAnimator) to set up the setter and getter
Chet Haased953d082010-08-16 17:44:28 -0700777 * prior to running the animation. If the setter has not been manually set for this
778 * object, it will be derived automatically given the property name, target object, and
779 * types of values supplied. If no getter has been set, it will be supplied iff any of the
780 * supplied values was null. If there is a null value, then the getter (supplied or derived)
781 * will be called to set those null values to the current value of the property
782 * on the target object.
783 * @param target The object on which the setter (and possibly getter) exist.
784 */
785 void setupSetterAndGetter(Object target) {
George Mount984011f2014-08-21 14:28:01 -0700786 mKeyframes.invalidateCache();
Chet Haaseb39f0512011-05-24 14:36:40 -0700787 if (mProperty != null) {
788 // check to make sure that mProperty is on the class of target
789 try {
George Mount16d2c9c2013-09-17 09:07:48 -0700790 Object testValue = null;
Yigit Boyard422dc32014-09-25 12:23:35 -0700791 List<Keyframe> keyframes = mKeyframes.getKeyframes();
George Mount984011f2014-08-21 14:28:01 -0700792 int keyframeCount = keyframes == null ? 0 : keyframes.size();
793 for (int i = 0; i < keyframeCount; i++) {
794 Keyframe kf = keyframes.get(i);
Yigit Boyar8619f482014-07-15 17:28:07 -0700795 if (!kf.hasValue() || kf.valueWasSetOnStart()) {
George Mount16d2c9c2013-09-17 09:07:48 -0700796 if (testValue == null) {
797 testValue = convertBack(mProperty.get(target));
798 }
799 kf.setValue(testValue);
Yigit Boyar8619f482014-07-15 17:28:07 -0700800 kf.setValueWasSetOnStart(true);
Chet Haaseb39f0512011-05-24 14:36:40 -0700801 }
802 }
803 return;
804 } catch (ClassCastException e) {
Chet Haasedb4101c2012-05-03 14:48:07 -0700805 Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
Chet Haaseb39f0512011-05-24 14:36:40 -0700806 ") on target object " + target + ". Trying reflection instead");
807 mProperty = null;
808 }
809 }
George Mount691487d2014-11-10 16:46:31 -0800810 // We can't just say 'else' here because the catch statement sets mProperty to null.
811 if (mProperty == null) {
812 Class targetClass = target.getClass();
813 if (mSetter == null) {
814 setupSetter(targetClass);
815 }
816 List<Keyframe> keyframes = mKeyframes.getKeyframes();
817 int keyframeCount = keyframes == null ? 0 : keyframes.size();
818 for (int i = 0; i < keyframeCount; i++) {
819 Keyframe kf = keyframes.get(i);
820 if (!kf.hasValue() || kf.valueWasSetOnStart()) {
Chet Haasedb4101c2012-05-03 14:48:07 -0700821 if (mGetter == null) {
George Mount691487d2014-11-10 16:46:31 -0800822 setupGetter(targetClass);
823 if (mGetter == null) {
824 // Already logged the error - just return to avoid NPE
825 return;
826 }
Chet Haasedb4101c2012-05-03 14:48:07 -0700827 }
George Mount691487d2014-11-10 16:46:31 -0800828 try {
829 Object value = convertBack(mGetter.invoke(target));
830 kf.setValue(value);
831 kf.setValueWasSetOnStart(true);
832 } catch (InvocationTargetException e) {
833 Log.e("PropertyValuesHolder", e.toString());
834 } catch (IllegalAccessException e) {
835 Log.e("PropertyValuesHolder", e.toString());
836 }
Chet Haased953d082010-08-16 17:44:28 -0700837 }
838 }
839 }
840 }
841
George Mount16d2c9c2013-09-17 09:07:48 -0700842 private Object convertBack(Object value) {
843 if (mConverter != null) {
George Mount42516d12014-05-19 15:49:29 -0700844 if (!(mConverter instanceof BidirectionalTypeConverter)) {
George Mount16d2c9c2013-09-17 09:07:48 -0700845 throw new IllegalArgumentException("Converter "
846 + mConverter.getClass().getName()
George Mount42516d12014-05-19 15:49:29 -0700847 + " must be a BidirectionalTypeConverter");
George Mount16d2c9c2013-09-17 09:07:48 -0700848 }
George Mount42516d12014-05-19 15:49:29 -0700849 value = ((BidirectionalTypeConverter) mConverter).convertBack(value);
George Mount16d2c9c2013-09-17 09:07:48 -0700850 }
851 return value;
852 }
853
Chet Haased953d082010-08-16 17:44:28 -0700854 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700855 * Utility function to set the value stored in a particular Keyframe. The value used is
856 * whatever the value is for the property name specified in the keyframe on the target object.
857 *
858 * @param target The target object from which the current value should be extracted.
859 * @param kf The keyframe which holds the property name and value.
860 */
861 private void setupValue(Object target, Keyframe kf) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700862 if (mProperty != null) {
George Mount16d2c9c2013-09-17 09:07:48 -0700863 Object value = convertBack(mProperty.get(target));
864 kf.setValue(value);
Chet Haase406a02b2016-01-11 16:27:20 -0800865 } else {
866 try {
Chet Haasedb4101c2012-05-03 14:48:07 -0700867 if (mGetter == null) {
Chet Haase406a02b2016-01-11 16:27:20 -0800868 Class targetClass = target.getClass();
869 setupGetter(targetClass);
870 if (mGetter == null) {
871 // Already logged the error - just return to avoid NPE
872 return;
873 }
Chet Haasedb4101c2012-05-03 14:48:07 -0700874 }
Chet Haase406a02b2016-01-11 16:27:20 -0800875 Object value = convertBack(mGetter.invoke(target));
876 kf.setValue(value);
877 } catch (InvocationTargetException e) {
878 Log.e("PropertyValuesHolder", e.toString());
879 } catch (IllegalAccessException e) {
880 Log.e("PropertyValuesHolder", e.toString());
Chet Haase21cd1382010-09-01 17:42:29 -0700881 }
Chet Haase21cd1382010-09-01 17:42:29 -0700882 }
883 }
884
885 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700886 * This function is called by ObjectAnimator when setting the start values for an animation.
Chet Haase21cd1382010-09-01 17:42:29 -0700887 * The start values are set according to the current values in the target object. The
888 * property whose value is extracted is whatever is specified by the propertyName of this
889 * PropertyValuesHolder object.
890 *
891 * @param target The object which holds the start values that should be set.
892 */
893 void setupStartValue(Object target) {
Yigit Boyard422dc32014-09-25 12:23:35 -0700894 List<Keyframe> keyframes = mKeyframes.getKeyframes();
George Mount984011f2014-08-21 14:28:01 -0700895 if (!keyframes.isEmpty()) {
896 setupValue(target, keyframes.get(0));
897 }
Chet Haase21cd1382010-09-01 17:42:29 -0700898 }
899
900 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700901 * This function is called by ObjectAnimator when setting the end values for an animation.
Chet Haase21cd1382010-09-01 17:42:29 -0700902 * The end values are set according to the current values in the target object. The
903 * property whose value is extracted is whatever is specified by the propertyName of this
904 * PropertyValuesHolder object.
905 *
906 * @param target The object which holds the start values that should be set.
907 */
908 void setupEndValue(Object target) {
Yigit Boyard422dc32014-09-25 12:23:35 -0700909 List<Keyframe> keyframes = mKeyframes.getKeyframes();
George Mount984011f2014-08-21 14:28:01 -0700910 if (!keyframes.isEmpty()) {
911 setupValue(target, keyframes.get(keyframes.size() - 1));
912 }
Chet Haase21cd1382010-09-01 17:42:29 -0700913 }
914
915 @Override
916 public PropertyValuesHolder clone() {
Chet Haase7c608f22010-10-22 17:54:04 -0700917 try {
918 PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
919 newPVH.mPropertyName = mPropertyName;
Chet Haaseb39f0512011-05-24 14:36:40 -0700920 newPVH.mProperty = mProperty;
George Mount984011f2014-08-21 14:28:01 -0700921 newPVH.mKeyframes = mKeyframes.clone();
Chet Haase7c608f22010-10-22 17:54:04 -0700922 newPVH.mEvaluator = mEvaluator;
923 return newPVH;
924 } catch (CloneNotSupportedException e) {
925 // won't reach here
926 return null;
Chet Haase21cd1382010-09-01 17:42:29 -0700927 }
Chet Haase21cd1382010-09-01 17:42:29 -0700928 }
Chet Haase7c608f22010-10-22 17:54:04 -0700929
Chet Haase21cd1382010-09-01 17:42:29 -0700930 /**
Chet Haased953d082010-08-16 17:44:28 -0700931 * Internal function to set the value on the target object, using the setter set up
Chet Haasea18a86b2010-09-07 13:20:00 -0700932 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
933 * to handle turning the value calculated by ValueAnimator into a value set on the object
Chet Haased953d082010-08-16 17:44:28 -0700934 * according to the name of the property.
935 * @param target The target object on which the value is set
936 */
937 void setAnimatedValue(Object target) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700938 if (mProperty != null) {
939 mProperty.set(target, getAnimatedValue());
940 }
Chet Haased953d082010-08-16 17:44:28 -0700941 if (mSetter != null) {
942 try {
Chet Haase7c608f22010-10-22 17:54:04 -0700943 mTmpValueArray[0] = getAnimatedValue();
Chet Haased953d082010-08-16 17:44:28 -0700944 mSetter.invoke(target, mTmpValueArray);
945 } catch (InvocationTargetException e) {
946 Log.e("PropertyValuesHolder", e.toString());
947 } catch (IllegalAccessException e) {
948 Log.e("PropertyValuesHolder", e.toString());
949 }
950 }
951 }
952
953 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700954 * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
Chet Haased953d082010-08-16 17:44:28 -0700955 * to calculate animated values.
956 */
957 void init() {
958 if (mEvaluator == null) {
Chet Haaseb2ab04f2011-01-16 11:03:22 -0800959 // We already handle int and float automatically, but not their Object
Chet Haase7c608f22010-10-22 17:54:04 -0700960 // equivalents
961 mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
962 (mValueType == Float.class) ? sFloatEvaluator :
963 null;
964 }
965 if (mEvaluator != null) {
966 // KeyframeSet knows how to evaluate the common types - only give it a custom
Chet Haaseb2ab04f2011-01-16 11:03:22 -0800967 // evaluator if one has been set on this class
George Mount984011f2014-08-21 14:28:01 -0700968 mKeyframes.setEvaluator(mEvaluator);
Chet Haased953d082010-08-16 17:44:28 -0700969 }
970 }
971
972 /**
Chet Haased82c8ac2013-08-26 14:20:16 -0700973 * The TypeEvaluator will be automatically determined based on the type of values
Chet Haased953d082010-08-16 17:44:28 -0700974 * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
975 * desired. This may be important in cases where either the type of the values supplied
976 * do not match the way that they should be interpolated between, or if the values
977 * are of a custom type or one not currently understood by the animation system. Currently,
Chet Haaseb2ab04f2011-01-16 11:03:22 -0800978 * only values of type float and int (and their Object equivalents: Float
Chet Haased953d082010-08-16 17:44:28 -0700979 * and Integer) are correctly interpolated; all other types require setting a TypeEvaluator.
980 * @param evaluator
981 */
Chet Haase7c608f22010-10-22 17:54:04 -0700982 public void setEvaluator(TypeEvaluator evaluator) {
Chet Haased953d082010-08-16 17:44:28 -0700983 mEvaluator = evaluator;
George Mount984011f2014-08-21 14:28:01 -0700984 mKeyframes.setEvaluator(evaluator);
Chet Haased953d082010-08-16 17:44:28 -0700985 }
986
987 /**
988 * Function used to calculate the value according to the evaluator set up for
Chet Haasea18a86b2010-09-07 13:20:00 -0700989 * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
Chet Haased953d082010-08-16 17:44:28 -0700990 *
991 * @param fraction The elapsed, interpolated fraction of the animation.
Chet Haased953d082010-08-16 17:44:28 -0700992 */
Chet Haase7c608f22010-10-22 17:54:04 -0700993 void calculateValue(float fraction) {
George Mount984011f2014-08-21 14:28:01 -0700994 Object value = mKeyframes.getValue(fraction);
George Mount16d2c9c2013-09-17 09:07:48 -0700995 mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
Chet Haased953d082010-08-16 17:44:28 -0700996 }
997
998 /**
Chet Haased953d082010-08-16 17:44:28 -0700999 * Sets the name of the property that will be animated. This name is used to derive
1000 * a setter function that will be called to set animated values.
1001 * For example, a property name of <code>foo</code> will result
1002 * in a call to the function <code>setFoo()</code> on the target object. If either
1003 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
1004 * also be derived and called.
1005 *
1006 * <p>Note that the setter function derived from this property name
1007 * must take the same parameter type as the
1008 * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
1009 * the setter function will fail.</p>
1010 *
1011 * @param propertyName The name of the property being animated.
1012 */
1013 public void setPropertyName(String propertyName) {
1014 mPropertyName = propertyName;
1015 }
1016
1017 /**
Chet Haaseb39f0512011-05-24 14:36:40 -07001018 * Sets the property that will be animated.
1019 *
1020 * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
1021 * must exist on the target object specified in that ObjectAnimator.</p>
1022 *
1023 * @param property The property being animated.
1024 */
1025 public void setProperty(Property property) {
1026 mProperty = property;
1027 }
1028
1029 /**
Chet Haased953d082010-08-16 17:44:28 -07001030 * Gets the name of the property that will be animated. This name will be used to derive
1031 * a setter function that will be called to set animated values.
1032 * For example, a property name of <code>foo</code> will result
1033 * in a call to the function <code>setFoo()</code> on the target object. If either
1034 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
1035 * also be derived and called.
1036 */
1037 public String getPropertyName() {
1038 return mPropertyName;
1039 }
1040
1041 /**
Chet Haasea18a86b2010-09-07 13:20:00 -07001042 * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
Chet Haased953d082010-08-16 17:44:28 -07001043 * most recently calculated in calculateValue().
1044 * @return
1045 */
1046 Object getAnimatedValue() {
1047 return mAnimatedValue;
1048 }
Chet Haase7c608f22010-10-22 17:54:04 -07001049
Doris Liu766431a2016-02-04 22:17:11 +00001050 /**
1051 * PropertyValuesHolder is Animators use to hold internal animation related data.
1052 * Therefore, in order to replicate the animation behavior, we need to get data out of
1053 * PropertyValuesHolder.
1054 * @hide
1055 */
1056 public void getPropertyValues(PropertyValues values) {
1057 init();
1058 values.propertyName = mPropertyName;
1059 values.type = mValueType;
1060 values.startValue = mKeyframes.getValue(0);
1061 if (values.startValue instanceof PathParser.PathData) {
1062 // PathData evaluator returns the same mutable PathData object when query fraction,
1063 // so we have to make a copy here.
1064 values.startValue = new PathParser.PathData((PathParser.PathData) values.startValue);
1065 }
1066 values.endValue = mKeyframes.getValue(1);
1067 if (values.endValue instanceof PathParser.PathData) {
1068 // PathData evaluator returns the same mutable PathData object when query fraction,
1069 // so we have to make a copy here.
1070 values.endValue = new PathParser.PathData((PathParser.PathData) values.endValue);
1071 }
1072 // TODO: We need a better way to get data out of keyframes.
1073 if (mKeyframes instanceof PathKeyframes.FloatKeyframesBase
1074 || mKeyframes instanceof PathKeyframes.IntKeyframesBase) {
1075 // property values will animate based on external data source (e.g. Path)
1076 values.dataSource = new PropertyValues.DataSource() {
1077 @Override
1078 public Object getValueAtFraction(float fraction) {
1079 return mKeyframes.getValue(fraction);
1080 }
1081 };
1082 } else {
1083 values.dataSource = null;
1084 }
1085 }
1086
Chet Haasee9140a72011-02-16 16:23:29 -08001087 @Override
1088 public String toString() {
George Mount984011f2014-08-21 14:28:01 -07001089 return mPropertyName + ": " + mKeyframes.toString();
Chet Haasee9140a72011-02-16 16:23:29 -08001090 }
1091
Chet Haase6e0ecb42010-11-03 19:41:18 -07001092 /**
1093 * Utility method to derive a setter/getter method name from a property name, where the
1094 * prefix is typically "set" or "get" and the first letter of the property name is
1095 * capitalized.
1096 *
1097 * @param prefix The precursor to the method name, before the property name begins, typically
1098 * "set" or "get".
1099 * @param propertyName The name of the property that represents the bulk of the method name
1100 * after the prefix. The first letter of this word will be capitalized in the resulting
1101 * method name.
1102 * @return String the property name converted to a method name according to the conventions
1103 * specified above.
1104 */
1105 static String getMethodName(String prefix, String propertyName) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001106 if (propertyName == null || propertyName.length() == 0) {
1107 // shouldn't get here
1108 return prefix;
1109 }
1110 char firstLetter = Character.toUpperCase(propertyName.charAt(0));
Chet Haase6e0ecb42010-11-03 19:41:18 -07001111 String theRest = propertyName.substring(1);
Chet Haase6e0ecb42010-11-03 19:41:18 -07001112 return prefix + firstLetter + theRest;
1113 }
1114
Chet Haase7c608f22010-10-22 17:54:04 -07001115 static class IntPropertyValuesHolder extends PropertyValuesHolder {
1116
Chet Haaseb39f0512011-05-24 14:36:40 -07001117 // Cache JNI functions to avoid looking them up twice
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001118 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1119 new HashMap<Class, HashMap<String, Long>>();
1120 long mJniSetter;
Chet Haaseb39f0512011-05-24 14:36:40 -07001121 private IntProperty mIntProperty;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001122
George Mount984011f2014-08-21 14:28:01 -07001123 Keyframes.IntKeyframes mIntKeyframes;
Chet Haase7c608f22010-10-22 17:54:04 -07001124 int mIntAnimatedValue;
1125
George Mount984011f2014-08-21 14:28:01 -07001126 public IntPropertyValuesHolder(String propertyName, Keyframes.IntKeyframes keyframes) {
Chet Haase7c608f22010-10-22 17:54:04 -07001127 super(propertyName);
1128 mValueType = int.class;
George Mount984011f2014-08-21 14:28:01 -07001129 mKeyframes = keyframes;
1130 mIntKeyframes = keyframes;
Chet Haase7c608f22010-10-22 17:54:04 -07001131 }
1132
George Mount984011f2014-08-21 14:28:01 -07001133 public IntPropertyValuesHolder(Property property, Keyframes.IntKeyframes keyframes) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001134 super(property);
1135 mValueType = int.class;
George Mount984011f2014-08-21 14:28:01 -07001136 mKeyframes = keyframes;
1137 mIntKeyframes = keyframes;
Chet Haaseb39f0512011-05-24 14:36:40 -07001138 if (property instanceof IntProperty) {
1139 mIntProperty = (IntProperty) mProperty;
1140 }
1141 }
1142
Chet Haase7c608f22010-10-22 17:54:04 -07001143 public IntPropertyValuesHolder(String propertyName, int... values) {
1144 super(propertyName);
1145 setIntValues(values);
Chet Haase691ac262010-11-03 22:38:32 -07001146 }
1147
Chet Haaseb39f0512011-05-24 14:36:40 -07001148 public IntPropertyValuesHolder(Property property, int... values) {
1149 super(property);
1150 setIntValues(values);
1151 if (property instanceof IntProperty) {
1152 mIntProperty = (IntProperty) mProperty;
1153 }
1154 }
1155
Chet Haase691ac262010-11-03 22:38:32 -07001156 @Override
1157 public void setIntValues(int... values) {
1158 super.setIntValues(values);
George Mount984011f2014-08-21 14:28:01 -07001159 mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
Chet Haase7c608f22010-10-22 17:54:04 -07001160 }
1161
1162 @Override
1163 void calculateValue(float fraction) {
George Mount984011f2014-08-21 14:28:01 -07001164 mIntAnimatedValue = mIntKeyframes.getIntValue(fraction);
Chet Haase7c608f22010-10-22 17:54:04 -07001165 }
1166
1167 @Override
1168 Object getAnimatedValue() {
1169 return mIntAnimatedValue;
1170 }
1171
1172 @Override
1173 public IntPropertyValuesHolder clone() {
1174 IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
George Mount984011f2014-08-21 14:28:01 -07001175 newPVH.mIntKeyframes = (Keyframes.IntKeyframes) newPVH.mKeyframes;
Chet Haase7c608f22010-10-22 17:54:04 -07001176 return newPVH;
1177 }
1178
1179 /**
1180 * Internal function to set the value on the target object, using the setter set up
1181 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1182 * to handle turning the value calculated by ValueAnimator into a value set on the object
1183 * according to the name of the property.
1184 * @param target The target object on which the value is set
1185 */
1186 @Override
1187 void setAnimatedValue(Object target) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001188 if (mIntProperty != null) {
1189 mIntProperty.setValue(target, mIntAnimatedValue);
1190 return;
1191 }
1192 if (mProperty != null) {
1193 mProperty.set(target, mIntAnimatedValue);
1194 return;
1195 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001196 if (mJniSetter != 0) {
1197 nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
1198 return;
1199 }
Chet Haase7c608f22010-10-22 17:54:04 -07001200 if (mSetter != null) {
1201 try {
1202 mTmpValueArray[0] = mIntAnimatedValue;
1203 mSetter.invoke(target, mTmpValueArray);
1204 } catch (InvocationTargetException e) {
1205 Log.e("PropertyValuesHolder", e.toString());
1206 } catch (IllegalAccessException e) {
1207 Log.e("PropertyValuesHolder", e.toString());
1208 }
1209 }
1210 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001211
1212 @Override
1213 void setupSetter(Class targetClass) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001214 if (mProperty != null) {
1215 return;
1216 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001217 // Check new static hashmap<propName, int> for setter method
George Mount691487d2014-11-10 16:46:31 -08001218 synchronized(sJNISetterPropertyMap) {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001219 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
George Mount691487d2014-11-10 16:46:31 -08001220 boolean wasInMap = false;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001221 if (propertyMap != null) {
George Mount691487d2014-11-10 16:46:31 -08001222 wasInMap = propertyMap.containsKey(mPropertyName);
1223 if (wasInMap) {
1224 Long jniSetter = propertyMap.get(mPropertyName);
1225 if (jniSetter != null) {
1226 mJniSetter = jniSetter;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001227 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001228 }
1229 }
George Mount691487d2014-11-10 16:46:31 -08001230 if (!wasInMap) {
1231 String methodName = getMethodName("set", mPropertyName);
1232 try {
1233 mJniSetter = nGetIntMethod(targetClass, methodName);
1234 } catch (NoSuchMethodError e) {
1235 // Couldn't find it via JNI - try reflection next. Probably means the method
1236 // doesn't exist, or the type is wrong. An error will be logged later if
1237 // reflection fails as well.
1238 }
1239 if (propertyMap == null) {
1240 propertyMap = new HashMap<String, Long>();
1241 sJNISetterPropertyMap.put(targetClass, propertyMap);
1242 }
1243 propertyMap.put(mPropertyName, mJniSetter);
1244 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001245 }
1246 if (mJniSetter == 0) {
1247 // Couldn't find method through fast JNI approach - just use reflection
1248 super.setupSetter(targetClass);
1249 }
1250 }
Chet Haase7c608f22010-10-22 17:54:04 -07001251 }
1252
1253 static class FloatPropertyValuesHolder extends PropertyValuesHolder {
1254
Chet Haaseb39f0512011-05-24 14:36:40 -07001255 // Cache JNI functions to avoid looking them up twice
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001256 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1257 new HashMap<Class, HashMap<String, Long>>();
1258 long mJniSetter;
Chet Haaseb39f0512011-05-24 14:36:40 -07001259 private FloatProperty mFloatProperty;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001260
George Mount984011f2014-08-21 14:28:01 -07001261 Keyframes.FloatKeyframes mFloatKeyframes;
Chet Haase7c608f22010-10-22 17:54:04 -07001262 float mFloatAnimatedValue;
1263
George Mount984011f2014-08-21 14:28:01 -07001264 public FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes) {
Chet Haase7c608f22010-10-22 17:54:04 -07001265 super(propertyName);
1266 mValueType = float.class;
George Mount984011f2014-08-21 14:28:01 -07001267 mKeyframes = keyframes;
1268 mFloatKeyframes = keyframes;
Chet Haase7c608f22010-10-22 17:54:04 -07001269 }
1270
George Mount984011f2014-08-21 14:28:01 -07001271 public FloatPropertyValuesHolder(Property property, Keyframes.FloatKeyframes keyframes) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001272 super(property);
1273 mValueType = float.class;
George Mount984011f2014-08-21 14:28:01 -07001274 mKeyframes = keyframes;
1275 mFloatKeyframes = keyframes;
Chet Haaseb39f0512011-05-24 14:36:40 -07001276 if (property instanceof FloatProperty) {
1277 mFloatProperty = (FloatProperty) mProperty;
1278 }
1279 }
1280
Chet Haase7c608f22010-10-22 17:54:04 -07001281 public FloatPropertyValuesHolder(String propertyName, float... values) {
1282 super(propertyName);
1283 setFloatValues(values);
Chet Haase691ac262010-11-03 22:38:32 -07001284 }
1285
Chet Haaseb39f0512011-05-24 14:36:40 -07001286 public FloatPropertyValuesHolder(Property property, float... values) {
1287 super(property);
1288 setFloatValues(values);
1289 if (property instanceof FloatProperty) {
1290 mFloatProperty = (FloatProperty) mProperty;
1291 }
1292 }
1293
Chet Haase691ac262010-11-03 22:38:32 -07001294 @Override
1295 public void setFloatValues(float... values) {
1296 super.setFloatValues(values);
George Mount984011f2014-08-21 14:28:01 -07001297 mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
Chet Haase7c608f22010-10-22 17:54:04 -07001298 }
1299
1300 @Override
1301 void calculateValue(float fraction) {
George Mount984011f2014-08-21 14:28:01 -07001302 mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
Chet Haase7c608f22010-10-22 17:54:04 -07001303 }
1304
1305 @Override
1306 Object getAnimatedValue() {
1307 return mFloatAnimatedValue;
1308 }
1309
1310 @Override
1311 public FloatPropertyValuesHolder clone() {
1312 FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
George Mount984011f2014-08-21 14:28:01 -07001313 newPVH.mFloatKeyframes = (Keyframes.FloatKeyframes) newPVH.mKeyframes;
Chet Haase7c608f22010-10-22 17:54:04 -07001314 return newPVH;
1315 }
1316
1317 /**
1318 * Internal function to set the value on the target object, using the setter set up
1319 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1320 * to handle turning the value calculated by ValueAnimator into a value set on the object
1321 * according to the name of the property.
1322 * @param target The target object on which the value is set
1323 */
1324 @Override
1325 void setAnimatedValue(Object target) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001326 if (mFloatProperty != null) {
1327 mFloatProperty.setValue(target, mFloatAnimatedValue);
1328 return;
1329 }
1330 if (mProperty != null) {
1331 mProperty.set(target, mFloatAnimatedValue);
1332 return;
1333 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001334 if (mJniSetter != 0) {
1335 nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
1336 return;
1337 }
Chet Haase7c608f22010-10-22 17:54:04 -07001338 if (mSetter != null) {
1339 try {
1340 mTmpValueArray[0] = mFloatAnimatedValue;
1341 mSetter.invoke(target, mTmpValueArray);
1342 } catch (InvocationTargetException e) {
1343 Log.e("PropertyValuesHolder", e.toString());
1344 } catch (IllegalAccessException e) {
1345 Log.e("PropertyValuesHolder", e.toString());
1346 }
1347 }
1348 }
1349
Chet Haase6e0ecb42010-11-03 19:41:18 -07001350 @Override
1351 void setupSetter(Class targetClass) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001352 if (mProperty != null) {
1353 return;
1354 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001355 // Check new static hashmap<propName, int> for setter method
George Mount691487d2014-11-10 16:46:31 -08001356 synchronized (sJNISetterPropertyMap) {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001357 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
George Mount691487d2014-11-10 16:46:31 -08001358 boolean wasInMap = false;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001359 if (propertyMap != null) {
George Mount691487d2014-11-10 16:46:31 -08001360 wasInMap = propertyMap.containsKey(mPropertyName);
1361 if (wasInMap) {
1362 Long jniSetter = propertyMap.get(mPropertyName);
1363 if (jniSetter != null) {
1364 mJniSetter = jniSetter;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001365 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001366 }
1367 }
George Mount691487d2014-11-10 16:46:31 -08001368 if (!wasInMap) {
1369 String methodName = getMethodName("set", mPropertyName);
1370 try {
1371 mJniSetter = nGetFloatMethod(targetClass, methodName);
1372 } catch (NoSuchMethodError e) {
1373 // Couldn't find it via JNI - try reflection next. Probably means the method
1374 // doesn't exist, or the type is wrong. An error will be logged later if
1375 // reflection fails as well.
1376 }
1377 if (propertyMap == null) {
1378 propertyMap = new HashMap<String, Long>();
1379 sJNISetterPropertyMap.put(targetClass, propertyMap);
1380 }
1381 propertyMap.put(mPropertyName, mJniSetter);
1382 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001383 }
1384 if (mJniSetter == 0) {
1385 // Couldn't find method through fast JNI approach - just use reflection
1386 super.setupSetter(targetClass);
1387 }
1388 }
1389
Chet Haase7c608f22010-10-22 17:54:04 -07001390 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001391
George Mount4eed5292013-08-30 13:56:01 -07001392 static class MultiFloatValuesHolder extends PropertyValuesHolder {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001393 private long mJniSetter;
1394 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1395 new HashMap<Class, HashMap<String, Long>>();
George Mount4eed5292013-08-30 13:56:01 -07001396
1397 public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
1398 TypeEvaluator evaluator, Object... values) {
1399 super(propertyName);
1400 setConverter(converter);
1401 setObjectValues(values);
1402 setEvaluator(evaluator);
1403 }
1404
1405 public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
George Mount984011f2014-08-21 14:28:01 -07001406 TypeEvaluator evaluator, Keyframes keyframes) {
George Mount4eed5292013-08-30 13:56:01 -07001407 super(propertyName);
1408 setConverter(converter);
George Mount984011f2014-08-21 14:28:01 -07001409 mKeyframes = keyframes;
George Mount4eed5292013-08-30 13:56:01 -07001410 setEvaluator(evaluator);
1411 }
1412
1413 /**
1414 * Internal function to set the value on the target object, using the setter set up
1415 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1416 * to handle turning the value calculated by ValueAnimator into a value set on the object
1417 * according to the name of the property.
1418 *
1419 * @param target The target object on which the value is set
1420 */
1421 @Override
1422 void setAnimatedValue(Object target) {
1423 float[] values = (float[]) getAnimatedValue();
1424 int numParameters = values.length;
1425 if (mJniSetter != 0) {
1426 switch (numParameters) {
1427 case 1:
1428 nCallFloatMethod(target, mJniSetter, values[0]);
1429 break;
1430 case 2:
1431 nCallTwoFloatMethod(target, mJniSetter, values[0], values[1]);
1432 break;
1433 case 4:
1434 nCallFourFloatMethod(target, mJniSetter, values[0], values[1],
1435 values[2], values[3]);
1436 break;
1437 default: {
1438 nCallMultipleFloatMethod(target, mJniSetter, values);
1439 break;
1440 }
1441 }
1442 }
1443 }
1444
1445 /**
1446 * Internal function (called from ObjectAnimator) to set up the setter and getter
1447 * prior to running the animation. No getter can be used for multiple parameters.
1448 *
1449 * @param target The object on which the setter exists.
1450 */
1451 @Override
1452 void setupSetterAndGetter(Object target) {
1453 setupSetter(target.getClass());
1454 }
1455
1456 @Override
1457 void setupSetter(Class targetClass) {
1458 if (mJniSetter != 0) {
1459 return;
1460 }
George Mount691487d2014-11-10 16:46:31 -08001461 synchronized(sJNISetterPropertyMap) {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001462 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
George Mount691487d2014-11-10 16:46:31 -08001463 boolean wasInMap = false;
George Mount4eed5292013-08-30 13:56:01 -07001464 if (propertyMap != null) {
George Mount691487d2014-11-10 16:46:31 -08001465 wasInMap = propertyMap.containsKey(mPropertyName);
1466 if (wasInMap) {
1467 Long jniSetter = propertyMap.get(mPropertyName);
1468 if (jniSetter != null) {
1469 mJniSetter = jniSetter;
1470 }
George Mount4eed5292013-08-30 13:56:01 -07001471 }
1472 }
George Mount691487d2014-11-10 16:46:31 -08001473 if (!wasInMap) {
George Mount4eed5292013-08-30 13:56:01 -07001474 String methodName = getMethodName("set", mPropertyName);
1475 calculateValue(0f);
1476 float[] values = (float[]) getAnimatedValue();
1477 int numParams = values.length;
1478 try {
1479 mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams);
1480 } catch (NoSuchMethodError e) {
1481 // try without the 'set' prefix
George Mount691487d2014-11-10 16:46:31 -08001482 try {
1483 mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName,
1484 numParams);
1485 } catch (NoSuchMethodError e2) {
1486 // just try reflection next
George Mount4eed5292013-08-30 13:56:01 -07001487 }
George Mount4eed5292013-08-30 13:56:01 -07001488 }
George Mount691487d2014-11-10 16:46:31 -08001489 if (propertyMap == null) {
1490 propertyMap = new HashMap<String, Long>();
1491 sJNISetterPropertyMap.put(targetClass, propertyMap);
1492 }
1493 propertyMap.put(mPropertyName, mJniSetter);
George Mount4eed5292013-08-30 13:56:01 -07001494 }
George Mount691487d2014-11-10 16:46:31 -08001495 }
George Mount4eed5292013-08-30 13:56:01 -07001496 }
1497 }
1498
1499 static class MultiIntValuesHolder extends PropertyValuesHolder {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001500 private long mJniSetter;
1501 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1502 new HashMap<Class, HashMap<String, Long>>();
George Mount4eed5292013-08-30 13:56:01 -07001503
1504 public MultiIntValuesHolder(String propertyName, TypeConverter converter,
1505 TypeEvaluator evaluator, Object... values) {
1506 super(propertyName);
1507 setConverter(converter);
1508 setObjectValues(values);
1509 setEvaluator(evaluator);
1510 }
1511
1512 public MultiIntValuesHolder(String propertyName, TypeConverter converter,
George Mount984011f2014-08-21 14:28:01 -07001513 TypeEvaluator evaluator, Keyframes keyframes) {
George Mount4eed5292013-08-30 13:56:01 -07001514 super(propertyName);
1515 setConverter(converter);
George Mount984011f2014-08-21 14:28:01 -07001516 mKeyframes = keyframes;
George Mount4eed5292013-08-30 13:56:01 -07001517 setEvaluator(evaluator);
1518 }
1519
1520 /**
1521 * Internal function to set the value on the target object, using the setter set up
1522 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1523 * to handle turning the value calculated by ValueAnimator into a value set on the object
1524 * according to the name of the property.
1525 *
1526 * @param target The target object on which the value is set
1527 */
1528 @Override
1529 void setAnimatedValue(Object target) {
1530 int[] values = (int[]) getAnimatedValue();
1531 int numParameters = values.length;
1532 if (mJniSetter != 0) {
1533 switch (numParameters) {
1534 case 1:
1535 nCallIntMethod(target, mJniSetter, values[0]);
1536 break;
1537 case 2:
1538 nCallTwoIntMethod(target, mJniSetter, values[0], values[1]);
1539 break;
1540 case 4:
1541 nCallFourIntMethod(target, mJniSetter, values[0], values[1],
1542 values[2], values[3]);
1543 break;
1544 default: {
1545 nCallMultipleIntMethod(target, mJniSetter, values);
1546 break;
1547 }
1548 }
1549 }
1550 }
1551
1552 /**
1553 * Internal function (called from ObjectAnimator) to set up the setter and getter
1554 * prior to running the animation. No getter can be used for multiple parameters.
1555 *
1556 * @param target The object on which the setter exists.
1557 */
1558 @Override
1559 void setupSetterAndGetter(Object target) {
1560 setupSetter(target.getClass());
1561 }
1562
1563 @Override
1564 void setupSetter(Class targetClass) {
1565 if (mJniSetter != 0) {
1566 return;
1567 }
George Mount691487d2014-11-10 16:46:31 -08001568 synchronized(sJNISetterPropertyMap) {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001569 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
George Mount691487d2014-11-10 16:46:31 -08001570 boolean wasInMap = false;
George Mount4eed5292013-08-30 13:56:01 -07001571 if (propertyMap != null) {
George Mount691487d2014-11-10 16:46:31 -08001572 wasInMap = propertyMap.containsKey(mPropertyName);
1573 if (wasInMap) {
1574 Long jniSetter = propertyMap.get(mPropertyName);
1575 if (jniSetter != null) {
1576 mJniSetter = jniSetter;
1577 }
George Mount4eed5292013-08-30 13:56:01 -07001578 }
1579 }
George Mount691487d2014-11-10 16:46:31 -08001580 if (!wasInMap) {
George Mount4eed5292013-08-30 13:56:01 -07001581 String methodName = getMethodName("set", mPropertyName);
1582 calculateValue(0f);
1583 int[] values = (int[]) getAnimatedValue();
1584 int numParams = values.length;
1585 try {
1586 mJniSetter = nGetMultipleIntMethod(targetClass, methodName, numParams);
1587 } catch (NoSuchMethodError e) {
1588 // try without the 'set' prefix
George Mount691487d2014-11-10 16:46:31 -08001589 try {
1590 mJniSetter = nGetMultipleIntMethod(targetClass, mPropertyName,
1591 numParams);
1592 } catch (NoSuchMethodError e2) {
1593 // couldn't find it.
George Mount4eed5292013-08-30 13:56:01 -07001594 }
George Mount4eed5292013-08-30 13:56:01 -07001595 }
George Mount691487d2014-11-10 16:46:31 -08001596 if (propertyMap == null) {
1597 propertyMap = new HashMap<String, Long>();
1598 sJNISetterPropertyMap.put(targetClass, propertyMap);
1599 }
1600 propertyMap.put(mPropertyName, mJniSetter);
George Mount4eed5292013-08-30 13:56:01 -07001601 }
George Mount4eed5292013-08-30 13:56:01 -07001602 }
1603 }
1604 }
1605
George Mountc96c7b22013-08-23 13:31:31 -07001606 /**
1607 * Convert from PointF to float[] for multi-float setters along a Path.
1608 */
1609 private static class PointFToFloatArray extends TypeConverter<PointF, float[]> {
1610 private float[] mCoordinates = new float[2];
1611
1612 public PointFToFloatArray() {
1613 super(PointF.class, float[].class);
1614 }
1615
1616 @Override
1617 public float[] convert(PointF value) {
1618 mCoordinates[0] = value.x;
1619 mCoordinates[1] = value.y;
1620 return mCoordinates;
1621 }
1622 };
1623
1624 /**
1625 * Convert from PointF to int[] for multi-int setters along a Path.
1626 */
1627 private static class PointFToIntArray extends TypeConverter<PointF, int[]> {
1628 private int[] mCoordinates = new int[2];
1629
1630 public PointFToIntArray() {
1631 super(PointF.class, int[].class);
1632 }
1633
1634 @Override
1635 public int[] convert(PointF value) {
1636 mCoordinates[0] = Math.round(value.x);
1637 mCoordinates[1] = Math.round(value.y);
1638 return mCoordinates;
1639 }
1640 };
1641
Doris Liu766431a2016-02-04 22:17:11 +00001642 /**
1643 * @hide
1644 */
1645 public static class PropertyValues {
1646 public String propertyName;
1647 public Class type;
1648 public Object startValue;
1649 public Object endValue;
1650 public DataSource dataSource = null;
1651 public interface DataSource {
1652 Object getValueAtFraction(float fraction);
1653 }
1654 public String toString() {
1655 return ("property name: " + propertyName + ", type: " + type + ", startValue: "
1656 + startValue.toString() + ", endValue: " + endValue.toString());
1657 }
1658 }
1659
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001660 native static private long nGetIntMethod(Class targetClass, String methodName);
1661 native static private long nGetFloatMethod(Class targetClass, String methodName);
1662 native static private long nGetMultipleIntMethod(Class targetClass, String methodName,
George Mount4eed5292013-08-30 13:56:01 -07001663 int numParams);
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001664 native static private long nGetMultipleFloatMethod(Class targetClass, String methodName,
George Mount4eed5292013-08-30 13:56:01 -07001665 int numParams);
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001666 native static private void nCallIntMethod(Object target, long methodID, int arg);
1667 native static private void nCallFloatMethod(Object target, long methodID, float arg);
1668 native static private void nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2);
1669 native static private void nCallFourIntMethod(Object target, long methodID, int arg1, int arg2,
George Mount4eed5292013-08-30 13:56:01 -07001670 int arg3, int arg4);
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001671 native static private void nCallMultipleIntMethod(Object target, long methodID, int[] args);
1672 native static private void nCallTwoFloatMethod(Object target, long methodID, float arg1,
George Mount4eed5292013-08-30 13:56:01 -07001673 float arg2);
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001674 native static private void nCallFourFloatMethod(Object target, long methodID, float arg1,
George Mount4eed5292013-08-30 13:56:01 -07001675 float arg2, float arg3, float arg4);
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001676 native static private void nCallMultipleFloatMethod(Object target, long methodID, float[] args);
George Mountc96c7b22013-08-23 13:31:31 -07001677}