blob: 76806a29787ec71a14bdbd1a56a41c48cf95351d [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 */
George Mountd98f4ba2016-03-14 14:29:24 -0700239 @SafeVarargs
George Mount4eed5292013-08-30 13:56:01 -0700240 public static <V> PropertyValuesHolder ofMultiInt(String propertyName,
241 TypeConverter<V, int[]> converter, TypeEvaluator<V> evaluator, V... values) {
242 return new MultiIntValuesHolder(propertyName, converter, evaluator, values);
243 }
244
245 /**
246 * Constructs and returns a PropertyValuesHolder object with the specified property name or
247 * setter name for use in a multi-int setter function using ObjectAnimator. The values can be
248 * of any type, but the type should be consistent so that the supplied
249 * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The
250 * <code>converter</code> converts the values to parameters in the setter function.
251 *
252 * <p>At least two values must be supplied, a start and an end value.</p>
253 *
254 * @param propertyName The name of the property to associate with the set of values. This
255 * may also be the complete name of a setter function.
256 * @param converter Converts <code>values</code> into int parameters for the setter.
257 * Can be null if the Keyframes have int[] values.
258 * @param evaluator Used to interpolate between values.
259 * @param values The values at specific fractional times to evaluate between
260 * @return A PropertyValuesHolder for a multi-int parameter setter.
261 */
262 public static <T> PropertyValuesHolder ofMultiInt(String propertyName,
263 TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, Keyframe... values) {
264 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
265 return new MultiIntValuesHolder(propertyName, converter, evaluator, keyframeSet);
266 }
267
268 /**
269 * Constructs and returns a PropertyValuesHolder with a given property name and
Chet Haase2794eb32010-10-12 16:29:28 -0700270 * set of float values.
271 * @param propertyName The name of the property being animated.
272 * @param values The values that the named property will animate between.
273 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
274 */
275 public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700276 return new FloatPropertyValuesHolder(propertyName, values);
277 }
278
279 /**
280 * Constructs and returns a PropertyValuesHolder with a given property and
281 * set of float values.
282 * @param property The property being animated. Should not be null.
283 * @param values The values that the property will animate between.
284 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
285 */
286 public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
287 return new FloatPropertyValuesHolder(property, values);
Chet Haase2794eb32010-10-12 16:29:28 -0700288 }
289
290 /**
291 * Constructs and returns a PropertyValuesHolder with a given property name and
George Mount4eed5292013-08-30 13:56:01 -0700292 * set of <code>float[]</code> values. At least two <code>float[]</code> values must be supplied,
293 * a start and end value. If more values are supplied, the values will be animated from the
294 * start, through all intermediate values to the end value. When used with ObjectAnimator,
295 * the elements of the array represent the parameters of the setter function.
296 *
George Mountc96c7b22013-08-23 13:31:31 -0700297 * @param propertyName The name of the property being animated. Can also be the
298 * case-sensitive name of the entire setter method. Should not be null.
George Mount4eed5292013-08-30 13:56:01 -0700299 * @param values The values that the property will animate between.
300 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
301 * @see FloatArrayEvaluator#FloatArrayEvaluator(float[])
302 * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[])
303 */
304 public static PropertyValuesHolder ofMultiFloat(String propertyName, float[][] values) {
305 if (values.length < 2) {
306 throw new IllegalArgumentException("At least 2 values must be supplied");
307 }
308 int numParameters = 0;
309 for (int i = 0; i < values.length; i++) {
310 if (values[i] == null) {
311 throw new IllegalArgumentException("values must not be null");
312 }
313 int length = values[i].length;
314 if (i == 0) {
315 numParameters = length;
316 } else if (length != numParameters) {
317 throw new IllegalArgumentException("Values must all have the same length");
318 }
319 }
320 FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[numParameters]);
321 return new MultiFloatValuesHolder(propertyName, null, evaluator, (Object[]) values);
322 }
323
324 /**
George Mountc96c7b22013-08-23 13:31:31 -0700325 * Constructs and returns a PropertyValuesHolder with a given property name to use
326 * as a multi-float setter. The values are animated along the path, with the first
327 * parameter of the setter set to the x coordinate and the second set to the y coordinate.
328 *
329 * @param propertyName The name of the property being animated. Can also be the
330 * case-sensitive name of the entire setter method. Should not be null.
331 * The setter must take exactly two <code>float</code> parameters.
332 * @param path The Path along which the values should be animated.
333 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
334 * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...)
335 */
336 public static PropertyValuesHolder ofMultiFloat(String propertyName, Path path) {
George Mount984011f2014-08-21 14:28:01 -0700337 Keyframes keyframes = KeyframeSet.ofPath(path);
George Mountc96c7b22013-08-23 13:31:31 -0700338 PointFToFloatArray converter = new PointFToFloatArray();
George Mount984011f2014-08-21 14:28:01 -0700339 return new MultiFloatValuesHolder(propertyName, converter, null, keyframes);
George Mountc96c7b22013-08-23 13:31:31 -0700340 }
341
342 /**
George Mount4eed5292013-08-30 13:56:01 -0700343 * Constructs and returns a PropertyValuesHolder with a given property and
344 * set of Object values for use with ObjectAnimator multi-value setters. The Object
345 * values are converted to <code>float[]</code> using the converter.
346 *
347 * @param propertyName The property being animated or complete name of the setter.
348 * Should not be null.
349 * @param converter Used to convert the animated value to setter parameters.
350 * @param evaluator A TypeEvaluator that will be called on each animation frame to
351 * provide the necessary interpolation between the Object values to derive the animated
352 * value.
353 * @param values The values that the property will animate between.
354 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
355 * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[])
356 */
George Mountd98f4ba2016-03-14 14:29:24 -0700357 @SafeVarargs
George Mount4eed5292013-08-30 13:56:01 -0700358 public static <V> PropertyValuesHolder ofMultiFloat(String propertyName,
359 TypeConverter<V, float[]> converter, TypeEvaluator<V> evaluator, V... values) {
360 return new MultiFloatValuesHolder(propertyName, converter, evaluator, values);
361 }
362
363 /**
364 * Constructs and returns a PropertyValuesHolder object with the specified property name or
365 * setter name for use in a multi-float setter function using ObjectAnimator. The values can be
366 * of any type, but the type should be consistent so that the supplied
367 * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The
368 * <code>converter</code> converts the values to parameters in the setter function.
369 *
370 * <p>At least two values must be supplied, a start and an end value.</p>
371 *
372 * @param propertyName The name of the property to associate with the set of values. This
373 * may also be the complete name of a setter function.
374 * @param converter Converts <code>values</code> into float parameters for the setter.
375 * Can be null if the Keyframes have float[] values.
376 * @param evaluator Used to interpolate between values.
377 * @param values The values at specific fractional times to evaluate between
378 * @return A PropertyValuesHolder for a multi-float parameter setter.
379 */
380 public static <T> PropertyValuesHolder ofMultiFloat(String propertyName,
381 TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, Keyframe... values) {
382 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
383 return new MultiFloatValuesHolder(propertyName, converter, evaluator, keyframeSet);
384 }
385
386 /**
387 * Constructs and returns a PropertyValuesHolder with a given property name and
Chet Haase2794eb32010-10-12 16:29:28 -0700388 * set of Object values. This variant also takes a TypeEvaluator because the system
Chet Haaseb39f0512011-05-24 14:36:40 -0700389 * cannot automatically interpolate between objects of unknown type.
Chet Haase2794eb32010-10-12 16:29:28 -0700390 *
Chet Haasefa21bdf2016-04-21 17:41:54 -0700391 * <p><strong>Note:</strong> The Object values are stored as references to the original
392 * objects, which means that changes to those objects after this method is called will
393 * affect the values on the PropertyValuesHolder. If the objects will be mutated externally
394 * after this method is called, callers should pass a copy of those objects instead.
395 *
Chet Haase2794eb32010-10-12 16:29:28 -0700396 * @param propertyName The name of the property being animated.
397 * @param evaluator A TypeEvaluator that will be called on each animation frame to
Chet Haaseb39f0512011-05-24 14:36:40 -0700398 * provide the necessary interpolation between the Object values to derive the animated
Chet Haase2794eb32010-10-12 16:29:28 -0700399 * value.
400 * @param values The values that the named property will animate between.
401 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
402 */
403 public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
404 Object... values) {
405 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
406 pvh.setObjectValues(values);
407 pvh.setEvaluator(evaluator);
408 return pvh;
409 }
410
411 /**
George Mountc96c7b22013-08-23 13:31:31 -0700412 * Constructs and returns a PropertyValuesHolder with a given property name and
413 * a Path along which the values should be animated. This variant supports a
414 * <code>TypeConverter</code> to convert from <code>PointF</code> to the target
415 * type.
416 *
George Mount984011f2014-08-21 14:28:01 -0700417 * <p>The PointF passed to <code>converter</code> or <code>property</code>, if
418 * <code>converter</code> is <code>null</code>, is reused on each animation frame and should
419 * not be stored by the setter or TypeConverter.</p>
420 *
George Mountc96c7b22013-08-23 13:31:31 -0700421 * @param propertyName The name of the property being animated.
422 * @param converter Converts a PointF to the type associated with the setter. May be
423 * null if conversion is unnecessary.
424 * @param path The Path along which the values should be animated.
425 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
426 */
427 public static PropertyValuesHolder ofObject(String propertyName,
428 TypeConverter<PointF, ?> converter, Path path) {
George Mount984011f2014-08-21 14:28:01 -0700429 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
430 pvh.mKeyframes = KeyframeSet.ofPath(path);
431 pvh.mValueType = PointF.class;
George Mountc96c7b22013-08-23 13:31:31 -0700432 pvh.setConverter(converter);
433 return pvh;
434 }
435
436 /**
Chet Haaseb39f0512011-05-24 14:36:40 -0700437 * Constructs and returns a PropertyValuesHolder with a given property and
438 * set of Object values. This variant also takes a TypeEvaluator because the system
439 * cannot automatically interpolate between objects of unknown type.
440 *
Chet Haasefa21bdf2016-04-21 17:41:54 -0700441 * <p><strong>Note:</strong> The Object values are stored as references to the original
442 * objects, which means that changes to those objects after this method is called will
443 * affect the values on the PropertyValuesHolder. If the objects will be mutated externally
444 * after this method is called, callers should pass a copy of those objects instead.
445 *
Chet Haaseb39f0512011-05-24 14:36:40 -0700446 * @param property The property being animated. Should not be null.
447 * @param evaluator A TypeEvaluator that will be called on each animation frame to
448 * provide the necessary interpolation between the Object values to derive the animated
449 * value.
450 * @param values The values that the property will animate between.
451 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
452 */
George Mountd98f4ba2016-03-14 14:29:24 -0700453 @SafeVarargs
Chet Haaseb39f0512011-05-24 14:36:40 -0700454 public static <V> PropertyValuesHolder ofObject(Property property,
455 TypeEvaluator<V> evaluator, V... values) {
456 PropertyValuesHolder pvh = new PropertyValuesHolder(property);
457 pvh.setObjectValues(values);
458 pvh.setEvaluator(evaluator);
459 return pvh;
460 }
461
462 /**
George Mount16d2c9c2013-09-17 09:07:48 -0700463 * Constructs and returns a PropertyValuesHolder with a given property and
464 * set of Object values. This variant also takes a TypeEvaluator because the system
465 * cannot automatically interpolate between objects of unknown type. This variant also
466 * takes a <code>TypeConverter</code> to convert from animated values to the type
467 * of the property. If only one value is supplied, the <code>TypeConverter</code>
George Mount42516d12014-05-19 15:49:29 -0700468 * must be a {@link android.animation.BidirectionalTypeConverter} to retrieve the current
George Mount16d2c9c2013-09-17 09:07:48 -0700469 * value.
470 *
Chet Haasefa21bdf2016-04-21 17:41:54 -0700471 * <p><strong>Note:</strong> The Object values are stored as references to the original
472 * objects, which means that changes to those objects after this method is called will
473 * affect the values on the PropertyValuesHolder. If the objects will be mutated externally
474 * after this method is called, callers should pass a copy of those objects instead.
475 *
George Mount16d2c9c2013-09-17 09:07:48 -0700476 * @param property The property being animated. Should not be null.
477 * @param converter Converts the animated object to the Property type.
478 * @param evaluator A TypeEvaluator that will be called on each animation frame to
479 * provide the necessary interpolation between the Object values to derive the animated
480 * value.
481 * @param values The values that the property will animate between.
482 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
483 * @see #setConverter(TypeConverter)
484 * @see TypeConverter
485 */
George Mountd98f4ba2016-03-14 14:29:24 -0700486 @SafeVarargs
George Mount16d2c9c2013-09-17 09:07:48 -0700487 public static <T, V> PropertyValuesHolder ofObject(Property<?, V> property,
488 TypeConverter<T, V> converter, TypeEvaluator<T> evaluator, T... values) {
489 PropertyValuesHolder pvh = new PropertyValuesHolder(property);
490 pvh.setConverter(converter);
491 pvh.setObjectValues(values);
492 pvh.setEvaluator(evaluator);
493 return pvh;
494 }
495
496 /**
George Mountc96c7b22013-08-23 13:31:31 -0700497 * Constructs and returns a PropertyValuesHolder with a given property and
498 * a Path along which the values should be animated. This variant supports a
499 * <code>TypeConverter</code> to convert from <code>PointF</code> to the target
500 * type.
501 *
George Mount984011f2014-08-21 14:28:01 -0700502 * <p>The PointF passed to <code>converter</code> or <code>property</code>, if
503 * <code>converter</code> is <code>null</code>, is reused on each animation frame and should
504 * not be stored by the setter or TypeConverter.</p>
505 *
George Mountc96c7b22013-08-23 13:31:31 -0700506 * @param property The property being animated. Should not be null.
507 * @param converter Converts a PointF to the type associated with the setter. May be
508 * null if conversion is unnecessary.
509 * @param path The Path along which the values should be animated.
510 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
511 */
512 public static <V> PropertyValuesHolder ofObject(Property<?, V> property,
513 TypeConverter<PointF, V> converter, Path path) {
George Mount984011f2014-08-21 14:28:01 -0700514 PropertyValuesHolder pvh = new PropertyValuesHolder(property);
515 pvh.mKeyframes = KeyframeSet.ofPath(path);
516 pvh.mValueType = PointF.class;
George Mountc96c7b22013-08-23 13:31:31 -0700517 pvh.setConverter(converter);
518 return pvh;
519 }
520
521 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700522 * Constructs and returns a PropertyValuesHolder object with the specified property name and set
523 * of values. These values can be of any type, but the type should be consistent so that
Chet Haased953d082010-08-16 17:44:28 -0700524 * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
525 * the common type.
526 * <p>If there is only one value, it is assumed to be the end value of an animation,
527 * and an initial value will be derived, if possible, by calling a getter function
528 * on the object. Also, if any value is null, the value will be filled in when the animation
529 * starts in the same way. This mechanism of automatically getting null values only works
530 * if the PropertyValuesHolder object is used in conjunction
Chet Haase1a8e4042010-12-07 11:29:39 -0800531 * {@link ObjectAnimator}, and with a getter function
532 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
Chet Haased953d082010-08-16 17:44:28 -0700533 * no way of determining what the value should be.
534 * @param propertyName The name of the property associated with this set of values. This
Chet Haasea18a86b2010-09-07 13:20:00 -0700535 * can be the actual property name to be used when using a ObjectAnimator object, or
Chet Haased953d082010-08-16 17:44:28 -0700536 * just a name used to get animated values, such as if this object is used with an
Chet Haasea18a86b2010-09-07 13:20:00 -0700537 * ValueAnimator object.
Chet Haased953d082010-08-16 17:44:28 -0700538 * @param values The set of values to animate between.
539 */
Chet Haase2794eb32010-10-12 16:29:28 -0700540 public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
Chet Haase7c608f22010-10-22 17:54:04 -0700541 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
George Mount984011f2014-08-21 14:28:01 -0700542 return ofKeyframes(propertyName, keyframeSet);
Chet Haased953d082010-08-16 17:44:28 -0700543 }
544
545 /**
Chet Haaseb39f0512011-05-24 14:36:40 -0700546 * Constructs and returns a PropertyValuesHolder object with the specified property and set
547 * of values. These values can be of any type, but the type should be consistent so that
548 * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
549 * the common type.
550 * <p>If there is only one value, it is assumed to be the end value of an animation,
551 * and an initial value will be derived, if possible, by calling the property's
552 * {@link android.util.Property#get(Object)} function.
553 * Also, if any value is null, the value will be filled in when the animation
554 * starts in the same way. This mechanism of automatically getting null values only works
555 * if the PropertyValuesHolder object is used in conjunction with
556 * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has
557 * no way of determining what the value should be.
558 * @param property The property associated with this set of values. Should not be null.
559 * @param values The set of values to animate between.
560 */
561 public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
562 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
George Mount984011f2014-08-21 14:28:01 -0700563 return ofKeyframes(property, keyframeSet);
564 }
565
566 static PropertyValuesHolder ofKeyframes(String propertyName, Keyframes keyframes) {
567 if (keyframes instanceof Keyframes.IntKeyframes) {
568 return new IntPropertyValuesHolder(propertyName, (Keyframes.IntKeyframes) keyframes);
569 } else if (keyframes instanceof Keyframes.FloatKeyframes) {
570 return new FloatPropertyValuesHolder(propertyName,
571 (Keyframes.FloatKeyframes) keyframes);
572 } else {
573 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
574 pvh.mKeyframes = keyframes;
575 pvh.mValueType = keyframes.getType();
576 return pvh;
Chet Haaseb39f0512011-05-24 14:36:40 -0700577 }
George Mount984011f2014-08-21 14:28:01 -0700578 }
579
580 static PropertyValuesHolder ofKeyframes(Property property, Keyframes keyframes) {
581 if (keyframes instanceof Keyframes.IntKeyframes) {
582 return new IntPropertyValuesHolder(property, (Keyframes.IntKeyframes) keyframes);
583 } else if (keyframes instanceof Keyframes.FloatKeyframes) {
584 return new FloatPropertyValuesHolder(property, (Keyframes.FloatKeyframes) keyframes);
585 } else {
Chet Haaseb39f0512011-05-24 14:36:40 -0700586 PropertyValuesHolder pvh = new PropertyValuesHolder(property);
George Mount984011f2014-08-21 14:28:01 -0700587 pvh.mKeyframes = keyframes;
588 pvh.mValueType = keyframes.getType();
Chet Haaseb39f0512011-05-24 14:36:40 -0700589 return pvh;
590 }
591 }
592
593 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700594 * Set the animated values for this object to this set of ints.
Chet Haased953d082010-08-16 17:44:28 -0700595 * If there is only one value, it is assumed to be the end value of an animation,
596 * and an initial value will be derived, if possible, by calling a getter function
597 * on the object. Also, if any value is null, the value will be filled in when the animation
598 * starts in the same way. This mechanism of automatically getting null values only works
599 * if the PropertyValuesHolder object is used in conjunction
Chet Haase1a8e4042010-12-07 11:29:39 -0800600 * {@link ObjectAnimator}, and with a getter function
601 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
Chet Haased953d082010-08-16 17:44:28 -0700602 * no way of determining what the value should be.
Chet Haase2794eb32010-10-12 16:29:28 -0700603 *
604 * @param values One or more values that the animation will animate between.
Chet Haased953d082010-08-16 17:44:28 -0700605 */
Chet Haase2794eb32010-10-12 16:29:28 -0700606 public void setIntValues(int... values) {
607 mValueType = int.class;
George Mount984011f2014-08-21 14:28:01 -0700608 mKeyframes = KeyframeSet.ofInt(values);
Chet Haase2794eb32010-10-12 16:29:28 -0700609 }
Chet Haased953d082010-08-16 17:44:28 -0700610
Chet Haase2794eb32010-10-12 16:29:28 -0700611 /**
612 * Set the animated values for this object to this set of floats.
613 * If there is only one value, it is assumed to be the end value of an animation,
614 * and an initial value will be derived, if possible, by calling a getter function
615 * on the object. Also, if any value is null, the value will be filled in when the animation
616 * starts in the same way. This mechanism of automatically getting null values only works
617 * if the PropertyValuesHolder object is used in conjunction
Chet Haase1a8e4042010-12-07 11:29:39 -0800618 * {@link ObjectAnimator}, and with a getter function
619 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
Chet Haase2794eb32010-10-12 16:29:28 -0700620 * no way of determining what the value should be.
621 *
622 * @param values One or more values that the animation will animate between.
623 */
624 public void setFloatValues(float... values) {
625 mValueType = float.class;
George Mount984011f2014-08-21 14:28:01 -0700626 mKeyframes = KeyframeSet.ofFloat(values);
Chet Haase2794eb32010-10-12 16:29:28 -0700627 }
628
629 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700630 * Set the animated values for this object to this set of Keyframes.
631 *
632 * @param values One or more values that the animation will animate between.
633 */
634 public void setKeyframes(Keyframe... values) {
635 int numKeyframes = values.length;
636 Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
637 mValueType = ((Keyframe)values[0]).getType();
638 for (int i = 0; i < numKeyframes; ++i) {
639 keyframes[i] = (Keyframe)values[i];
Chet Haased953d082010-08-16 17:44:28 -0700640 }
George Mount984011f2014-08-21 14:28:01 -0700641 mKeyframes = new KeyframeSet(keyframes);
Chet Haased953d082010-08-16 17:44:28 -0700642 }
643
Chet Haase2794eb32010-10-12 16:29:28 -0700644 /**
645 * Set the animated values for this object to this set of Objects.
646 * If there is only one value, it is assumed to be the end value of an animation,
647 * and an initial value will be derived, if possible, by calling a getter function
648 * on the object. Also, if any value is null, the value will be filled in when the animation
649 * starts in the same way. This mechanism of automatically getting null values only works
650 * if the PropertyValuesHolder object is used in conjunction
Chet Haase1a8e4042010-12-07 11:29:39 -0800651 * {@link ObjectAnimator}, and with a getter function
652 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
Chet Haase2794eb32010-10-12 16:29:28 -0700653 * no way of determining what the value should be.
Chet Haasefa21bdf2016-04-21 17:41:54 -0700654 *
655 * <p><strong>Note:</strong> The Object values are stored as references to the original
656 * objects, which means that changes to those objects after this method is called will
657 * affect the values on the PropertyValuesHolder. If the objects will be mutated externally
658 * after this method is called, callers should pass a copy of those objects instead.
659 *
Chet Haase2794eb32010-10-12 16:29:28 -0700660 * @param values One or more values that the animation will animate between.
661 */
662 public void setObjectValues(Object... values) {
663 mValueType = values[0].getClass();
George Mount984011f2014-08-21 14:28:01 -0700664 mKeyframes = KeyframeSet.ofObject(values);
Chet Haase4ae3e6a2014-07-14 18:28:28 -0700665 if (mEvaluator != null) {
George Mount984011f2014-08-21 14:28:01 -0700666 mKeyframes.setEvaluator(mEvaluator);
Chet Haase4ae3e6a2014-07-14 18:28:28 -0700667 }
Chet Haase2794eb32010-10-12 16:29:28 -0700668 }
Chet Haased953d082010-08-16 17:44:28 -0700669
670 /**
George Mount16d2c9c2013-09-17 09:07:48 -0700671 * Sets the converter to convert from the values type to the setter's parameter type.
George Mount42516d12014-05-19 15:49:29 -0700672 * If only one value is supplied, <var>converter</var> must be a
673 * {@link android.animation.BidirectionalTypeConverter}.
George Mount16d2c9c2013-09-17 09:07:48 -0700674 * @param converter The converter to use to convert values.
675 */
676 public void setConverter(TypeConverter converter) {
677 mConverter = converter;
678 }
679
680 /**
Chet Haased953d082010-08-16 17:44:28 -0700681 * Determine the setter or getter function using the JavaBeans convention of setFoo or
682 * getFoo for a property named 'foo'. This function figures out what the name of the
683 * function should be and uses reflection to find the Method with that name on the
684 * target object.
685 *
686 * @param targetClass The class to search for the method
687 * @param prefix "set" or "get", depending on whether we need a setter or getter.
688 * @param valueType The type of the parameter (in the case of a setter). This type
689 * is derived from the values set on this PropertyValuesHolder. This type is used as
690 * a first guess at the parameter type, but we check for methods with several different
691 * types to avoid problems with slight mis-matches between supplied values and actual
692 * value types used on the setter.
693 * @return Method the method associated with mPropertyName.
694 */
695 private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
696 // TODO: faster implementation...
697 Method returnVal = null;
Chet Haase6e0ecb42010-11-03 19:41:18 -0700698 String methodName = getMethodName(prefix, mPropertyName);
Chet Haased953d082010-08-16 17:44:28 -0700699 Class args[] = null;
700 if (valueType == null) {
701 try {
702 returnVal = targetClass.getMethod(methodName, args);
703 } catch (NoSuchMethodException e) {
Chet Haasedb4101c2012-05-03 14:48:07 -0700704 // Swallow the error, log it later
Chet Haased953d082010-08-16 17:44:28 -0700705 }
706 } else {
707 args = new Class[1];
708 Class typeVariants[];
George Mount16d2c9c2013-09-17 09:07:48 -0700709 if (valueType.equals(Float.class)) {
Chet Haased953d082010-08-16 17:44:28 -0700710 typeVariants = FLOAT_VARIANTS;
George Mount16d2c9c2013-09-17 09:07:48 -0700711 } else if (valueType.equals(Integer.class)) {
Chet Haased953d082010-08-16 17:44:28 -0700712 typeVariants = INTEGER_VARIANTS;
George Mount16d2c9c2013-09-17 09:07:48 -0700713 } else if (valueType.equals(Double.class)) {
Chet Haased953d082010-08-16 17:44:28 -0700714 typeVariants = DOUBLE_VARIANTS;
715 } else {
716 typeVariants = new Class[1];
George Mount16d2c9c2013-09-17 09:07:48 -0700717 typeVariants[0] = valueType;
Chet Haased953d082010-08-16 17:44:28 -0700718 }
719 for (Class typeVariant : typeVariants) {
720 args[0] = typeVariant;
721 try {
722 returnVal = targetClass.getMethod(methodName, args);
George Mount16d2c9c2013-09-17 09:07:48 -0700723 if (mConverter == null) {
724 // change the value type to suit
725 mValueType = typeVariant;
726 }
Chet Haased953d082010-08-16 17:44:28 -0700727 return returnVal;
728 } catch (NoSuchMethodException e) {
729 // Swallow the error and keep trying other variants
730 }
731 }
Chet Haase602e4d32010-08-16 08:57:23 -0700732 // If we got here, then no appropriate function was found
Chet Haasedb4101c2012-05-03 14:48:07 -0700733 }
734
735 if (returnVal == null) {
736 Log.w("PropertyValuesHolder", "Method " +
George Mount16d2c9c2013-09-17 09:07:48 -0700737 getMethodName(prefix, mPropertyName) + "() with type " + valueType +
Chet Haasedb4101c2012-05-03 14:48:07 -0700738 " not found on target class " + targetClass);
Chet Haased953d082010-08-16 17:44:28 -0700739 }
Chet Haase602e4d32010-08-16 08:57:23 -0700740
Chet Haased953d082010-08-16 17:44:28 -0700741 return returnVal;
742 }
743
744
745 /**
746 * Returns the setter or getter requested. This utility function checks whether the
747 * requested method exists in the propertyMapMap cache. If not, it calls another
748 * utility function to request the Method from the targetClass directly.
749 * @param targetClass The Class on which the requested method should exist.
750 * @param propertyMapMap The cache of setters/getters derived so far.
751 * @param prefix "set" or "get", for the setter or getter.
752 * @param valueType The type of parameter passed into the method (null for getter).
753 * @return Method the method associated with mPropertyName.
754 */
755 private Method setupSetterOrGetter(Class targetClass,
756 HashMap<Class, HashMap<String, Method>> propertyMapMap,
757 String prefix, Class valueType) {
758 Method setterOrGetter = null;
George Mount691487d2014-11-10 16:46:31 -0800759 synchronized(propertyMapMap) {
Chet Haased953d082010-08-16 17:44:28 -0700760 // Have to lock property map prior to reading it, to guard against
761 // another thread putting something in there after we've checked it
762 // but before we've added an entry to it
Chet Haased953d082010-08-16 17:44:28 -0700763 HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
George Mount691487d2014-11-10 16:46:31 -0800764 boolean wasInMap = false;
Chet Haased953d082010-08-16 17:44:28 -0700765 if (propertyMap != null) {
George Mount691487d2014-11-10 16:46:31 -0800766 wasInMap = propertyMap.containsKey(mPropertyName);
767 if (wasInMap) {
768 setterOrGetter = propertyMap.get(mPropertyName);
769 }
Chet Haased953d082010-08-16 17:44:28 -0700770 }
George Mount691487d2014-11-10 16:46:31 -0800771 if (!wasInMap) {
Chet Haased953d082010-08-16 17:44:28 -0700772 setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
773 if (propertyMap == null) {
774 propertyMap = new HashMap<String, Method>();
775 propertyMapMap.put(targetClass, propertyMap);
776 }
777 propertyMap.put(mPropertyName, setterOrGetter);
778 }
Chet Haased953d082010-08-16 17:44:28 -0700779 }
780 return setterOrGetter;
781 }
782
783 /**
784 * Utility function to get the setter from targetClass
785 * @param targetClass The Class on which the requested method should exist.
786 */
Chet Haase6e0ecb42010-11-03 19:41:18 -0700787 void setupSetter(Class targetClass) {
George Mount16d2c9c2013-09-17 09:07:48 -0700788 Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
789 mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
Chet Haased953d082010-08-16 17:44:28 -0700790 }
791
792 /**
793 * Utility function to get the getter from targetClass
794 */
795 private void setupGetter(Class targetClass) {
796 mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
797 }
798
799 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700800 * Internal function (called from ObjectAnimator) to set up the setter and getter
Chet Haased953d082010-08-16 17:44:28 -0700801 * prior to running the animation. If the setter has not been manually set for this
802 * object, it will be derived automatically given the property name, target object, and
803 * types of values supplied. If no getter has been set, it will be supplied iff any of the
804 * supplied values was null. If there is a null value, then the getter (supplied or derived)
805 * will be called to set those null values to the current value of the property
806 * on the target object.
807 * @param target The object on which the setter (and possibly getter) exist.
808 */
809 void setupSetterAndGetter(Object target) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700810 if (mProperty != null) {
811 // check to make sure that mProperty is on the class of target
812 try {
George Mount16d2c9c2013-09-17 09:07:48 -0700813 Object testValue = null;
Yigit Boyard422dc32014-09-25 12:23:35 -0700814 List<Keyframe> keyframes = mKeyframes.getKeyframes();
George Mount984011f2014-08-21 14:28:01 -0700815 int keyframeCount = keyframes == null ? 0 : keyframes.size();
816 for (int i = 0; i < keyframeCount; i++) {
817 Keyframe kf = keyframes.get(i);
Yigit Boyar8619f482014-07-15 17:28:07 -0700818 if (!kf.hasValue() || kf.valueWasSetOnStart()) {
George Mount16d2c9c2013-09-17 09:07:48 -0700819 if (testValue == null) {
820 testValue = convertBack(mProperty.get(target));
821 }
822 kf.setValue(testValue);
Yigit Boyar8619f482014-07-15 17:28:07 -0700823 kf.setValueWasSetOnStart(true);
Chet Haaseb39f0512011-05-24 14:36:40 -0700824 }
825 }
826 return;
827 } catch (ClassCastException e) {
Chet Haasedb4101c2012-05-03 14:48:07 -0700828 Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
Chet Haaseb39f0512011-05-24 14:36:40 -0700829 ") on target object " + target + ". Trying reflection instead");
830 mProperty = null;
831 }
832 }
George Mount691487d2014-11-10 16:46:31 -0800833 // We can't just say 'else' here because the catch statement sets mProperty to null.
834 if (mProperty == null) {
835 Class targetClass = target.getClass();
836 if (mSetter == null) {
837 setupSetter(targetClass);
838 }
839 List<Keyframe> keyframes = mKeyframes.getKeyframes();
840 int keyframeCount = keyframes == null ? 0 : keyframes.size();
841 for (int i = 0; i < keyframeCount; i++) {
842 Keyframe kf = keyframes.get(i);
843 if (!kf.hasValue() || kf.valueWasSetOnStart()) {
Chet Haasedb4101c2012-05-03 14:48:07 -0700844 if (mGetter == null) {
George Mount691487d2014-11-10 16:46:31 -0800845 setupGetter(targetClass);
846 if (mGetter == null) {
847 // Already logged the error - just return to avoid NPE
848 return;
849 }
Chet Haasedb4101c2012-05-03 14:48:07 -0700850 }
George Mount691487d2014-11-10 16:46:31 -0800851 try {
852 Object value = convertBack(mGetter.invoke(target));
853 kf.setValue(value);
854 kf.setValueWasSetOnStart(true);
855 } catch (InvocationTargetException e) {
856 Log.e("PropertyValuesHolder", e.toString());
857 } catch (IllegalAccessException e) {
858 Log.e("PropertyValuesHolder", e.toString());
859 }
Chet Haased953d082010-08-16 17:44:28 -0700860 }
861 }
862 }
863 }
864
George Mount16d2c9c2013-09-17 09:07:48 -0700865 private Object convertBack(Object value) {
866 if (mConverter != null) {
George Mount42516d12014-05-19 15:49:29 -0700867 if (!(mConverter instanceof BidirectionalTypeConverter)) {
George Mount16d2c9c2013-09-17 09:07:48 -0700868 throw new IllegalArgumentException("Converter "
869 + mConverter.getClass().getName()
George Mount42516d12014-05-19 15:49:29 -0700870 + " must be a BidirectionalTypeConverter");
George Mount16d2c9c2013-09-17 09:07:48 -0700871 }
George Mount42516d12014-05-19 15:49:29 -0700872 value = ((BidirectionalTypeConverter) mConverter).convertBack(value);
George Mount16d2c9c2013-09-17 09:07:48 -0700873 }
874 return value;
875 }
876
Chet Haased953d082010-08-16 17:44:28 -0700877 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700878 * Utility function to set the value stored in a particular Keyframe. The value used is
879 * whatever the value is for the property name specified in the keyframe on the target object.
880 *
881 * @param target The target object from which the current value should be extracted.
882 * @param kf The keyframe which holds the property name and value.
883 */
884 private void setupValue(Object target, Keyframe kf) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700885 if (mProperty != null) {
George Mount16d2c9c2013-09-17 09:07:48 -0700886 Object value = convertBack(mProperty.get(target));
887 kf.setValue(value);
Chet Haase406a02b2016-01-11 16:27:20 -0800888 } else {
889 try {
Chet Haasedb4101c2012-05-03 14:48:07 -0700890 if (mGetter == null) {
Chet Haase406a02b2016-01-11 16:27:20 -0800891 Class targetClass = target.getClass();
892 setupGetter(targetClass);
893 if (mGetter == null) {
894 // Already logged the error - just return to avoid NPE
895 return;
896 }
Chet Haasedb4101c2012-05-03 14:48:07 -0700897 }
Chet Haase406a02b2016-01-11 16:27:20 -0800898 Object value = convertBack(mGetter.invoke(target));
899 kf.setValue(value);
900 } catch (InvocationTargetException e) {
901 Log.e("PropertyValuesHolder", e.toString());
902 } catch (IllegalAccessException e) {
903 Log.e("PropertyValuesHolder", e.toString());
Chet Haase21cd1382010-09-01 17:42:29 -0700904 }
Chet Haase21cd1382010-09-01 17:42:29 -0700905 }
906 }
907
908 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700909 * This function is called by ObjectAnimator when setting the start values for an animation.
Chet Haase21cd1382010-09-01 17:42:29 -0700910 * The start values are set according to the current values in the target object. The
911 * property whose value is extracted is whatever is specified by the propertyName of this
912 * PropertyValuesHolder object.
913 *
914 * @param target The object which holds the start values that should be set.
915 */
916 void setupStartValue(Object target) {
Yigit Boyard422dc32014-09-25 12:23:35 -0700917 List<Keyframe> keyframes = mKeyframes.getKeyframes();
George Mount984011f2014-08-21 14:28:01 -0700918 if (!keyframes.isEmpty()) {
919 setupValue(target, keyframes.get(0));
920 }
Chet Haase21cd1382010-09-01 17:42:29 -0700921 }
922
923 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700924 * This function is called by ObjectAnimator when setting the end values for an animation.
Chet Haase21cd1382010-09-01 17:42:29 -0700925 * The end values are set according to the current values in the target object. The
926 * property whose value is extracted is whatever is specified by the propertyName of this
927 * PropertyValuesHolder object.
928 *
929 * @param target The object which holds the start values that should be set.
930 */
931 void setupEndValue(Object target) {
Yigit Boyard422dc32014-09-25 12:23:35 -0700932 List<Keyframe> keyframes = mKeyframes.getKeyframes();
George Mount984011f2014-08-21 14:28:01 -0700933 if (!keyframes.isEmpty()) {
934 setupValue(target, keyframes.get(keyframes.size() - 1));
935 }
Chet Haase21cd1382010-09-01 17:42:29 -0700936 }
937
938 @Override
939 public PropertyValuesHolder clone() {
Chet Haase7c608f22010-10-22 17:54:04 -0700940 try {
941 PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
942 newPVH.mPropertyName = mPropertyName;
Chet Haaseb39f0512011-05-24 14:36:40 -0700943 newPVH.mProperty = mProperty;
George Mount984011f2014-08-21 14:28:01 -0700944 newPVH.mKeyframes = mKeyframes.clone();
Chet Haase7c608f22010-10-22 17:54:04 -0700945 newPVH.mEvaluator = mEvaluator;
946 return newPVH;
947 } catch (CloneNotSupportedException e) {
948 // won't reach here
949 return null;
Chet Haase21cd1382010-09-01 17:42:29 -0700950 }
Chet Haase21cd1382010-09-01 17:42:29 -0700951 }
Chet Haase7c608f22010-10-22 17:54:04 -0700952
Chet Haase21cd1382010-09-01 17:42:29 -0700953 /**
Chet Haased953d082010-08-16 17:44:28 -0700954 * Internal function to set the value on the target object, using the setter set up
Chet Haasea18a86b2010-09-07 13:20:00 -0700955 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
956 * to handle turning the value calculated by ValueAnimator into a value set on the object
Chet Haased953d082010-08-16 17:44:28 -0700957 * according to the name of the property.
958 * @param target The target object on which the value is set
959 */
960 void setAnimatedValue(Object target) {
Chet Haaseb39f0512011-05-24 14:36:40 -0700961 if (mProperty != null) {
962 mProperty.set(target, getAnimatedValue());
963 }
Chet Haased953d082010-08-16 17:44:28 -0700964 if (mSetter != null) {
965 try {
Chet Haase7c608f22010-10-22 17:54:04 -0700966 mTmpValueArray[0] = getAnimatedValue();
Chet Haased953d082010-08-16 17:44:28 -0700967 mSetter.invoke(target, mTmpValueArray);
968 } catch (InvocationTargetException e) {
969 Log.e("PropertyValuesHolder", e.toString());
970 } catch (IllegalAccessException e) {
971 Log.e("PropertyValuesHolder", e.toString());
972 }
973 }
974 }
975
976 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700977 * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
Chet Haased953d082010-08-16 17:44:28 -0700978 * to calculate animated values.
979 */
980 void init() {
981 if (mEvaluator == null) {
Chet Haaseb2ab04f2011-01-16 11:03:22 -0800982 // We already handle int and float automatically, but not their Object
Chet Haase7c608f22010-10-22 17:54:04 -0700983 // equivalents
984 mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
985 (mValueType == Float.class) ? sFloatEvaluator :
986 null;
987 }
988 if (mEvaluator != null) {
989 // KeyframeSet knows how to evaluate the common types - only give it a custom
Chet Haaseb2ab04f2011-01-16 11:03:22 -0800990 // evaluator if one has been set on this class
George Mount984011f2014-08-21 14:28:01 -0700991 mKeyframes.setEvaluator(mEvaluator);
Chet Haased953d082010-08-16 17:44:28 -0700992 }
993 }
994
995 /**
Chet Haased82c8ac2013-08-26 14:20:16 -0700996 * The TypeEvaluator will be automatically determined based on the type of values
Chet Haased953d082010-08-16 17:44:28 -0700997 * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
998 * desired. This may be important in cases where either the type of the values supplied
999 * do not match the way that they should be interpolated between, or if the values
1000 * are of a custom type or one not currently understood by the animation system. Currently,
Chet Haaseb2ab04f2011-01-16 11:03:22 -08001001 * only values of type float and int (and their Object equivalents: Float
Chet Haased953d082010-08-16 17:44:28 -07001002 * and Integer) are correctly interpolated; all other types require setting a TypeEvaluator.
1003 * @param evaluator
1004 */
Chet Haase7c608f22010-10-22 17:54:04 -07001005 public void setEvaluator(TypeEvaluator evaluator) {
Chet Haased953d082010-08-16 17:44:28 -07001006 mEvaluator = evaluator;
George Mount984011f2014-08-21 14:28:01 -07001007 mKeyframes.setEvaluator(evaluator);
Chet Haased953d082010-08-16 17:44:28 -07001008 }
1009
1010 /**
1011 * Function used to calculate the value according to the evaluator set up for
Chet Haasea18a86b2010-09-07 13:20:00 -07001012 * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
Chet Haased953d082010-08-16 17:44:28 -07001013 *
1014 * @param fraction The elapsed, interpolated fraction of the animation.
Chet Haased953d082010-08-16 17:44:28 -07001015 */
Chet Haase7c608f22010-10-22 17:54:04 -07001016 void calculateValue(float fraction) {
George Mount984011f2014-08-21 14:28:01 -07001017 Object value = mKeyframes.getValue(fraction);
George Mount16d2c9c2013-09-17 09:07:48 -07001018 mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
Chet Haased953d082010-08-16 17:44:28 -07001019 }
1020
1021 /**
Chet Haased953d082010-08-16 17:44:28 -07001022 * Sets the name of the property that will be animated. This name is used to derive
1023 * a setter function that will be called to set animated values.
1024 * For example, a property name of <code>foo</code> will result
1025 * in a call to the function <code>setFoo()</code> on the target object. If either
1026 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
1027 * also be derived and called.
1028 *
1029 * <p>Note that the setter function derived from this property name
1030 * must take the same parameter type as the
1031 * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
1032 * the setter function will fail.</p>
1033 *
1034 * @param propertyName The name of the property being animated.
1035 */
1036 public void setPropertyName(String propertyName) {
1037 mPropertyName = propertyName;
1038 }
1039
1040 /**
Chet Haaseb39f0512011-05-24 14:36:40 -07001041 * Sets the property that will be animated.
1042 *
1043 * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
1044 * must exist on the target object specified in that ObjectAnimator.</p>
1045 *
1046 * @param property The property being animated.
1047 */
1048 public void setProperty(Property property) {
1049 mProperty = property;
1050 }
1051
1052 /**
Chet Haased953d082010-08-16 17:44:28 -07001053 * Gets the name of the property that will be animated. This name will be used to derive
1054 * a setter function that will be called to set animated values.
1055 * For example, a property name of <code>foo</code> will result
1056 * in a call to the function <code>setFoo()</code> on the target object. If either
1057 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
1058 * also be derived and called.
1059 */
1060 public String getPropertyName() {
1061 return mPropertyName;
1062 }
1063
1064 /**
Chet Haasea18a86b2010-09-07 13:20:00 -07001065 * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
Chet Haased953d082010-08-16 17:44:28 -07001066 * most recently calculated in calculateValue().
1067 * @return
1068 */
1069 Object getAnimatedValue() {
1070 return mAnimatedValue;
1071 }
Chet Haase7c608f22010-10-22 17:54:04 -07001072
Doris Liu766431a2016-02-04 22:17:11 +00001073 /**
1074 * PropertyValuesHolder is Animators use to hold internal animation related data.
1075 * Therefore, in order to replicate the animation behavior, we need to get data out of
1076 * PropertyValuesHolder.
1077 * @hide
1078 */
1079 public void getPropertyValues(PropertyValues values) {
1080 init();
1081 values.propertyName = mPropertyName;
1082 values.type = mValueType;
1083 values.startValue = mKeyframes.getValue(0);
1084 if (values.startValue instanceof PathParser.PathData) {
1085 // PathData evaluator returns the same mutable PathData object when query fraction,
1086 // so we have to make a copy here.
1087 values.startValue = new PathParser.PathData((PathParser.PathData) values.startValue);
1088 }
1089 values.endValue = mKeyframes.getValue(1);
1090 if (values.endValue instanceof PathParser.PathData) {
1091 // PathData evaluator returns the same mutable PathData object when query fraction,
1092 // so we have to make a copy here.
1093 values.endValue = new PathParser.PathData((PathParser.PathData) values.endValue);
1094 }
1095 // TODO: We need a better way to get data out of keyframes.
1096 if (mKeyframes instanceof PathKeyframes.FloatKeyframesBase
Doris Liua6b967c2016-06-02 16:20:18 -07001097 || mKeyframes instanceof PathKeyframes.IntKeyframesBase
1098 || (mKeyframes.getKeyframes() != null && mKeyframes.getKeyframes().size() > 2)) {
1099 // When a pvh has more than 2 keyframes, that means there are intermediate values in
1100 // addition to start/end values defined for animators. Another case where such
1101 // intermediate values are defined is when animator has a path to animate along. In
1102 // these cases, a data source is needed to capture these intermediate values.
Doris Liu766431a2016-02-04 22:17:11 +00001103 values.dataSource = new PropertyValues.DataSource() {
1104 @Override
1105 public Object getValueAtFraction(float fraction) {
1106 return mKeyframes.getValue(fraction);
1107 }
1108 };
1109 } else {
1110 values.dataSource = null;
1111 }
1112 }
1113
Doris Liu94db0992016-06-01 14:22:28 -07001114 /**
1115 * @hide
1116 */
1117 public Class getValueType() {
1118 return mValueType;
1119 }
1120
Chet Haasee9140a72011-02-16 16:23:29 -08001121 @Override
1122 public String toString() {
George Mount984011f2014-08-21 14:28:01 -07001123 return mPropertyName + ": " + mKeyframes.toString();
Chet Haasee9140a72011-02-16 16:23:29 -08001124 }
1125
Chet Haase6e0ecb42010-11-03 19:41:18 -07001126 /**
1127 * Utility method to derive a setter/getter method name from a property name, where the
1128 * prefix is typically "set" or "get" and the first letter of the property name is
1129 * capitalized.
1130 *
1131 * @param prefix The precursor to the method name, before the property name begins, typically
1132 * "set" or "get".
1133 * @param propertyName The name of the property that represents the bulk of the method name
1134 * after the prefix. The first letter of this word will be capitalized in the resulting
1135 * method name.
1136 * @return String the property name converted to a method name according to the conventions
1137 * specified above.
1138 */
1139 static String getMethodName(String prefix, String propertyName) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001140 if (propertyName == null || propertyName.length() == 0) {
1141 // shouldn't get here
1142 return prefix;
1143 }
1144 char firstLetter = Character.toUpperCase(propertyName.charAt(0));
Chet Haase6e0ecb42010-11-03 19:41:18 -07001145 String theRest = propertyName.substring(1);
Chet Haase6e0ecb42010-11-03 19:41:18 -07001146 return prefix + firstLetter + theRest;
1147 }
1148
Chet Haase7c608f22010-10-22 17:54:04 -07001149 static class IntPropertyValuesHolder extends PropertyValuesHolder {
1150
Chet Haaseb39f0512011-05-24 14:36:40 -07001151 // Cache JNI functions to avoid looking them up twice
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001152 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1153 new HashMap<Class, HashMap<String, Long>>();
1154 long mJniSetter;
Chet Haaseb39f0512011-05-24 14:36:40 -07001155 private IntProperty mIntProperty;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001156
George Mount984011f2014-08-21 14:28:01 -07001157 Keyframes.IntKeyframes mIntKeyframes;
Chet Haase7c608f22010-10-22 17:54:04 -07001158 int mIntAnimatedValue;
1159
George Mount984011f2014-08-21 14:28:01 -07001160 public IntPropertyValuesHolder(String propertyName, Keyframes.IntKeyframes keyframes) {
Chet Haase7c608f22010-10-22 17:54:04 -07001161 super(propertyName);
1162 mValueType = int.class;
George Mount984011f2014-08-21 14:28:01 -07001163 mKeyframes = keyframes;
1164 mIntKeyframes = keyframes;
Chet Haase7c608f22010-10-22 17:54:04 -07001165 }
1166
George Mount984011f2014-08-21 14:28:01 -07001167 public IntPropertyValuesHolder(Property property, Keyframes.IntKeyframes keyframes) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001168 super(property);
1169 mValueType = int.class;
George Mount984011f2014-08-21 14:28:01 -07001170 mKeyframes = keyframes;
1171 mIntKeyframes = keyframes;
Chet Haaseb39f0512011-05-24 14:36:40 -07001172 if (property instanceof IntProperty) {
1173 mIntProperty = (IntProperty) mProperty;
1174 }
1175 }
1176
Chet Haase7c608f22010-10-22 17:54:04 -07001177 public IntPropertyValuesHolder(String propertyName, int... values) {
1178 super(propertyName);
1179 setIntValues(values);
Chet Haase691ac262010-11-03 22:38:32 -07001180 }
1181
Chet Haaseb39f0512011-05-24 14:36:40 -07001182 public IntPropertyValuesHolder(Property property, int... values) {
1183 super(property);
1184 setIntValues(values);
1185 if (property instanceof IntProperty) {
1186 mIntProperty = (IntProperty) mProperty;
1187 }
1188 }
1189
Chet Haase691ac262010-11-03 22:38:32 -07001190 @Override
Doris Liu9f3b31b2016-05-19 19:04:47 -07001191 public void setProperty(Property property) {
1192 if (property instanceof IntProperty) {
1193 mIntProperty = (IntProperty) property;
1194 } else {
1195 super.setProperty(property);
1196 }
1197 }
1198
1199 @Override
Chet Haase691ac262010-11-03 22:38:32 -07001200 public void setIntValues(int... values) {
1201 super.setIntValues(values);
George Mount984011f2014-08-21 14:28:01 -07001202 mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
Chet Haase7c608f22010-10-22 17:54:04 -07001203 }
1204
1205 @Override
1206 void calculateValue(float fraction) {
George Mount984011f2014-08-21 14:28:01 -07001207 mIntAnimatedValue = mIntKeyframes.getIntValue(fraction);
Chet Haase7c608f22010-10-22 17:54:04 -07001208 }
1209
1210 @Override
1211 Object getAnimatedValue() {
1212 return mIntAnimatedValue;
1213 }
1214
1215 @Override
1216 public IntPropertyValuesHolder clone() {
1217 IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
George Mount984011f2014-08-21 14:28:01 -07001218 newPVH.mIntKeyframes = (Keyframes.IntKeyframes) newPVH.mKeyframes;
Chet Haase7c608f22010-10-22 17:54:04 -07001219 return newPVH;
1220 }
1221
1222 /**
1223 * Internal function to set the value on the target object, using the setter set up
1224 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1225 * to handle turning the value calculated by ValueAnimator into a value set on the object
1226 * according to the name of the property.
1227 * @param target The target object on which the value is set
1228 */
1229 @Override
1230 void setAnimatedValue(Object target) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001231 if (mIntProperty != null) {
1232 mIntProperty.setValue(target, mIntAnimatedValue);
1233 return;
1234 }
1235 if (mProperty != null) {
1236 mProperty.set(target, mIntAnimatedValue);
1237 return;
1238 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001239 if (mJniSetter != 0) {
1240 nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
1241 return;
1242 }
Chet Haase7c608f22010-10-22 17:54:04 -07001243 if (mSetter != null) {
1244 try {
1245 mTmpValueArray[0] = mIntAnimatedValue;
1246 mSetter.invoke(target, mTmpValueArray);
1247 } catch (InvocationTargetException e) {
1248 Log.e("PropertyValuesHolder", e.toString());
1249 } catch (IllegalAccessException e) {
1250 Log.e("PropertyValuesHolder", e.toString());
1251 }
1252 }
1253 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001254
1255 @Override
1256 void setupSetter(Class targetClass) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001257 if (mProperty != null) {
1258 return;
1259 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001260 // Check new static hashmap<propName, int> for setter method
George Mount691487d2014-11-10 16:46:31 -08001261 synchronized(sJNISetterPropertyMap) {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001262 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
George Mount691487d2014-11-10 16:46:31 -08001263 boolean wasInMap = false;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001264 if (propertyMap != null) {
George Mount691487d2014-11-10 16:46:31 -08001265 wasInMap = propertyMap.containsKey(mPropertyName);
1266 if (wasInMap) {
1267 Long jniSetter = propertyMap.get(mPropertyName);
1268 if (jniSetter != null) {
1269 mJniSetter = jniSetter;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001270 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001271 }
1272 }
George Mount691487d2014-11-10 16:46:31 -08001273 if (!wasInMap) {
1274 String methodName = getMethodName("set", mPropertyName);
1275 try {
1276 mJniSetter = nGetIntMethod(targetClass, methodName);
1277 } catch (NoSuchMethodError e) {
1278 // Couldn't find it via JNI - try reflection next. Probably means the method
1279 // doesn't exist, or the type is wrong. An error will be logged later if
1280 // reflection fails as well.
1281 }
1282 if (propertyMap == null) {
1283 propertyMap = new HashMap<String, Long>();
1284 sJNISetterPropertyMap.put(targetClass, propertyMap);
1285 }
1286 propertyMap.put(mPropertyName, mJniSetter);
1287 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001288 }
1289 if (mJniSetter == 0) {
1290 // Couldn't find method through fast JNI approach - just use reflection
1291 super.setupSetter(targetClass);
1292 }
1293 }
Chet Haase7c608f22010-10-22 17:54:04 -07001294 }
1295
1296 static class FloatPropertyValuesHolder extends PropertyValuesHolder {
1297
Chet Haaseb39f0512011-05-24 14:36:40 -07001298 // Cache JNI functions to avoid looking them up twice
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001299 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1300 new HashMap<Class, HashMap<String, Long>>();
1301 long mJniSetter;
Chet Haaseb39f0512011-05-24 14:36:40 -07001302 private FloatProperty mFloatProperty;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001303
George Mount984011f2014-08-21 14:28:01 -07001304 Keyframes.FloatKeyframes mFloatKeyframes;
Chet Haase7c608f22010-10-22 17:54:04 -07001305 float mFloatAnimatedValue;
1306
George Mount984011f2014-08-21 14:28:01 -07001307 public FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes) {
Chet Haase7c608f22010-10-22 17:54:04 -07001308 super(propertyName);
1309 mValueType = float.class;
George Mount984011f2014-08-21 14:28:01 -07001310 mKeyframes = keyframes;
1311 mFloatKeyframes = keyframes;
Chet Haase7c608f22010-10-22 17:54:04 -07001312 }
1313
George Mount984011f2014-08-21 14:28:01 -07001314 public FloatPropertyValuesHolder(Property property, Keyframes.FloatKeyframes keyframes) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001315 super(property);
1316 mValueType = float.class;
George Mount984011f2014-08-21 14:28:01 -07001317 mKeyframes = keyframes;
1318 mFloatKeyframes = keyframes;
Chet Haaseb39f0512011-05-24 14:36:40 -07001319 if (property instanceof FloatProperty) {
1320 mFloatProperty = (FloatProperty) mProperty;
1321 }
1322 }
1323
Chet Haase7c608f22010-10-22 17:54:04 -07001324 public FloatPropertyValuesHolder(String propertyName, float... values) {
1325 super(propertyName);
1326 setFloatValues(values);
Chet Haase691ac262010-11-03 22:38:32 -07001327 }
1328
Chet Haaseb39f0512011-05-24 14:36:40 -07001329 public FloatPropertyValuesHolder(Property property, float... values) {
1330 super(property);
1331 setFloatValues(values);
1332 if (property instanceof FloatProperty) {
1333 mFloatProperty = (FloatProperty) mProperty;
1334 }
1335 }
1336
Chet Haase691ac262010-11-03 22:38:32 -07001337 @Override
Doris Liu9f3b31b2016-05-19 19:04:47 -07001338 public void setProperty(Property property) {
1339 if (property instanceof FloatProperty) {
1340 mFloatProperty = (FloatProperty) property;
1341 } else {
1342 super.setProperty(property);
1343 }
1344 }
1345
1346 @Override
Chet Haase691ac262010-11-03 22:38:32 -07001347 public void setFloatValues(float... values) {
1348 super.setFloatValues(values);
George Mount984011f2014-08-21 14:28:01 -07001349 mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
Chet Haase7c608f22010-10-22 17:54:04 -07001350 }
1351
1352 @Override
1353 void calculateValue(float fraction) {
George Mount984011f2014-08-21 14:28:01 -07001354 mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
Chet Haase7c608f22010-10-22 17:54:04 -07001355 }
1356
1357 @Override
1358 Object getAnimatedValue() {
1359 return mFloatAnimatedValue;
1360 }
1361
1362 @Override
1363 public FloatPropertyValuesHolder clone() {
1364 FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
George Mount984011f2014-08-21 14:28:01 -07001365 newPVH.mFloatKeyframes = (Keyframes.FloatKeyframes) newPVH.mKeyframes;
Chet Haase7c608f22010-10-22 17:54:04 -07001366 return newPVH;
1367 }
1368
1369 /**
1370 * Internal function to set the value on the target object, using the setter set up
1371 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1372 * to handle turning the value calculated by ValueAnimator into a value set on the object
1373 * according to the name of the property.
1374 * @param target The target object on which the value is set
1375 */
1376 @Override
1377 void setAnimatedValue(Object target) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001378 if (mFloatProperty != null) {
1379 mFloatProperty.setValue(target, mFloatAnimatedValue);
1380 return;
1381 }
1382 if (mProperty != null) {
1383 mProperty.set(target, mFloatAnimatedValue);
1384 return;
1385 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001386 if (mJniSetter != 0) {
1387 nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
1388 return;
1389 }
Chet Haase7c608f22010-10-22 17:54:04 -07001390 if (mSetter != null) {
1391 try {
1392 mTmpValueArray[0] = mFloatAnimatedValue;
1393 mSetter.invoke(target, mTmpValueArray);
1394 } catch (InvocationTargetException e) {
1395 Log.e("PropertyValuesHolder", e.toString());
1396 } catch (IllegalAccessException e) {
1397 Log.e("PropertyValuesHolder", e.toString());
1398 }
1399 }
1400 }
1401
Chet Haase6e0ecb42010-11-03 19:41:18 -07001402 @Override
1403 void setupSetter(Class targetClass) {
Chet Haaseb39f0512011-05-24 14:36:40 -07001404 if (mProperty != null) {
1405 return;
1406 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001407 // Check new static hashmap<propName, int> for setter method
George Mount691487d2014-11-10 16:46:31 -08001408 synchronized (sJNISetterPropertyMap) {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001409 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
George Mount691487d2014-11-10 16:46:31 -08001410 boolean wasInMap = false;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001411 if (propertyMap != null) {
George Mount691487d2014-11-10 16:46:31 -08001412 wasInMap = propertyMap.containsKey(mPropertyName);
1413 if (wasInMap) {
1414 Long jniSetter = propertyMap.get(mPropertyName);
1415 if (jniSetter != null) {
1416 mJniSetter = jniSetter;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001417 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001418 }
1419 }
George Mount691487d2014-11-10 16:46:31 -08001420 if (!wasInMap) {
1421 String methodName = getMethodName("set", mPropertyName);
1422 try {
1423 mJniSetter = nGetFloatMethod(targetClass, methodName);
1424 } catch (NoSuchMethodError e) {
1425 // Couldn't find it via JNI - try reflection next. Probably means the method
1426 // doesn't exist, or the type is wrong. An error will be logged later if
1427 // reflection fails as well.
1428 }
1429 if (propertyMap == null) {
1430 propertyMap = new HashMap<String, Long>();
1431 sJNISetterPropertyMap.put(targetClass, propertyMap);
1432 }
1433 propertyMap.put(mPropertyName, mJniSetter);
1434 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001435 }
1436 if (mJniSetter == 0) {
1437 // Couldn't find method through fast JNI approach - just use reflection
1438 super.setupSetter(targetClass);
1439 }
1440 }
1441
Chet Haase7c608f22010-10-22 17:54:04 -07001442 }
Chet Haase6e0ecb42010-11-03 19:41:18 -07001443
George Mount4eed5292013-08-30 13:56:01 -07001444 static class MultiFloatValuesHolder extends PropertyValuesHolder {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001445 private long mJniSetter;
1446 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1447 new HashMap<Class, HashMap<String, Long>>();
George Mount4eed5292013-08-30 13:56:01 -07001448
1449 public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
1450 TypeEvaluator evaluator, Object... values) {
1451 super(propertyName);
1452 setConverter(converter);
1453 setObjectValues(values);
1454 setEvaluator(evaluator);
1455 }
1456
1457 public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
George Mount984011f2014-08-21 14:28:01 -07001458 TypeEvaluator evaluator, Keyframes keyframes) {
George Mount4eed5292013-08-30 13:56:01 -07001459 super(propertyName);
1460 setConverter(converter);
George Mount984011f2014-08-21 14:28:01 -07001461 mKeyframes = keyframes;
George Mount4eed5292013-08-30 13:56:01 -07001462 setEvaluator(evaluator);
1463 }
1464
1465 /**
1466 * Internal function to set the value on the target object, using the setter set up
1467 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1468 * to handle turning the value calculated by ValueAnimator into a value set on the object
1469 * according to the name of the property.
1470 *
1471 * @param target The target object on which the value is set
1472 */
1473 @Override
1474 void setAnimatedValue(Object target) {
1475 float[] values = (float[]) getAnimatedValue();
1476 int numParameters = values.length;
1477 if (mJniSetter != 0) {
1478 switch (numParameters) {
1479 case 1:
1480 nCallFloatMethod(target, mJniSetter, values[0]);
1481 break;
1482 case 2:
1483 nCallTwoFloatMethod(target, mJniSetter, values[0], values[1]);
1484 break;
1485 case 4:
1486 nCallFourFloatMethod(target, mJniSetter, values[0], values[1],
1487 values[2], values[3]);
1488 break;
1489 default: {
1490 nCallMultipleFloatMethod(target, mJniSetter, values);
1491 break;
1492 }
1493 }
1494 }
1495 }
1496
1497 /**
1498 * Internal function (called from ObjectAnimator) to set up the setter and getter
1499 * prior to running the animation. No getter can be used for multiple parameters.
1500 *
1501 * @param target The object on which the setter exists.
1502 */
1503 @Override
1504 void setupSetterAndGetter(Object target) {
1505 setupSetter(target.getClass());
1506 }
1507
1508 @Override
1509 void setupSetter(Class targetClass) {
1510 if (mJniSetter != 0) {
1511 return;
1512 }
George Mount691487d2014-11-10 16:46:31 -08001513 synchronized(sJNISetterPropertyMap) {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001514 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
George Mount691487d2014-11-10 16:46:31 -08001515 boolean wasInMap = false;
George Mount4eed5292013-08-30 13:56:01 -07001516 if (propertyMap != null) {
George Mount691487d2014-11-10 16:46:31 -08001517 wasInMap = propertyMap.containsKey(mPropertyName);
1518 if (wasInMap) {
1519 Long jniSetter = propertyMap.get(mPropertyName);
1520 if (jniSetter != null) {
1521 mJniSetter = jniSetter;
1522 }
George Mount4eed5292013-08-30 13:56:01 -07001523 }
1524 }
George Mount691487d2014-11-10 16:46:31 -08001525 if (!wasInMap) {
George Mount4eed5292013-08-30 13:56:01 -07001526 String methodName = getMethodName("set", mPropertyName);
1527 calculateValue(0f);
1528 float[] values = (float[]) getAnimatedValue();
1529 int numParams = values.length;
1530 try {
1531 mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams);
1532 } catch (NoSuchMethodError e) {
1533 // try without the 'set' prefix
George Mount691487d2014-11-10 16:46:31 -08001534 try {
1535 mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName,
1536 numParams);
1537 } catch (NoSuchMethodError e2) {
1538 // just try reflection next
George Mount4eed5292013-08-30 13:56:01 -07001539 }
George Mount4eed5292013-08-30 13:56:01 -07001540 }
George Mount691487d2014-11-10 16:46:31 -08001541 if (propertyMap == null) {
1542 propertyMap = new HashMap<String, Long>();
1543 sJNISetterPropertyMap.put(targetClass, propertyMap);
1544 }
1545 propertyMap.put(mPropertyName, mJniSetter);
George Mount4eed5292013-08-30 13:56:01 -07001546 }
Doris Liu9f3b31b2016-05-19 19:04:47 -07001547 }
George Mount4eed5292013-08-30 13:56:01 -07001548 }
1549 }
1550
1551 static class MultiIntValuesHolder extends PropertyValuesHolder {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001552 private long mJniSetter;
1553 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1554 new HashMap<Class, HashMap<String, Long>>();
George Mount4eed5292013-08-30 13:56:01 -07001555
1556 public MultiIntValuesHolder(String propertyName, TypeConverter converter,
1557 TypeEvaluator evaluator, Object... values) {
1558 super(propertyName);
1559 setConverter(converter);
1560 setObjectValues(values);
1561 setEvaluator(evaluator);
1562 }
1563
1564 public MultiIntValuesHolder(String propertyName, TypeConverter converter,
George Mount984011f2014-08-21 14:28:01 -07001565 TypeEvaluator evaluator, Keyframes keyframes) {
George Mount4eed5292013-08-30 13:56:01 -07001566 super(propertyName);
1567 setConverter(converter);
George Mount984011f2014-08-21 14:28:01 -07001568 mKeyframes = keyframes;
George Mount4eed5292013-08-30 13:56:01 -07001569 setEvaluator(evaluator);
1570 }
1571
1572 /**
1573 * Internal function to set the value on the target object, using the setter set up
1574 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1575 * to handle turning the value calculated by ValueAnimator into a value set on the object
1576 * according to the name of the property.
1577 *
1578 * @param target The target object on which the value is set
1579 */
1580 @Override
1581 void setAnimatedValue(Object target) {
1582 int[] values = (int[]) getAnimatedValue();
1583 int numParameters = values.length;
1584 if (mJniSetter != 0) {
1585 switch (numParameters) {
1586 case 1:
1587 nCallIntMethod(target, mJniSetter, values[0]);
1588 break;
1589 case 2:
1590 nCallTwoIntMethod(target, mJniSetter, values[0], values[1]);
1591 break;
1592 case 4:
1593 nCallFourIntMethod(target, mJniSetter, values[0], values[1],
1594 values[2], values[3]);
1595 break;
1596 default: {
1597 nCallMultipleIntMethod(target, mJniSetter, values);
1598 break;
1599 }
1600 }
1601 }
1602 }
1603
1604 /**
1605 * Internal function (called from ObjectAnimator) to set up the setter and getter
1606 * prior to running the animation. No getter can be used for multiple parameters.
1607 *
1608 * @param target The object on which the setter exists.
1609 */
1610 @Override
1611 void setupSetterAndGetter(Object target) {
1612 setupSetter(target.getClass());
1613 }
1614
1615 @Override
1616 void setupSetter(Class targetClass) {
1617 if (mJniSetter != 0) {
1618 return;
1619 }
George Mount691487d2014-11-10 16:46:31 -08001620 synchronized(sJNISetterPropertyMap) {
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001621 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
George Mount691487d2014-11-10 16:46:31 -08001622 boolean wasInMap = false;
George Mount4eed5292013-08-30 13:56:01 -07001623 if (propertyMap != null) {
George Mount691487d2014-11-10 16:46:31 -08001624 wasInMap = propertyMap.containsKey(mPropertyName);
1625 if (wasInMap) {
1626 Long jniSetter = propertyMap.get(mPropertyName);
1627 if (jniSetter != null) {
1628 mJniSetter = jniSetter;
1629 }
George Mount4eed5292013-08-30 13:56:01 -07001630 }
1631 }
George Mount691487d2014-11-10 16:46:31 -08001632 if (!wasInMap) {
George Mount4eed5292013-08-30 13:56:01 -07001633 String methodName = getMethodName("set", mPropertyName);
1634 calculateValue(0f);
1635 int[] values = (int[]) getAnimatedValue();
1636 int numParams = values.length;
1637 try {
1638 mJniSetter = nGetMultipleIntMethod(targetClass, methodName, numParams);
1639 } catch (NoSuchMethodError e) {
1640 // try without the 'set' prefix
George Mount691487d2014-11-10 16:46:31 -08001641 try {
1642 mJniSetter = nGetMultipleIntMethod(targetClass, mPropertyName,
1643 numParams);
1644 } catch (NoSuchMethodError e2) {
1645 // couldn't find it.
George Mount4eed5292013-08-30 13:56:01 -07001646 }
George Mount4eed5292013-08-30 13:56:01 -07001647 }
George Mount691487d2014-11-10 16:46:31 -08001648 if (propertyMap == null) {
1649 propertyMap = new HashMap<String, Long>();
1650 sJNISetterPropertyMap.put(targetClass, propertyMap);
1651 }
1652 propertyMap.put(mPropertyName, mJniSetter);
George Mount4eed5292013-08-30 13:56:01 -07001653 }
George Mount4eed5292013-08-30 13:56:01 -07001654 }
1655 }
1656 }
1657
George Mountc96c7b22013-08-23 13:31:31 -07001658 /**
1659 * Convert from PointF to float[] for multi-float setters along a Path.
1660 */
1661 private static class PointFToFloatArray extends TypeConverter<PointF, float[]> {
1662 private float[] mCoordinates = new float[2];
1663
1664 public PointFToFloatArray() {
1665 super(PointF.class, float[].class);
1666 }
1667
1668 @Override
1669 public float[] convert(PointF value) {
1670 mCoordinates[0] = value.x;
1671 mCoordinates[1] = value.y;
1672 return mCoordinates;
1673 }
1674 };
1675
1676 /**
1677 * Convert from PointF to int[] for multi-int setters along a Path.
1678 */
1679 private static class PointFToIntArray extends TypeConverter<PointF, int[]> {
1680 private int[] mCoordinates = new int[2];
1681
1682 public PointFToIntArray() {
1683 super(PointF.class, int[].class);
1684 }
1685
1686 @Override
1687 public int[] convert(PointF value) {
1688 mCoordinates[0] = Math.round(value.x);
1689 mCoordinates[1] = Math.round(value.y);
1690 return mCoordinates;
1691 }
1692 };
1693
Doris Liu766431a2016-02-04 22:17:11 +00001694 /**
1695 * @hide
1696 */
1697 public static class PropertyValues {
1698 public String propertyName;
1699 public Class type;
1700 public Object startValue;
1701 public Object endValue;
1702 public DataSource dataSource = null;
1703 public interface DataSource {
1704 Object getValueAtFraction(float fraction);
1705 }
1706 public String toString() {
1707 return ("property name: " + propertyName + ", type: " + type + ", startValue: "
1708 + startValue.toString() + ", endValue: " + endValue.toString());
1709 }
1710 }
1711
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001712 native static private long nGetIntMethod(Class targetClass, String methodName);
1713 native static private long nGetFloatMethod(Class targetClass, String methodName);
1714 native static private long nGetMultipleIntMethod(Class targetClass, String methodName,
George Mount4eed5292013-08-30 13:56:01 -07001715 int numParams);
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001716 native static private long nGetMultipleFloatMethod(Class targetClass, String methodName,
George Mount4eed5292013-08-30 13:56:01 -07001717 int numParams);
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001718 native static private void nCallIntMethod(Object target, long methodID, int arg);
1719 native static private void nCallFloatMethod(Object target, long methodID, float arg);
1720 native static private void nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2);
1721 native static private void nCallFourIntMethod(Object target, long methodID, int arg1, int arg2,
George Mount4eed5292013-08-30 13:56:01 -07001722 int arg3, int arg4);
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001723 native static private void nCallMultipleIntMethod(Object target, long methodID, int[] args);
1724 native static private void nCallTwoFloatMethod(Object target, long methodID, float arg1,
George Mount4eed5292013-08-30 13:56:01 -07001725 float arg2);
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001726 native static private void nCallFourFloatMethod(Object target, long methodID, float arg1,
George Mount4eed5292013-08-30 13:56:01 -07001727 float arg2, float arg3, float arg4);
Ashok Bhatfbb35fb2014-01-17 16:44:27 +00001728 native static private void nCallMultipleFloatMethod(Object target, long methodID, float[] args);
George Mountc96c7b22013-08-23 13:31:31 -07001729}