1 /* 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 17 package android.animation; 18 19 import android.graphics.Path; 20 import android.graphics.PointF; 21 import android.util.FloatProperty; 22 import android.util.IntProperty; 23 import android.util.Log; 24 import android.util.PathParser; 25 import android.util.Property; 26 27 import java.lang.reflect.InvocationTargetException; 28 import java.lang.reflect.Method; 29 import java.util.HashMap; 30 import java.util.List; 31 32 /** 33 * 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 35 * animations with ValueAnimator or ObjectAnimator that operate on several different properties 36 * in parallel. 37 */ 38 public class PropertyValuesHolder implements Cloneable { 39 40 /** 41 * The name of the property associated with the values. This need not be a real property, 42 * 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. 44 */ 45 String mPropertyName; 46 47 /** 48 * @hide 49 */ 50 protected Property mProperty; 51 52 /** 53 * The setter function, if needed. ObjectAnimator hands off this functionality to 54 * PropertyValuesHolder, since it holds all of the per-property information. This 55 * property is automatically 56 * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator. 57 */ 58 Method mSetter = null; 59 60 /** 61 * The getter function, if needed. ObjectAnimator hands off this functionality to 62 * PropertyValuesHolder, since it holds all of the per-property information. This 63 * property is automatically 64 * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator. 65 * 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 */ 73 Class mValueType; 74 75 /** 76 * The set of keyframes (time/value pairs) that define this animation. 77 */ 78 Keyframes mKeyframes = null; 79 80 81 // type evaluators for the primitive types handled by this implementation 82 private static final TypeEvaluator sIntEvaluator = new IntEvaluator(); 83 private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator(); 84 85 // We try several different types when searching for appropriate setter/getter functions. 86 // The caller may have supplied values in a type that does not match the setter/getter 87 // functions (such as the integers 0 and 1 to represent floating point values for alpha). 88 // Also, the use of generics in constructors means that we end up with the Object versions 89 // of primitive types (Float vs. float). But most likely, the setter/getter functions 90 // will take primitive types instead. 91 // So we supply an ordered array of other types to try before giving up. 92 private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class, 93 Double.class, Integer.class}; 94 private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class, 95 Float.class, Double.class}; 96 private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class, 97 Float.class, Integer.class}; 98 99 // These maps hold all property entries for a particular class. This map 100 // is used to speed up property/setter/getter lookups for a given class/property 101 // combination. No need to use reflection on the combination more than once. 102 private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap = 103 new HashMap<Class, HashMap<String, Method>>(); 104 private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap = 105 new HashMap<Class, HashMap<String, Method>>(); 106 107 // Used to pass single value to varargs parameter in setter invocation 108 final Object[] mTmpValueArray = new Object[1]; 109 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, 113 * but the system only knows about the primitive types int and float. Any other 114 * 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 120 * that function and might be retrieved later either by ValueAnimator.animatedValue() or 121 * by the property-setting logic in ObjectAnimator.animatedValue(). 122 */ 123 private Object mAnimatedValue; 124 125 /** 126 * Converts from the source Object type to the setter Object type. 127 */ 128 private TypeConverter mConverter; 129 130 /** 131 * Internal utility constructor, used by the factory methods to set the property name. 132 * @param propertyName The name of the property for this holder. 133 */ PropertyValuesHolder(String propertyName)134 private PropertyValuesHolder(String propertyName) { 135 mPropertyName = propertyName; 136 } 137 138 /** 139 * Internal utility constructor, used by the factory methods to set the property. 140 * @param property The property for this holder. 141 */ PropertyValuesHolder(Property property)142 private PropertyValuesHolder(Property property) { 143 mProperty = property; 144 if (property != null) { 145 mPropertyName = property.getName(); 146 } 147 } 148 149 /** 150 * 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 */ ofInt(String propertyName, int... values)156 public static PropertyValuesHolder ofInt(String propertyName, int... values) { 157 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 */ ofInt(Property<?, Integer> property, int... values)167 public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) { 168 return new IntPropertyValuesHolder(property, values); 169 } 170 171 /** 172 * Constructs and returns a PropertyValuesHolder with a given property name and 173 * 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 * 178 * @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. 180 * @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 */ ofMultiInt(String propertyName, int[][] values)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 /** 206 * 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 */ ofMultiInt(String propertyName, Path path)217 public static PropertyValuesHolder ofMultiInt(String propertyName, Path path) { 218 Keyframes keyframes = KeyframeSet.ofPath(path); 219 PointFToIntArray converter = new PointFToIntArray(); 220 return new MultiIntValuesHolder(propertyName, converter, null, keyframes); 221 } 222 223 /** 224 * 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 @SafeVarargs ofMultiInt(String propertyName, TypeConverter<V, int[]> converter, TypeEvaluator<V> evaluator, V... values)240 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 */ ofMultiInt(String propertyName, TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, Keyframe... values)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 270 * 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 */ ofFloat(String propertyName, float... values)275 public static PropertyValuesHolder ofFloat(String propertyName, float... values) { 276 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 */ ofFloat(Property<?, Float> property, float... values)286 public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) { 287 return new FloatPropertyValuesHolder(property, values); 288 } 289 290 /** 291 * Constructs and returns a PropertyValuesHolder with a given property name and 292 * 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 * 297 * @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. 299 * @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 */ ofMultiFloat(String propertyName, float[][] values)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 /** 325 * 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 */ ofMultiFloat(String propertyName, Path path)336 public static PropertyValuesHolder ofMultiFloat(String propertyName, Path path) { 337 Keyframes keyframes = KeyframeSet.ofPath(path); 338 PointFToFloatArray converter = new PointFToFloatArray(); 339 return new MultiFloatValuesHolder(propertyName, converter, null, keyframes); 340 } 341 342 /** 343 * 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 */ 357 @SafeVarargs ofMultiFloat(String propertyName, TypeConverter<V, float[]> converter, TypeEvaluator<V> evaluator, V... values)358 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 */ ofMultiFloat(String propertyName, TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, Keyframe... values)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 388 * set of Object values. This variant also takes a TypeEvaluator because the system 389 * cannot automatically interpolate between objects of unknown type. 390 * 391 * <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 * 396 * @param propertyName The name of the property being animated. 397 * @param evaluator A TypeEvaluator that will be called on each animation frame to 398 * provide the necessary interpolation between the Object values to derive the animated 399 * value. 400 * @param values The values that the named property will animate between. 401 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 402 */ ofObject(String propertyName, TypeEvaluator evaluator, Object... values)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 /** 412 * 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 * 417 * <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 * 421 * @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 */ ofObject(String propertyName, TypeConverter<PointF, ?> converter, Path path)427 public static PropertyValuesHolder ofObject(String propertyName, 428 TypeConverter<PointF, ?> converter, Path path) { 429 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); 430 pvh.mKeyframes = KeyframeSet.ofPath(path); 431 pvh.mValueType = PointF.class; 432 pvh.setConverter(converter); 433 return pvh; 434 } 435 436 /** 437 * 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 * 441 * <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 * 446 * @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 */ 453 @SafeVarargs ofObject(Property property, TypeEvaluator<V> evaluator, V... values)454 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 /** 463 * 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> 468 * must be a {@link android.animation.BidirectionalTypeConverter} to retrieve the current 469 * value. 470 * 471 * <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 * 476 * @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 */ 486 @SafeVarargs ofObject(Property<?, V> property, TypeConverter<T, V> converter, TypeEvaluator<T> evaluator, T... values)487 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 /** 497 * 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 * 502 * <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 * 506 * @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 */ ofObject(Property<?, V> property, TypeConverter<PointF, V> converter, Path path)512 public static <V> PropertyValuesHolder ofObject(Property<?, V> property, 513 TypeConverter<PointF, V> converter, Path path) { 514 PropertyValuesHolder pvh = new PropertyValuesHolder(property); 515 pvh.mKeyframes = KeyframeSet.ofPath(path); 516 pvh.mValueType = PointF.class; 517 pvh.setConverter(converter); 518 return pvh; 519 } 520 521 /** 522 * 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 524 * 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 531 * {@link ObjectAnimator}, and with a getter function 532 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has 533 * no way of determining what the value should be. 534 * @param propertyName The name of the property associated with this set of values. This 535 * can be the actual property name to be used when using a ObjectAnimator object, or 536 * just a name used to get animated values, such as if this object is used with an 537 * ValueAnimator object. 538 * @param values The set of values to animate between. 539 */ ofKeyframe(String propertyName, Keyframe... values)540 public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) { 541 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); 542 return ofKeyframes(propertyName, keyframeSet); 543 } 544 545 /** 546 * 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 */ ofKeyframe(Property property, Keyframe... values)561 public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) { 562 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); 563 return ofKeyframes(property, keyframeSet); 564 } 565 ofKeyframes(String propertyName, Keyframes keyframes)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; 577 } 578 } 579 ofKeyframes(Property property, Keyframes keyframes)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 { 586 PropertyValuesHolder pvh = new PropertyValuesHolder(property); 587 pvh.mKeyframes = keyframes; 588 pvh.mValueType = keyframes.getType(); 589 return pvh; 590 } 591 } 592 593 /** 594 * Set the animated values for this object to this set of ints. 595 * 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 600 * {@link ObjectAnimator}, and with a getter function 601 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has 602 * no way of determining what the value should be. 603 * 604 * @param values One or more values that the animation will animate between. 605 */ setIntValues(int... values)606 public void setIntValues(int... values) { 607 mValueType = int.class; 608 mKeyframes = KeyframeSet.ofInt(values); 609 } 610 611 /** 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 618 * {@link ObjectAnimator}, and with a getter function 619 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has 620 * no way of determining what the value should be. 621 * 622 * @param values One or more values that the animation will animate between. 623 */ setFloatValues(float... values)624 public void setFloatValues(float... values) { 625 mValueType = float.class; 626 mKeyframes = KeyframeSet.ofFloat(values); 627 } 628 629 /** 630 * 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 */ setKeyframes(Keyframe... values)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]; 640 } 641 mKeyframes = new KeyframeSet(keyframes); 642 } 643 644 /** 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 651 * {@link ObjectAnimator}, and with a getter function 652 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has 653 * no way of determining what the value should be. 654 * 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 * 660 * @param values One or more values that the animation will animate between. 661 */ setObjectValues(Object... values)662 public void setObjectValues(Object... values) { 663 mValueType = values[0].getClass(); 664 mKeyframes = KeyframeSet.ofObject(values); 665 if (mEvaluator != null) { 666 mKeyframes.setEvaluator(mEvaluator); 667 } 668 } 669 670 /** 671 * Sets the converter to convert from the values type to the setter's parameter type. 672 * If only one value is supplied, <var>converter</var> must be a 673 * {@link android.animation.BidirectionalTypeConverter}. 674 * @param converter The converter to use to convert values. 675 */ setConverter(TypeConverter converter)676 public void setConverter(TypeConverter converter) { 677 mConverter = converter; 678 } 679 680 /** 681 * 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 */ getPropertyFunction(Class targetClass, String prefix, Class valueType)695 private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) { 696 // TODO: faster implementation... 697 Method returnVal = null; 698 String methodName = getMethodName(prefix, mPropertyName); 699 Class args[] = null; 700 if (valueType == null) { 701 try { 702 returnVal = targetClass.getMethod(methodName, args); 703 } catch (NoSuchMethodException e) { 704 // Swallow the error, log it later 705 } 706 } else { 707 args = new Class[1]; 708 Class typeVariants[]; 709 if (valueType.equals(Float.class)) { 710 typeVariants = FLOAT_VARIANTS; 711 } else if (valueType.equals(Integer.class)) { 712 typeVariants = INTEGER_VARIANTS; 713 } else if (valueType.equals(Double.class)) { 714 typeVariants = DOUBLE_VARIANTS; 715 } else { 716 typeVariants = new Class[1]; 717 typeVariants[0] = valueType; 718 } 719 for (Class typeVariant : typeVariants) { 720 args[0] = typeVariant; 721 try { 722 returnVal = targetClass.getMethod(methodName, args); 723 if (mConverter == null) { 724 // change the value type to suit 725 mValueType = typeVariant; 726 } 727 return returnVal; 728 } catch (NoSuchMethodException e) { 729 // Swallow the error and keep trying other variants 730 } 731 } 732 // If we got here, then no appropriate function was found 733 } 734 735 if (returnVal == null) { 736 Log.w("PropertyValuesHolder", "Method " + 737 getMethodName(prefix, mPropertyName) + "() with type " + valueType + 738 " not found on target class " + targetClass); 739 } 740 741 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 */ setupSetterOrGetter(Class targetClass, HashMap<Class, HashMap<String, Method>> propertyMapMap, String prefix, Class valueType)755 private Method setupSetterOrGetter(Class targetClass, 756 HashMap<Class, HashMap<String, Method>> propertyMapMap, 757 String prefix, Class valueType) { 758 Method setterOrGetter = null; 759 synchronized(propertyMapMap) { 760 // 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 763 HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass); 764 boolean wasInMap = false; 765 if (propertyMap != null) { 766 wasInMap = propertyMap.containsKey(mPropertyName); 767 if (wasInMap) { 768 setterOrGetter = propertyMap.get(mPropertyName); 769 } 770 } 771 if (!wasInMap) { 772 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 } 779 } 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 */ setupSetter(Class targetClass)787 void setupSetter(Class targetClass) { 788 Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType(); 789 mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType); 790 } 791 792 /** 793 * Utility function to get the getter from targetClass 794 */ setupGetter(Class targetClass)795 private void setupGetter(Class targetClass) { 796 mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null); 797 } 798 799 /** 800 * Internal function (called from ObjectAnimator) to set up the setter and getter 801 * 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 */ setupSetterAndGetter(Object target)809 void setupSetterAndGetter(Object target) { 810 mKeyframes.invalidateCache(); 811 if (mProperty != null) { 812 // check to make sure that mProperty is on the class of target 813 try { 814 Object testValue = null; 815 List<Keyframe> keyframes = mKeyframes.getKeyframes(); 816 int keyframeCount = keyframes == null ? 0 : keyframes.size(); 817 for (int i = 0; i < keyframeCount; i++) { 818 Keyframe kf = keyframes.get(i); 819 if (!kf.hasValue() || kf.valueWasSetOnStart()) { 820 if (testValue == null) { 821 testValue = convertBack(mProperty.get(target)); 822 } 823 kf.setValue(testValue); 824 kf.setValueWasSetOnStart(true); 825 } 826 } 827 return; 828 } catch (ClassCastException e) { 829 Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() + 830 ") on target object " + target + ". Trying reflection instead"); 831 mProperty = null; 832 } 833 } 834 // We can't just say 'else' here because the catch statement sets mProperty to null. 835 if (mProperty == null) { 836 Class targetClass = target.getClass(); 837 if (mSetter == null) { 838 setupSetter(targetClass); 839 } 840 List<Keyframe> keyframes = mKeyframes.getKeyframes(); 841 int keyframeCount = keyframes == null ? 0 : keyframes.size(); 842 for (int i = 0; i < keyframeCount; i++) { 843 Keyframe kf = keyframes.get(i); 844 if (!kf.hasValue() || kf.valueWasSetOnStart()) { 845 if (mGetter == null) { 846 setupGetter(targetClass); 847 if (mGetter == null) { 848 // Already logged the error - just return to avoid NPE 849 return; 850 } 851 } 852 try { 853 Object value = convertBack(mGetter.invoke(target)); 854 kf.setValue(value); 855 kf.setValueWasSetOnStart(true); 856 } catch (InvocationTargetException e) { 857 Log.e("PropertyValuesHolder", e.toString()); 858 } catch (IllegalAccessException e) { 859 Log.e("PropertyValuesHolder", e.toString()); 860 } 861 } 862 } 863 } 864 } 865 convertBack(Object value)866 private Object convertBack(Object value) { 867 if (mConverter != null) { 868 if (!(mConverter instanceof BidirectionalTypeConverter)) { 869 throw new IllegalArgumentException("Converter " 870 + mConverter.getClass().getName() 871 + " must be a BidirectionalTypeConverter"); 872 } 873 value = ((BidirectionalTypeConverter) mConverter).convertBack(value); 874 } 875 return value; 876 } 877 878 /** 879 * Utility function to set the value stored in a particular Keyframe. The value used is 880 * whatever the value is for the property name specified in the keyframe on the target object. 881 * 882 * @param target The target object from which the current value should be extracted. 883 * @param kf The keyframe which holds the property name and value. 884 */ setupValue(Object target, Keyframe kf)885 private void setupValue(Object target, Keyframe kf) { 886 if (mProperty != null) { 887 Object value = convertBack(mProperty.get(target)); 888 kf.setValue(value); 889 } else { 890 try { 891 if (mGetter == null) { 892 Class targetClass = target.getClass(); 893 setupGetter(targetClass); 894 if (mGetter == null) { 895 // Already logged the error - just return to avoid NPE 896 return; 897 } 898 } 899 Object value = convertBack(mGetter.invoke(target)); 900 kf.setValue(value); 901 } catch (InvocationTargetException e) { 902 Log.e("PropertyValuesHolder", e.toString()); 903 } catch (IllegalAccessException e) { 904 Log.e("PropertyValuesHolder", e.toString()); 905 } 906 } 907 } 908 909 /** 910 * This function is called by ObjectAnimator when setting the start values for an animation. 911 * The start values are set according to the current values in the target object. The 912 * property whose value is extracted is whatever is specified by the propertyName of this 913 * PropertyValuesHolder object. 914 * 915 * @param target The object which holds the start values that should be set. 916 */ setupStartValue(Object target)917 void setupStartValue(Object target) { 918 List<Keyframe> keyframes = mKeyframes.getKeyframes(); 919 if (!keyframes.isEmpty()) { 920 setupValue(target, keyframes.get(0)); 921 } 922 } 923 924 /** 925 * This function is called by ObjectAnimator when setting the end values for an animation. 926 * The end values are set according to the current values in the target object. The 927 * property whose value is extracted is whatever is specified by the propertyName of this 928 * PropertyValuesHolder object. 929 * 930 * @param target The object which holds the start values that should be set. 931 */ setupEndValue(Object target)932 void setupEndValue(Object target) { 933 List<Keyframe> keyframes = mKeyframes.getKeyframes(); 934 if (!keyframes.isEmpty()) { 935 setupValue(target, keyframes.get(keyframes.size() - 1)); 936 } 937 } 938 939 @Override clone()940 public PropertyValuesHolder clone() { 941 try { 942 PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone(); 943 newPVH.mPropertyName = mPropertyName; 944 newPVH.mProperty = mProperty; 945 newPVH.mKeyframes = mKeyframes.clone(); 946 newPVH.mEvaluator = mEvaluator; 947 return newPVH; 948 } catch (CloneNotSupportedException e) { 949 // won't reach here 950 return null; 951 } 952 } 953 954 /** 955 * Internal function to set the value on the target object, using the setter set up 956 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 957 * to handle turning the value calculated by ValueAnimator into a value set on the object 958 * according to the name of the property. 959 * @param target The target object on which the value is set 960 */ setAnimatedValue(Object target)961 void setAnimatedValue(Object target) { 962 if (mProperty != null) { 963 mProperty.set(target, getAnimatedValue()); 964 } 965 if (mSetter != null) { 966 try { 967 mTmpValueArray[0] = getAnimatedValue(); 968 mSetter.invoke(target, mTmpValueArray); 969 } catch (InvocationTargetException e) { 970 Log.e("PropertyValuesHolder", e.toString()); 971 } catch (IllegalAccessException e) { 972 Log.e("PropertyValuesHolder", e.toString()); 973 } 974 } 975 } 976 977 /** 978 * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used 979 * to calculate animated values. 980 */ init()981 void init() { 982 if (mEvaluator == null) { 983 // We already handle int and float automatically, but not their Object 984 // equivalents 985 mEvaluator = (mValueType == Integer.class) ? sIntEvaluator : 986 (mValueType == Float.class) ? sFloatEvaluator : 987 null; 988 } 989 if (mEvaluator != null) { 990 // KeyframeSet knows how to evaluate the common types - only give it a custom 991 // evaluator if one has been set on this class 992 mKeyframes.setEvaluator(mEvaluator); 993 } 994 } 995 996 /** 997 * The TypeEvaluator will be automatically determined based on the type of values 998 * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so 999 * desired. This may be important in cases where either the type of the values supplied 1000 * do not match the way that they should be interpolated between, or if the values 1001 * are of a custom type or one not currently understood by the animation system. Currently, 1002 * only values of type float and int (and their Object equivalents: Float 1003 * and Integer) are correctly interpolated; all other types require setting a TypeEvaluator. 1004 * @param evaluator 1005 */ setEvaluator(TypeEvaluator evaluator)1006 public void setEvaluator(TypeEvaluator evaluator) { 1007 mEvaluator = evaluator; 1008 mKeyframes.setEvaluator(evaluator); 1009 } 1010 1011 /** 1012 * Function used to calculate the value according to the evaluator set up for 1013 * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue(). 1014 * 1015 * @param fraction The elapsed, interpolated fraction of the animation. 1016 */ calculateValue(float fraction)1017 void calculateValue(float fraction) { 1018 Object value = mKeyframes.getValue(fraction); 1019 mAnimatedValue = mConverter == null ? value : mConverter.convert(value); 1020 } 1021 1022 /** 1023 * Sets the name of the property that will be animated. This name is used to derive 1024 * a setter function that will be called to set animated values. 1025 * For example, a property name of <code>foo</code> will result 1026 * in a call to the function <code>setFoo()</code> on the target object. If either 1027 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 1028 * also be derived and called. 1029 * 1030 * <p>Note that the setter function derived from this property name 1031 * must take the same parameter type as the 1032 * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to 1033 * the setter function will fail.</p> 1034 * 1035 * @param propertyName The name of the property being animated. 1036 */ setPropertyName(String propertyName)1037 public void setPropertyName(String propertyName) { 1038 mPropertyName = propertyName; 1039 } 1040 1041 /** 1042 * Sets the property that will be animated. 1043 * 1044 * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property 1045 * must exist on the target object specified in that ObjectAnimator.</p> 1046 * 1047 * @param property The property being animated. 1048 */ setProperty(Property property)1049 public void setProperty(Property property) { 1050 mProperty = property; 1051 } 1052 1053 /** 1054 * Gets the name of the property that will be animated. This name will be used to derive 1055 * a setter function that will be called to set animated values. 1056 * For example, a property name of <code>foo</code> will result 1057 * in a call to the function <code>setFoo()</code> on the target object. If either 1058 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 1059 * also be derived and called. 1060 */ getPropertyName()1061 public String getPropertyName() { 1062 return mPropertyName; 1063 } 1064 1065 /** 1066 * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value 1067 * most recently calculated in calculateValue(). 1068 * @return 1069 */ getAnimatedValue()1070 Object getAnimatedValue() { 1071 return mAnimatedValue; 1072 } 1073 1074 /** 1075 * PropertyValuesHolder is Animators use to hold internal animation related data. 1076 * Therefore, in order to replicate the animation behavior, we need to get data out of 1077 * PropertyValuesHolder. 1078 * @hide 1079 */ getPropertyValues(PropertyValues values)1080 public void getPropertyValues(PropertyValues values) { 1081 init(); 1082 values.propertyName = mPropertyName; 1083 values.type = mValueType; 1084 values.startValue = mKeyframes.getValue(0); 1085 if (values.startValue instanceof PathParser.PathData) { 1086 // PathData evaluator returns the same mutable PathData object when query fraction, 1087 // so we have to make a copy here. 1088 values.startValue = new PathParser.PathData((PathParser.PathData) values.startValue); 1089 } 1090 values.endValue = mKeyframes.getValue(1); 1091 if (values.endValue instanceof PathParser.PathData) { 1092 // PathData evaluator returns the same mutable PathData object when query fraction, 1093 // so we have to make a copy here. 1094 values.endValue = new PathParser.PathData((PathParser.PathData) values.endValue); 1095 } 1096 // TODO: We need a better way to get data out of keyframes. 1097 if (mKeyframes instanceof PathKeyframes.FloatKeyframesBase 1098 || mKeyframes instanceof PathKeyframes.IntKeyframesBase) { 1099 // property values will animate based on external data source (e.g. Path) 1100 values.dataSource = new PropertyValues.DataSource() { 1101 @Override 1102 public Object getValueAtFraction(float fraction) { 1103 return mKeyframes.getValue(fraction); 1104 } 1105 }; 1106 } else { 1107 values.dataSource = null; 1108 } 1109 } 1110 1111 @Override toString()1112 public String toString() { 1113 return mPropertyName + ": " + mKeyframes.toString(); 1114 } 1115 1116 /** 1117 * Utility method to derive a setter/getter method name from a property name, where the 1118 * prefix is typically "set" or "get" and the first letter of the property name is 1119 * capitalized. 1120 * 1121 * @param prefix The precursor to the method name, before the property name begins, typically 1122 * "set" or "get". 1123 * @param propertyName The name of the property that represents the bulk of the method name 1124 * after the prefix. The first letter of this word will be capitalized in the resulting 1125 * method name. 1126 * @return String the property name converted to a method name according to the conventions 1127 * specified above. 1128 */ getMethodName(String prefix, String propertyName)1129 static String getMethodName(String prefix, String propertyName) { 1130 if (propertyName == null || propertyName.length() == 0) { 1131 // shouldn't get here 1132 return prefix; 1133 } 1134 char firstLetter = Character.toUpperCase(propertyName.charAt(0)); 1135 String theRest = propertyName.substring(1); 1136 return prefix + firstLetter + theRest; 1137 } 1138 1139 static class IntPropertyValuesHolder extends PropertyValuesHolder { 1140 1141 // Cache JNI functions to avoid looking them up twice 1142 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap = 1143 new HashMap<Class, HashMap<String, Long>>(); 1144 long mJniSetter; 1145 private IntProperty mIntProperty; 1146 1147 Keyframes.IntKeyframes mIntKeyframes; 1148 int mIntAnimatedValue; 1149 IntPropertyValuesHolder(String propertyName, Keyframes.IntKeyframes keyframes)1150 public IntPropertyValuesHolder(String propertyName, Keyframes.IntKeyframes keyframes) { 1151 super(propertyName); 1152 mValueType = int.class; 1153 mKeyframes = keyframes; 1154 mIntKeyframes = keyframes; 1155 } 1156 IntPropertyValuesHolder(Property property, Keyframes.IntKeyframes keyframes)1157 public IntPropertyValuesHolder(Property property, Keyframes.IntKeyframes keyframes) { 1158 super(property); 1159 mValueType = int.class; 1160 mKeyframes = keyframes; 1161 mIntKeyframes = keyframes; 1162 if (property instanceof IntProperty) { 1163 mIntProperty = (IntProperty) mProperty; 1164 } 1165 } 1166 IntPropertyValuesHolder(String propertyName, int... values)1167 public IntPropertyValuesHolder(String propertyName, int... values) { 1168 super(propertyName); 1169 setIntValues(values); 1170 } 1171 IntPropertyValuesHolder(Property property, int... values)1172 public IntPropertyValuesHolder(Property property, int... values) { 1173 super(property); 1174 setIntValues(values); 1175 if (property instanceof IntProperty) { 1176 mIntProperty = (IntProperty) mProperty; 1177 } 1178 } 1179 1180 @Override setIntValues(int... values)1181 public void setIntValues(int... values) { 1182 super.setIntValues(values); 1183 mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes; 1184 } 1185 1186 @Override calculateValue(float fraction)1187 void calculateValue(float fraction) { 1188 mIntAnimatedValue = mIntKeyframes.getIntValue(fraction); 1189 } 1190 1191 @Override getAnimatedValue()1192 Object getAnimatedValue() { 1193 return mIntAnimatedValue; 1194 } 1195 1196 @Override clone()1197 public IntPropertyValuesHolder clone() { 1198 IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone(); 1199 newPVH.mIntKeyframes = (Keyframes.IntKeyframes) newPVH.mKeyframes; 1200 return newPVH; 1201 } 1202 1203 /** 1204 * Internal function to set the value on the target object, using the setter set up 1205 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 1206 * to handle turning the value calculated by ValueAnimator into a value set on the object 1207 * according to the name of the property. 1208 * @param target The target object on which the value is set 1209 */ 1210 @Override setAnimatedValue(Object target)1211 void setAnimatedValue(Object target) { 1212 if (mIntProperty != null) { 1213 mIntProperty.setValue(target, mIntAnimatedValue); 1214 return; 1215 } 1216 if (mProperty != null) { 1217 mProperty.set(target, mIntAnimatedValue); 1218 return; 1219 } 1220 if (mJniSetter != 0) { 1221 nCallIntMethod(target, mJniSetter, mIntAnimatedValue); 1222 return; 1223 } 1224 if (mSetter != null) { 1225 try { 1226 mTmpValueArray[0] = mIntAnimatedValue; 1227 mSetter.invoke(target, mTmpValueArray); 1228 } catch (InvocationTargetException e) { 1229 Log.e("PropertyValuesHolder", e.toString()); 1230 } catch (IllegalAccessException e) { 1231 Log.e("PropertyValuesHolder", e.toString()); 1232 } 1233 } 1234 } 1235 1236 @Override setupSetter(Class targetClass)1237 void setupSetter(Class targetClass) { 1238 if (mProperty != null) { 1239 return; 1240 } 1241 // Check new static hashmap<propName, int> for setter method 1242 synchronized(sJNISetterPropertyMap) { 1243 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); 1244 boolean wasInMap = false; 1245 if (propertyMap != null) { 1246 wasInMap = propertyMap.containsKey(mPropertyName); 1247 if (wasInMap) { 1248 Long jniSetter = propertyMap.get(mPropertyName); 1249 if (jniSetter != null) { 1250 mJniSetter = jniSetter; 1251 } 1252 } 1253 } 1254 if (!wasInMap) { 1255 String methodName = getMethodName("set", mPropertyName); 1256 try { 1257 mJniSetter = nGetIntMethod(targetClass, methodName); 1258 } catch (NoSuchMethodError e) { 1259 // Couldn't find it via JNI - try reflection next. Probably means the method 1260 // doesn't exist, or the type is wrong. An error will be logged later if 1261 // reflection fails as well. 1262 } 1263 if (propertyMap == null) { 1264 propertyMap = new HashMap<String, Long>(); 1265 sJNISetterPropertyMap.put(targetClass, propertyMap); 1266 } 1267 propertyMap.put(mPropertyName, mJniSetter); 1268 } 1269 } 1270 if (mJniSetter == 0) { 1271 // Couldn't find method through fast JNI approach - just use reflection 1272 super.setupSetter(targetClass); 1273 } 1274 } 1275 } 1276 1277 static class FloatPropertyValuesHolder extends PropertyValuesHolder { 1278 1279 // Cache JNI functions to avoid looking them up twice 1280 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap = 1281 new HashMap<Class, HashMap<String, Long>>(); 1282 long mJniSetter; 1283 private FloatProperty mFloatProperty; 1284 1285 Keyframes.FloatKeyframes mFloatKeyframes; 1286 float mFloatAnimatedValue; 1287 FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes)1288 public FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes) { 1289 super(propertyName); 1290 mValueType = float.class; 1291 mKeyframes = keyframes; 1292 mFloatKeyframes = keyframes; 1293 } 1294 FloatPropertyValuesHolder(Property property, Keyframes.FloatKeyframes keyframes)1295 public FloatPropertyValuesHolder(Property property, Keyframes.FloatKeyframes keyframes) { 1296 super(property); 1297 mValueType = float.class; 1298 mKeyframes = keyframes; 1299 mFloatKeyframes = keyframes; 1300 if (property instanceof FloatProperty) { 1301 mFloatProperty = (FloatProperty) mProperty; 1302 } 1303 } 1304 FloatPropertyValuesHolder(String propertyName, float... values)1305 public FloatPropertyValuesHolder(String propertyName, float... values) { 1306 super(propertyName); 1307 setFloatValues(values); 1308 } 1309 FloatPropertyValuesHolder(Property property, float... values)1310 public FloatPropertyValuesHolder(Property property, float... values) { 1311 super(property); 1312 setFloatValues(values); 1313 if (property instanceof FloatProperty) { 1314 mFloatProperty = (FloatProperty) mProperty; 1315 } 1316 } 1317 1318 @Override setFloatValues(float... values)1319 public void setFloatValues(float... values) { 1320 super.setFloatValues(values); 1321 mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes; 1322 } 1323 1324 @Override calculateValue(float fraction)1325 void calculateValue(float fraction) { 1326 mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction); 1327 } 1328 1329 @Override getAnimatedValue()1330 Object getAnimatedValue() { 1331 return mFloatAnimatedValue; 1332 } 1333 1334 @Override clone()1335 public FloatPropertyValuesHolder clone() { 1336 FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone(); 1337 newPVH.mFloatKeyframes = (Keyframes.FloatKeyframes) newPVH.mKeyframes; 1338 return newPVH; 1339 } 1340 1341 /** 1342 * Internal function to set the value on the target object, using the setter set up 1343 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 1344 * to handle turning the value calculated by ValueAnimator into a value set on the object 1345 * according to the name of the property. 1346 * @param target The target object on which the value is set 1347 */ 1348 @Override setAnimatedValue(Object target)1349 void setAnimatedValue(Object target) { 1350 if (mFloatProperty != null) { 1351 mFloatProperty.setValue(target, mFloatAnimatedValue); 1352 return; 1353 } 1354 if (mProperty != null) { 1355 mProperty.set(target, mFloatAnimatedValue); 1356 return; 1357 } 1358 if (mJniSetter != 0) { 1359 nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue); 1360 return; 1361 } 1362 if (mSetter != null) { 1363 try { 1364 mTmpValueArray[0] = mFloatAnimatedValue; 1365 mSetter.invoke(target, mTmpValueArray); 1366 } catch (InvocationTargetException e) { 1367 Log.e("PropertyValuesHolder", e.toString()); 1368 } catch (IllegalAccessException e) { 1369 Log.e("PropertyValuesHolder", e.toString()); 1370 } 1371 } 1372 } 1373 1374 @Override setupSetter(Class targetClass)1375 void setupSetter(Class targetClass) { 1376 if (mProperty != null) { 1377 return; 1378 } 1379 // Check new static hashmap<propName, int> for setter method 1380 synchronized (sJNISetterPropertyMap) { 1381 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); 1382 boolean wasInMap = false; 1383 if (propertyMap != null) { 1384 wasInMap = propertyMap.containsKey(mPropertyName); 1385 if (wasInMap) { 1386 Long jniSetter = propertyMap.get(mPropertyName); 1387 if (jniSetter != null) { 1388 mJniSetter = jniSetter; 1389 } 1390 } 1391 } 1392 if (!wasInMap) { 1393 String methodName = getMethodName("set", mPropertyName); 1394 try { 1395 mJniSetter = nGetFloatMethod(targetClass, methodName); 1396 } catch (NoSuchMethodError e) { 1397 // Couldn't find it via JNI - try reflection next. Probably means the method 1398 // doesn't exist, or the type is wrong. An error will be logged later if 1399 // reflection fails as well. 1400 } 1401 if (propertyMap == null) { 1402 propertyMap = new HashMap<String, Long>(); 1403 sJNISetterPropertyMap.put(targetClass, propertyMap); 1404 } 1405 propertyMap.put(mPropertyName, mJniSetter); 1406 } 1407 } 1408 if (mJniSetter == 0) { 1409 // Couldn't find method through fast JNI approach - just use reflection 1410 super.setupSetter(targetClass); 1411 } 1412 } 1413 1414 } 1415 1416 static class MultiFloatValuesHolder extends PropertyValuesHolder { 1417 private long mJniSetter; 1418 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap = 1419 new HashMap<Class, HashMap<String, Long>>(); 1420 MultiFloatValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Object... values)1421 public MultiFloatValuesHolder(String propertyName, TypeConverter converter, 1422 TypeEvaluator evaluator, Object... values) { 1423 super(propertyName); 1424 setConverter(converter); 1425 setObjectValues(values); 1426 setEvaluator(evaluator); 1427 } 1428 MultiFloatValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Keyframes keyframes)1429 public MultiFloatValuesHolder(String propertyName, TypeConverter converter, 1430 TypeEvaluator evaluator, Keyframes keyframes) { 1431 super(propertyName); 1432 setConverter(converter); 1433 mKeyframes = keyframes; 1434 setEvaluator(evaluator); 1435 } 1436 1437 /** 1438 * Internal function to set the value on the target object, using the setter set up 1439 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 1440 * to handle turning the value calculated by ValueAnimator into a value set on the object 1441 * according to the name of the property. 1442 * 1443 * @param target The target object on which the value is set 1444 */ 1445 @Override setAnimatedValue(Object target)1446 void setAnimatedValue(Object target) { 1447 float[] values = (float[]) getAnimatedValue(); 1448 int numParameters = values.length; 1449 if (mJniSetter != 0) { 1450 switch (numParameters) { 1451 case 1: 1452 nCallFloatMethod(target, mJniSetter, values[0]); 1453 break; 1454 case 2: 1455 nCallTwoFloatMethod(target, mJniSetter, values[0], values[1]); 1456 break; 1457 case 4: 1458 nCallFourFloatMethod(target, mJniSetter, values[0], values[1], 1459 values[2], values[3]); 1460 break; 1461 default: { 1462 nCallMultipleFloatMethod(target, mJniSetter, values); 1463 break; 1464 } 1465 } 1466 } 1467 } 1468 1469 /** 1470 * Internal function (called from ObjectAnimator) to set up the setter and getter 1471 * prior to running the animation. No getter can be used for multiple parameters. 1472 * 1473 * @param target The object on which the setter exists. 1474 */ 1475 @Override setupSetterAndGetter(Object target)1476 void setupSetterAndGetter(Object target) { 1477 setupSetter(target.getClass()); 1478 } 1479 1480 @Override setupSetter(Class targetClass)1481 void setupSetter(Class targetClass) { 1482 if (mJniSetter != 0) { 1483 return; 1484 } 1485 synchronized(sJNISetterPropertyMap) { 1486 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); 1487 boolean wasInMap = false; 1488 if (propertyMap != null) { 1489 wasInMap = propertyMap.containsKey(mPropertyName); 1490 if (wasInMap) { 1491 Long jniSetter = propertyMap.get(mPropertyName); 1492 if (jniSetter != null) { 1493 mJniSetter = jniSetter; 1494 } 1495 } 1496 } 1497 if (!wasInMap) { 1498 String methodName = getMethodName("set", mPropertyName); 1499 calculateValue(0f); 1500 float[] values = (float[]) getAnimatedValue(); 1501 int numParams = values.length; 1502 try { 1503 mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams); 1504 } catch (NoSuchMethodError e) { 1505 // try without the 'set' prefix 1506 try { 1507 mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName, 1508 numParams); 1509 } catch (NoSuchMethodError e2) { 1510 // just try reflection next 1511 } 1512 } 1513 if (propertyMap == null) { 1514 propertyMap = new HashMap<String, Long>(); 1515 sJNISetterPropertyMap.put(targetClass, propertyMap); 1516 } 1517 propertyMap.put(mPropertyName, mJniSetter); 1518 } 1519 } 1520 } 1521 } 1522 1523 static class MultiIntValuesHolder extends PropertyValuesHolder { 1524 private long mJniSetter; 1525 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap = 1526 new HashMap<Class, HashMap<String, Long>>(); 1527 MultiIntValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Object... values)1528 public MultiIntValuesHolder(String propertyName, TypeConverter converter, 1529 TypeEvaluator evaluator, Object... values) { 1530 super(propertyName); 1531 setConverter(converter); 1532 setObjectValues(values); 1533 setEvaluator(evaluator); 1534 } 1535 MultiIntValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Keyframes keyframes)1536 public MultiIntValuesHolder(String propertyName, TypeConverter converter, 1537 TypeEvaluator evaluator, Keyframes keyframes) { 1538 super(propertyName); 1539 setConverter(converter); 1540 mKeyframes = keyframes; 1541 setEvaluator(evaluator); 1542 } 1543 1544 /** 1545 * Internal function to set the value on the target object, using the setter set up 1546 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 1547 * to handle turning the value calculated by ValueAnimator into a value set on the object 1548 * according to the name of the property. 1549 * 1550 * @param target The target object on which the value is set 1551 */ 1552 @Override setAnimatedValue(Object target)1553 void setAnimatedValue(Object target) { 1554 int[] values = (int[]) getAnimatedValue(); 1555 int numParameters = values.length; 1556 if (mJniSetter != 0) { 1557 switch (numParameters) { 1558 case 1: 1559 nCallIntMethod(target, mJniSetter, values[0]); 1560 break; 1561 case 2: 1562 nCallTwoIntMethod(target, mJniSetter, values[0], values[1]); 1563 break; 1564 case 4: 1565 nCallFourIntMethod(target, mJniSetter, values[0], values[1], 1566 values[2], values[3]); 1567 break; 1568 default: { 1569 nCallMultipleIntMethod(target, mJniSetter, values); 1570 break; 1571 } 1572 } 1573 } 1574 } 1575 1576 /** 1577 * Internal function (called from ObjectAnimator) to set up the setter and getter 1578 * prior to running the animation. No getter can be used for multiple parameters. 1579 * 1580 * @param target The object on which the setter exists. 1581 */ 1582 @Override setupSetterAndGetter(Object target)1583 void setupSetterAndGetter(Object target) { 1584 setupSetter(target.getClass()); 1585 } 1586 1587 @Override setupSetter(Class targetClass)1588 void setupSetter(Class targetClass) { 1589 if (mJniSetter != 0) { 1590 return; 1591 } 1592 synchronized(sJNISetterPropertyMap) { 1593 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); 1594 boolean wasInMap = false; 1595 if (propertyMap != null) { 1596 wasInMap = propertyMap.containsKey(mPropertyName); 1597 if (wasInMap) { 1598 Long jniSetter = propertyMap.get(mPropertyName); 1599 if (jniSetter != null) { 1600 mJniSetter = jniSetter; 1601 } 1602 } 1603 } 1604 if (!wasInMap) { 1605 String methodName = getMethodName("set", mPropertyName); 1606 calculateValue(0f); 1607 int[] values = (int[]) getAnimatedValue(); 1608 int numParams = values.length; 1609 try { 1610 mJniSetter = nGetMultipleIntMethod(targetClass, methodName, numParams); 1611 } catch (NoSuchMethodError e) { 1612 // try without the 'set' prefix 1613 try { 1614 mJniSetter = nGetMultipleIntMethod(targetClass, mPropertyName, 1615 numParams); 1616 } catch (NoSuchMethodError e2) { 1617 // couldn't find it. 1618 } 1619 } 1620 if (propertyMap == null) { 1621 propertyMap = new HashMap<String, Long>(); 1622 sJNISetterPropertyMap.put(targetClass, propertyMap); 1623 } 1624 propertyMap.put(mPropertyName, mJniSetter); 1625 } 1626 } 1627 } 1628 } 1629 1630 /** 1631 * Convert from PointF to float[] for multi-float setters along a Path. 1632 */ 1633 private static class PointFToFloatArray extends TypeConverter<PointF, float[]> { 1634 private float[] mCoordinates = new float[2]; 1635 PointFToFloatArray()1636 public PointFToFloatArray() { 1637 super(PointF.class, float[].class); 1638 } 1639 1640 @Override convert(PointF value)1641 public float[] convert(PointF value) { 1642 mCoordinates[0] = value.x; 1643 mCoordinates[1] = value.y; 1644 return mCoordinates; 1645 } 1646 }; 1647 1648 /** 1649 * Convert from PointF to int[] for multi-int setters along a Path. 1650 */ 1651 private static class PointFToIntArray extends TypeConverter<PointF, int[]> { 1652 private int[] mCoordinates = new int[2]; 1653 PointFToIntArray()1654 public PointFToIntArray() { 1655 super(PointF.class, int[].class); 1656 } 1657 1658 @Override convert(PointF value)1659 public int[] convert(PointF value) { 1660 mCoordinates[0] = Math.round(value.x); 1661 mCoordinates[1] = Math.round(value.y); 1662 return mCoordinates; 1663 } 1664 }; 1665 1666 /** 1667 * @hide 1668 */ 1669 public static class PropertyValues { 1670 public String propertyName; 1671 public Class type; 1672 public Object startValue; 1673 public Object endValue; 1674 public DataSource dataSource = null; 1675 public interface DataSource { getValueAtFraction(float fraction)1676 Object getValueAtFraction(float fraction); 1677 } toString()1678 public String toString() { 1679 return ("property name: " + propertyName + ", type: " + type + ", startValue: " 1680 + startValue.toString() + ", endValue: " + endValue.toString()); 1681 } 1682 } 1683 nGetIntMethod(Class targetClass, String methodName)1684 native static private long nGetIntMethod(Class targetClass, String methodName); nGetFloatMethod(Class targetClass, String methodName)1685 native static private long nGetFloatMethod(Class targetClass, String methodName); nGetMultipleIntMethod(Class targetClass, String methodName, int numParams)1686 native static private long nGetMultipleIntMethod(Class targetClass, String methodName, 1687 int numParams); nGetMultipleFloatMethod(Class targetClass, String methodName, int numParams)1688 native static private long nGetMultipleFloatMethod(Class targetClass, String methodName, 1689 int numParams); nCallIntMethod(Object target, long methodID, int arg)1690 native static private void nCallIntMethod(Object target, long methodID, int arg); nCallFloatMethod(Object target, long methodID, float arg)1691 native static private void nCallFloatMethod(Object target, long methodID, float arg); nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2)1692 native static private void nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2); nCallFourIntMethod(Object target, long methodID, int arg1, int arg2, int arg3, int arg4)1693 native static private void nCallFourIntMethod(Object target, long methodID, int arg1, int arg2, 1694 int arg3, int arg4); nCallMultipleIntMethod(Object target, long methodID, int[] args)1695 native static private void nCallMultipleIntMethod(Object target, long methodID, int[] args); nCallTwoFloatMethod(Object target, long methodID, float arg1, float arg2)1696 native static private void nCallTwoFloatMethod(Object target, long methodID, float arg1, 1697 float arg2); nCallFourFloatMethod(Object target, long methodID, float arg1, float arg2, float arg3, float arg4)1698 native static private void nCallFourFloatMethod(Object target, long methodID, float arg1, 1699 float arg2, float arg3, float arg4); nCallMultipleFloatMethod(Object target, long methodID, float[] args)1700 native static private void nCallMultipleFloatMethod(Object target, long methodID, float[] args); 1701 } 1702