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 if (mProperty != null) { 811 // check to make sure that mProperty is on the class of target 812 try { 813 Object testValue = null; 814 List<Keyframe> keyframes = mKeyframes.getKeyframes(); 815 int keyframeCount = keyframes == null ? 0 : keyframes.size(); 816 for (int i = 0; i < keyframeCount; i++) { 817 Keyframe kf = keyframes.get(i); 818 if (!kf.hasValue() || kf.valueWasSetOnStart()) { 819 if (testValue == null) { 820 testValue = convertBack(mProperty.get(target)); 821 } 822 kf.setValue(testValue); 823 kf.setValueWasSetOnStart(true); 824 } 825 } 826 return; 827 } catch (ClassCastException e) { 828 Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() + 829 ") on target object " + target + ". Trying reflection instead"); 830 mProperty = null; 831 } 832 } 833 // 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()) { 844 if (mGetter == null) { 845 setupGetter(targetClass); 846 if (mGetter == null) { 847 // Already logged the error - just return to avoid NPE 848 return; 849 } 850 } 851 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 } 860 } 861 } 862 } 863 } 864 convertBack(Object value)865 private Object convertBack(Object value) { 866 if (mConverter != null) { 867 if (!(mConverter instanceof BidirectionalTypeConverter)) { 868 throw new IllegalArgumentException("Converter " 869 + mConverter.getClass().getName() 870 + " must be a BidirectionalTypeConverter"); 871 } 872 value = ((BidirectionalTypeConverter) mConverter).convertBack(value); 873 } 874 return value; 875 } 876 877 /** 878 * 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 */ setupValue(Object target, Keyframe kf)884 private void setupValue(Object target, Keyframe kf) { 885 if (mProperty != null) { 886 Object value = convertBack(mProperty.get(target)); 887 kf.setValue(value); 888 } else { 889 try { 890 if (mGetter == null) { 891 Class targetClass = target.getClass(); 892 setupGetter(targetClass); 893 if (mGetter == null) { 894 // Already logged the error - just return to avoid NPE 895 return; 896 } 897 } 898 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()); 904 } 905 } 906 } 907 908 /** 909 * This function is called by ObjectAnimator when setting the start values for an animation. 910 * 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 */ setupStartValue(Object target)916 void setupStartValue(Object target) { 917 List<Keyframe> keyframes = mKeyframes.getKeyframes(); 918 if (!keyframes.isEmpty()) { 919 setupValue(target, keyframes.get(0)); 920 } 921 } 922 923 /** 924 * This function is called by ObjectAnimator when setting the end values for an animation. 925 * 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 */ setupEndValue(Object target)931 void setupEndValue(Object target) { 932 List<Keyframe> keyframes = mKeyframes.getKeyframes(); 933 if (!keyframes.isEmpty()) { 934 setupValue(target, keyframes.get(keyframes.size() - 1)); 935 } 936 } 937 938 @Override clone()939 public PropertyValuesHolder clone() { 940 try { 941 PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone(); 942 newPVH.mPropertyName = mPropertyName; 943 newPVH.mProperty = mProperty; 944 newPVH.mKeyframes = mKeyframes.clone(); 945 newPVH.mEvaluator = mEvaluator; 946 return newPVH; 947 } catch (CloneNotSupportedException e) { 948 // won't reach here 949 return null; 950 } 951 } 952 953 /** 954 * Internal function to set the value on the target object, using the setter set up 955 * 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 957 * according to the name of the property. 958 * @param target The target object on which the value is set 959 */ setAnimatedValue(Object target)960 void setAnimatedValue(Object target) { 961 if (mProperty != null) { 962 mProperty.set(target, getAnimatedValue()); 963 } 964 if (mSetter != null) { 965 try { 966 mTmpValueArray[0] = getAnimatedValue(); 967 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 /** 977 * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used 978 * to calculate animated values. 979 */ init()980 void init() { 981 if (mEvaluator == null) { 982 // We already handle int and float automatically, but not their Object 983 // 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 990 // evaluator if one has been set on this class 991 mKeyframes.setEvaluator(mEvaluator); 992 } 993 } 994 995 /** 996 * The TypeEvaluator will be automatically determined based on the type of values 997 * 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, 1001 * only values of type float and int (and their Object equivalents: Float 1002 * and Integer) are correctly interpolated; all other types require setting a TypeEvaluator. 1003 * @param evaluator 1004 */ setEvaluator(TypeEvaluator evaluator)1005 public void setEvaluator(TypeEvaluator evaluator) { 1006 mEvaluator = evaluator; 1007 mKeyframes.setEvaluator(evaluator); 1008 } 1009 1010 /** 1011 * Function used to calculate the value according to the evaluator set up for 1012 * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue(). 1013 * 1014 * @param fraction The elapsed, interpolated fraction of the animation. 1015 */ calculateValue(float fraction)1016 void calculateValue(float fraction) { 1017 Object value = mKeyframes.getValue(fraction); 1018 mAnimatedValue = mConverter == null ? value : mConverter.convert(value); 1019 } 1020 1021 /** 1022 * 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 */ setPropertyName(String propertyName)1036 public void setPropertyName(String propertyName) { 1037 mPropertyName = propertyName; 1038 } 1039 1040 /** 1041 * 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 */ setProperty(Property property)1048 public void setProperty(Property property) { 1049 mProperty = property; 1050 } 1051 1052 /** 1053 * 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 */ getPropertyName()1060 public String getPropertyName() { 1061 return mPropertyName; 1062 } 1063 1064 /** 1065 * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value 1066 * most recently calculated in calculateValue(). 1067 * @return 1068 */ getAnimatedValue()1069 Object getAnimatedValue() { 1070 return mAnimatedValue; 1071 } 1072 1073 /** 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 */ getPropertyValues(PropertyValues values)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 1097 || 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. 1103 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 1114 /** 1115 * @hide 1116 */ getValueType()1117 public Class getValueType() { 1118 return mValueType; 1119 } 1120 1121 @Override toString()1122 public String toString() { 1123 return mPropertyName + ": " + mKeyframes.toString(); 1124 } 1125 1126 /** 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 */ getMethodName(String prefix, String propertyName)1139 static String getMethodName(String prefix, String propertyName) { 1140 if (propertyName == null || propertyName.length() == 0) { 1141 // shouldn't get here 1142 return prefix; 1143 } 1144 char firstLetter = Character.toUpperCase(propertyName.charAt(0)); 1145 String theRest = propertyName.substring(1); 1146 return prefix + firstLetter + theRest; 1147 } 1148 1149 static class IntPropertyValuesHolder extends PropertyValuesHolder { 1150 1151 // Cache JNI functions to avoid looking them up twice 1152 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap = 1153 new HashMap<Class, HashMap<String, Long>>(); 1154 long mJniSetter; 1155 private IntProperty mIntProperty; 1156 1157 Keyframes.IntKeyframes mIntKeyframes; 1158 int mIntAnimatedValue; 1159 IntPropertyValuesHolder(String propertyName, Keyframes.IntKeyframes keyframes)1160 public IntPropertyValuesHolder(String propertyName, Keyframes.IntKeyframes keyframes) { 1161 super(propertyName); 1162 mValueType = int.class; 1163 mKeyframes = keyframes; 1164 mIntKeyframes = keyframes; 1165 } 1166 IntPropertyValuesHolder(Property property, Keyframes.IntKeyframes keyframes)1167 public IntPropertyValuesHolder(Property property, Keyframes.IntKeyframes keyframes) { 1168 super(property); 1169 mValueType = int.class; 1170 mKeyframes = keyframes; 1171 mIntKeyframes = keyframes; 1172 if (property instanceof IntProperty) { 1173 mIntProperty = (IntProperty) mProperty; 1174 } 1175 } 1176 IntPropertyValuesHolder(String propertyName, int... values)1177 public IntPropertyValuesHolder(String propertyName, int... values) { 1178 super(propertyName); 1179 setIntValues(values); 1180 } 1181 IntPropertyValuesHolder(Property property, int... values)1182 public IntPropertyValuesHolder(Property property, int... values) { 1183 super(property); 1184 setIntValues(values); 1185 if (property instanceof IntProperty) { 1186 mIntProperty = (IntProperty) mProperty; 1187 } 1188 } 1189 1190 @Override setProperty(Property property)1191 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 setIntValues(int... values)1200 public void setIntValues(int... values) { 1201 super.setIntValues(values); 1202 mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes; 1203 } 1204 1205 @Override calculateValue(float fraction)1206 void calculateValue(float fraction) { 1207 mIntAnimatedValue = mIntKeyframes.getIntValue(fraction); 1208 } 1209 1210 @Override getAnimatedValue()1211 Object getAnimatedValue() { 1212 return mIntAnimatedValue; 1213 } 1214 1215 @Override clone()1216 public IntPropertyValuesHolder clone() { 1217 IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone(); 1218 newPVH.mIntKeyframes = (Keyframes.IntKeyframes) newPVH.mKeyframes; 1219 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 setAnimatedValue(Object target)1230 void setAnimatedValue(Object target) { 1231 if (mIntProperty != null) { 1232 mIntProperty.setValue(target, mIntAnimatedValue); 1233 return; 1234 } 1235 if (mProperty != null) { 1236 mProperty.set(target, mIntAnimatedValue); 1237 return; 1238 } 1239 if (mJniSetter != 0) { 1240 nCallIntMethod(target, mJniSetter, mIntAnimatedValue); 1241 return; 1242 } 1243 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 } 1254 1255 @Override setupSetter(Class targetClass)1256 void setupSetter(Class targetClass) { 1257 if (mProperty != null) { 1258 return; 1259 } 1260 // Check new static hashmap<propName, int> for setter method 1261 synchronized(sJNISetterPropertyMap) { 1262 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); 1263 boolean wasInMap = false; 1264 if (propertyMap != null) { 1265 wasInMap = propertyMap.containsKey(mPropertyName); 1266 if (wasInMap) { 1267 Long jniSetter = propertyMap.get(mPropertyName); 1268 if (jniSetter != null) { 1269 mJniSetter = jniSetter; 1270 } 1271 } 1272 } 1273 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 } 1288 } 1289 if (mJniSetter == 0) { 1290 // Couldn't find method through fast JNI approach - just use reflection 1291 super.setupSetter(targetClass); 1292 } 1293 } 1294 } 1295 1296 static class FloatPropertyValuesHolder extends PropertyValuesHolder { 1297 1298 // Cache JNI functions to avoid looking them up twice 1299 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap = 1300 new HashMap<Class, HashMap<String, Long>>(); 1301 long mJniSetter; 1302 private FloatProperty mFloatProperty; 1303 1304 Keyframes.FloatKeyframes mFloatKeyframes; 1305 float mFloatAnimatedValue; 1306 FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes)1307 public FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes) { 1308 super(propertyName); 1309 mValueType = float.class; 1310 mKeyframes = keyframes; 1311 mFloatKeyframes = keyframes; 1312 } 1313 FloatPropertyValuesHolder(Property property, Keyframes.FloatKeyframes keyframes)1314 public FloatPropertyValuesHolder(Property property, Keyframes.FloatKeyframes keyframes) { 1315 super(property); 1316 mValueType = float.class; 1317 mKeyframes = keyframes; 1318 mFloatKeyframes = keyframes; 1319 if (property instanceof FloatProperty) { 1320 mFloatProperty = (FloatProperty) mProperty; 1321 } 1322 } 1323 FloatPropertyValuesHolder(String propertyName, float... values)1324 public FloatPropertyValuesHolder(String propertyName, float... values) { 1325 super(propertyName); 1326 setFloatValues(values); 1327 } 1328 FloatPropertyValuesHolder(Property property, float... values)1329 public FloatPropertyValuesHolder(Property property, float... values) { 1330 super(property); 1331 setFloatValues(values); 1332 if (property instanceof FloatProperty) { 1333 mFloatProperty = (FloatProperty) mProperty; 1334 } 1335 } 1336 1337 @Override setProperty(Property property)1338 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 setFloatValues(float... values)1347 public void setFloatValues(float... values) { 1348 super.setFloatValues(values); 1349 mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes; 1350 } 1351 1352 @Override calculateValue(float fraction)1353 void calculateValue(float fraction) { 1354 mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction); 1355 } 1356 1357 @Override getAnimatedValue()1358 Object getAnimatedValue() { 1359 return mFloatAnimatedValue; 1360 } 1361 1362 @Override clone()1363 public FloatPropertyValuesHolder clone() { 1364 FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone(); 1365 newPVH.mFloatKeyframes = (Keyframes.FloatKeyframes) newPVH.mKeyframes; 1366 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 setAnimatedValue(Object target)1377 void setAnimatedValue(Object target) { 1378 if (mFloatProperty != null) { 1379 mFloatProperty.setValue(target, mFloatAnimatedValue); 1380 return; 1381 } 1382 if (mProperty != null) { 1383 mProperty.set(target, mFloatAnimatedValue); 1384 return; 1385 } 1386 if (mJniSetter != 0) { 1387 nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue); 1388 return; 1389 } 1390 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 1402 @Override setupSetter(Class targetClass)1403 void setupSetter(Class targetClass) { 1404 if (mProperty != null) { 1405 return; 1406 } 1407 // Check new static hashmap<propName, int> for setter method 1408 synchronized (sJNISetterPropertyMap) { 1409 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); 1410 boolean wasInMap = false; 1411 if (propertyMap != null) { 1412 wasInMap = propertyMap.containsKey(mPropertyName); 1413 if (wasInMap) { 1414 Long jniSetter = propertyMap.get(mPropertyName); 1415 if (jniSetter != null) { 1416 mJniSetter = jniSetter; 1417 } 1418 } 1419 } 1420 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 } 1435 } 1436 if (mJniSetter == 0) { 1437 // Couldn't find method through fast JNI approach - just use reflection 1438 super.setupSetter(targetClass); 1439 } 1440 } 1441 1442 } 1443 1444 static class MultiFloatValuesHolder extends PropertyValuesHolder { 1445 private long mJniSetter; 1446 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap = 1447 new HashMap<Class, HashMap<String, Long>>(); 1448 MultiFloatValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Object... values)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 MultiFloatValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Keyframes keyframes)1457 public MultiFloatValuesHolder(String propertyName, TypeConverter converter, 1458 TypeEvaluator evaluator, Keyframes keyframes) { 1459 super(propertyName); 1460 setConverter(converter); 1461 mKeyframes = keyframes; 1462 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 setAnimatedValue(Object target)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 setupSetterAndGetter(Object target)1504 void setupSetterAndGetter(Object target) { 1505 setupSetter(target.getClass()); 1506 } 1507 1508 @Override setupSetter(Class targetClass)1509 void setupSetter(Class targetClass) { 1510 if (mJniSetter != 0) { 1511 return; 1512 } 1513 synchronized(sJNISetterPropertyMap) { 1514 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); 1515 boolean wasInMap = false; 1516 if (propertyMap != null) { 1517 wasInMap = propertyMap.containsKey(mPropertyName); 1518 if (wasInMap) { 1519 Long jniSetter = propertyMap.get(mPropertyName); 1520 if (jniSetter != null) { 1521 mJniSetter = jniSetter; 1522 } 1523 } 1524 } 1525 if (!wasInMap) { 1526 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 1534 try { 1535 mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName, 1536 numParams); 1537 } catch (NoSuchMethodError e2) { 1538 // just try reflection next 1539 } 1540 } 1541 if (propertyMap == null) { 1542 propertyMap = new HashMap<String, Long>(); 1543 sJNISetterPropertyMap.put(targetClass, propertyMap); 1544 } 1545 propertyMap.put(mPropertyName, mJniSetter); 1546 } 1547 } 1548 } 1549 } 1550 1551 static class MultiIntValuesHolder extends PropertyValuesHolder { 1552 private long mJniSetter; 1553 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap = 1554 new HashMap<Class, HashMap<String, Long>>(); 1555 MultiIntValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Object... values)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 MultiIntValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Keyframes keyframes)1564 public MultiIntValuesHolder(String propertyName, TypeConverter converter, 1565 TypeEvaluator evaluator, Keyframes keyframes) { 1566 super(propertyName); 1567 setConverter(converter); 1568 mKeyframes = keyframes; 1569 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 setAnimatedValue(Object target)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 setupSetterAndGetter(Object target)1611 void setupSetterAndGetter(Object target) { 1612 setupSetter(target.getClass()); 1613 } 1614 1615 @Override setupSetter(Class targetClass)1616 void setupSetter(Class targetClass) { 1617 if (mJniSetter != 0) { 1618 return; 1619 } 1620 synchronized(sJNISetterPropertyMap) { 1621 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); 1622 boolean wasInMap = false; 1623 if (propertyMap != null) { 1624 wasInMap = propertyMap.containsKey(mPropertyName); 1625 if (wasInMap) { 1626 Long jniSetter = propertyMap.get(mPropertyName); 1627 if (jniSetter != null) { 1628 mJniSetter = jniSetter; 1629 } 1630 } 1631 } 1632 if (!wasInMap) { 1633 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 1641 try { 1642 mJniSetter = nGetMultipleIntMethod(targetClass, mPropertyName, 1643 numParams); 1644 } catch (NoSuchMethodError e2) { 1645 // couldn't find it. 1646 } 1647 } 1648 if (propertyMap == null) { 1649 propertyMap = new HashMap<String, Long>(); 1650 sJNISetterPropertyMap.put(targetClass, propertyMap); 1651 } 1652 propertyMap.put(mPropertyName, mJniSetter); 1653 } 1654 } 1655 } 1656 } 1657 1658 /** 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 PointFToFloatArray()1664 public PointFToFloatArray() { 1665 super(PointF.class, float[].class); 1666 } 1667 1668 @Override convert(PointF value)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 PointFToIntArray()1682 public PointFToIntArray() { 1683 super(PointF.class, int[].class); 1684 } 1685 1686 @Override convert(PointF value)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 1694 /** 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 { getValueAtFraction(float fraction)1704 Object getValueAtFraction(float fraction); 1705 } toString()1706 public String toString() { 1707 return ("property name: " + propertyName + ", type: " + type + ", startValue: " 1708 + startValue.toString() + ", endValue: " + endValue.toString()); 1709 } 1710 } 1711 nGetIntMethod(Class targetClass, String methodName)1712 native static private long nGetIntMethod(Class targetClass, String methodName); nGetFloatMethod(Class targetClass, String methodName)1713 native static private long nGetFloatMethod(Class targetClass, String methodName); nGetMultipleIntMethod(Class targetClass, String methodName, int numParams)1714 native static private long nGetMultipleIntMethod(Class targetClass, String methodName, 1715 int numParams); nGetMultipleFloatMethod(Class targetClass, String methodName, int numParams)1716 native static private long nGetMultipleFloatMethod(Class targetClass, String methodName, 1717 int numParams); nCallIntMethod(Object target, long methodID, int arg)1718 native static private void nCallIntMethod(Object target, long methodID, int arg); nCallFloatMethod(Object target, long methodID, float arg)1719 native static private void nCallFloatMethod(Object target, long methodID, float arg); nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2)1720 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)1721 native static private void nCallFourIntMethod(Object target, long methodID, int arg1, int arg2, 1722 int arg3, int arg4); nCallMultipleIntMethod(Object target, long methodID, int[] args)1723 native static private void nCallMultipleIntMethod(Object target, long methodID, int[] args); nCallTwoFloatMethod(Object target, long methodID, float arg1, float arg2)1724 native static private void nCallTwoFloatMethod(Object target, long methodID, float arg1, 1725 float arg2); nCallFourFloatMethod(Object target, long methodID, float arg1, float arg2, float arg3, float arg4)1726 native static private void nCallFourFloatMethod(Object target, long methodID, float arg1, 1727 float arg2, float arg3, float arg4); nCallMultipleFloatMethod(Object target, long methodID, float[] args)1728 native static private void nCallMultipleFloatMethod(Object target, long methodID, float[] args); 1729 } 1730