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.Property; 25 26 import java.lang.reflect.InvocationTargetException; 27 import java.lang.reflect.Method; 28 import java.util.HashMap; 29 import java.util.List; 30 31 /** 32 * This class holds information about a property and the values that that property 33 * should take on during an animation. PropertyValuesHolder objects can be used to create 34 * animations with ValueAnimator or ObjectAnimator that operate on several different properties 35 * in parallel. 36 */ 37 public class PropertyValuesHolder implements Cloneable { 38 39 /** 40 * The name of the property associated with the values. This need not be a real property, 41 * unless this object is being used with ObjectAnimator. But this is the name by which 42 * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator. 43 */ 44 String mPropertyName; 45 46 /** 47 * @hide 48 */ 49 protected Property mProperty; 50 51 /** 52 * The setter function, if needed. ObjectAnimator hands off this functionality to 53 * PropertyValuesHolder, since it holds all of the per-property information. This 54 * property is automatically 55 * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator. 56 */ 57 Method mSetter = null; 58 59 /** 60 * The getter function, if needed. ObjectAnimator hands off this functionality to 61 * PropertyValuesHolder, since it holds all of the per-property information. This 62 * property is automatically 63 * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator. 64 * The getter is only derived and used if one of the values is null. 65 */ 66 private Method mGetter = null; 67 68 /** 69 * The type of values supplied. This information is used both in deriving the setter/getter 70 * functions and in deriving the type of TypeEvaluator. 71 */ 72 Class mValueType; 73 74 /** 75 * The set of keyframes (time/value pairs) that define this animation. 76 */ 77 Keyframes mKeyframes = null; 78 79 80 // type evaluators for the primitive types handled by this implementation 81 private static final TypeEvaluator sIntEvaluator = new IntEvaluator(); 82 private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator(); 83 84 // We try several different types when searching for appropriate setter/getter functions. 85 // The caller may have supplied values in a type that does not match the setter/getter 86 // functions (such as the integers 0 and 1 to represent floating point values for alpha). 87 // Also, the use of generics in constructors means that we end up with the Object versions 88 // of primitive types (Float vs. float). But most likely, the setter/getter functions 89 // will take primitive types instead. 90 // So we supply an ordered array of other types to try before giving up. 91 private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class, 92 Double.class, Integer.class}; 93 private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class, 94 Float.class, Double.class}; 95 private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class, 96 Float.class, Integer.class}; 97 98 // These maps hold all property entries for a particular class. This map 99 // is used to speed up property/setter/getter lookups for a given class/property 100 // combination. No need to use reflection on the combination more than once. 101 private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap = 102 new HashMap<Class, HashMap<String, Method>>(); 103 private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap = 104 new HashMap<Class, HashMap<String, Method>>(); 105 106 // Used to pass single value to varargs parameter in setter invocation 107 final Object[] mTmpValueArray = new Object[1]; 108 109 /** 110 * The type evaluator used to calculate the animated values. This evaluator is determined 111 * automatically based on the type of the start/end objects passed into the constructor, 112 * but the system only knows about the primitive types int and float. Any other 113 * type will need to set the evaluator to a custom evaluator for that type. 114 */ 115 private TypeEvaluator mEvaluator; 116 117 /** 118 * The value most recently calculated by calculateValue(). This is set during 119 * that function and might be retrieved later either by ValueAnimator.animatedValue() or 120 * by the property-setting logic in ObjectAnimator.animatedValue(). 121 */ 122 private Object mAnimatedValue; 123 124 /** 125 * Converts from the source Object type to the setter Object type. 126 */ 127 private TypeConverter mConverter; 128 129 /** 130 * Internal utility constructor, used by the factory methods to set the property name. 131 * @param propertyName The name of the property for this holder. 132 */ PropertyValuesHolder(String propertyName)133 private PropertyValuesHolder(String propertyName) { 134 mPropertyName = propertyName; 135 } 136 137 /** 138 * Internal utility constructor, used by the factory methods to set the property. 139 * @param property The property for this holder. 140 */ PropertyValuesHolder(Property property)141 private PropertyValuesHolder(Property property) { 142 mProperty = property; 143 if (property != null) { 144 mPropertyName = property.getName(); 145 } 146 } 147 148 /** 149 * Constructs and returns a PropertyValuesHolder with a given property name and 150 * set of int values. 151 * @param propertyName The name of the property being animated. 152 * @param values The values that the named property will animate between. 153 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 154 */ ofInt(String propertyName, int... values)155 public static PropertyValuesHolder ofInt(String propertyName, int... values) { 156 return new IntPropertyValuesHolder(propertyName, values); 157 } 158 159 /** 160 * Constructs and returns a PropertyValuesHolder with a given property and 161 * set of int values. 162 * @param property The property being animated. Should not be null. 163 * @param values The values that the property will animate between. 164 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 165 */ ofInt(Property<?, Integer> property, int... values)166 public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) { 167 return new IntPropertyValuesHolder(property, values); 168 } 169 170 /** 171 * Constructs and returns a PropertyValuesHolder with a given property name and 172 * set of <code>int[]</code> values. At least two <code>int[]</code> values must be supplied, 173 * a start and end value. If more values are supplied, the values will be animated from the 174 * start, through all intermediate values to the end value. When used with ObjectAnimator, 175 * the elements of the array represent the parameters of the setter function. 176 * 177 * @param propertyName The name of the property being animated. Can also be the 178 * case-sensitive name of the entire setter method. Should not be null. 179 * @param values The values that the property will animate between. 180 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 181 * @see IntArrayEvaluator#IntArrayEvaluator(int[]) 182 * @see ObjectAnimator#ofMultiInt(Object, String, TypeConverter, TypeEvaluator, Object[]) 183 */ ofMultiInt(String propertyName, int[][] values)184 public static PropertyValuesHolder ofMultiInt(String propertyName, int[][] values) { 185 if (values.length < 2) { 186 throw new IllegalArgumentException("At least 2 values must be supplied"); 187 } 188 int numParameters = 0; 189 for (int i = 0; i < values.length; i++) { 190 if (values[i] == null) { 191 throw new IllegalArgumentException("values must not be null"); 192 } 193 int length = values[i].length; 194 if (i == 0) { 195 numParameters = length; 196 } else if (length != numParameters) { 197 throw new IllegalArgumentException("Values must all have the same length"); 198 } 199 } 200 IntArrayEvaluator evaluator = new IntArrayEvaluator(new int[numParameters]); 201 return new MultiIntValuesHolder(propertyName, null, evaluator, (Object[]) values); 202 } 203 204 /** 205 * Constructs and returns a PropertyValuesHolder with a given property name to use 206 * as a multi-int setter. The values are animated along the path, with the first 207 * parameter of the setter set to the x coordinate and the second set to the y coordinate. 208 * 209 * @param propertyName The name of the property being animated. Can also be the 210 * case-sensitive name of the entire setter method. Should not be null. 211 * The setter must take exactly two <code>int</code> parameters. 212 * @param path The Path along which the values should be animated. 213 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 214 * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...) 215 */ ofMultiInt(String propertyName, Path path)216 public static PropertyValuesHolder ofMultiInt(String propertyName, Path path) { 217 Keyframes keyframes = KeyframeSet.ofPath(path); 218 PointFToIntArray converter = new PointFToIntArray(); 219 return new MultiIntValuesHolder(propertyName, converter, null, keyframes); 220 } 221 222 /** 223 * Constructs and returns a PropertyValuesHolder with a given property and 224 * set of Object values for use with ObjectAnimator multi-value setters. The Object 225 * values are converted to <code>int[]</code> using the converter. 226 * 227 * @param propertyName The property being animated or complete name of the setter. 228 * Should not be null. 229 * @param converter Used to convert the animated value to setter parameters. 230 * @param evaluator A TypeEvaluator that will be called on each animation frame to 231 * provide the necessary interpolation between the Object values to derive the animated 232 * value. 233 * @param values The values that the property will animate between. 234 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 235 * @see ObjectAnimator#ofMultiInt(Object, String, TypeConverter, TypeEvaluator, Object[]) 236 * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...) 237 */ ofMultiInt(String propertyName, TypeConverter<V, int[]> converter, TypeEvaluator<V> evaluator, V... values)238 public static <V> PropertyValuesHolder ofMultiInt(String propertyName, 239 TypeConverter<V, int[]> converter, TypeEvaluator<V> evaluator, V... values) { 240 return new MultiIntValuesHolder(propertyName, converter, evaluator, values); 241 } 242 243 /** 244 * Constructs and returns a PropertyValuesHolder object with the specified property name or 245 * setter name for use in a multi-int setter function using ObjectAnimator. The values can be 246 * of any type, but the type should be consistent so that the supplied 247 * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The 248 * <code>converter</code> converts the values to parameters in the setter function. 249 * 250 * <p>At least two values must be supplied, a start and an end value.</p> 251 * 252 * @param propertyName The name of the property to associate with the set of values. This 253 * may also be the complete name of a setter function. 254 * @param converter Converts <code>values</code> into int parameters for the setter. 255 * Can be null if the Keyframes have int[] values. 256 * @param evaluator Used to interpolate between values. 257 * @param values The values at specific fractional times to evaluate between 258 * @return A PropertyValuesHolder for a multi-int parameter setter. 259 */ ofMultiInt(String propertyName, TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, Keyframe... values)260 public static <T> PropertyValuesHolder ofMultiInt(String propertyName, 261 TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, Keyframe... values) { 262 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); 263 return new MultiIntValuesHolder(propertyName, converter, evaluator, keyframeSet); 264 } 265 266 /** 267 * Constructs and returns a PropertyValuesHolder with a given property name and 268 * set of float values. 269 * @param propertyName The name of the property being animated. 270 * @param values The values that the named property will animate between. 271 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 272 */ ofFloat(String propertyName, float... values)273 public static PropertyValuesHolder ofFloat(String propertyName, float... values) { 274 return new FloatPropertyValuesHolder(propertyName, values); 275 } 276 277 /** 278 * Constructs and returns a PropertyValuesHolder with a given property and 279 * set of float values. 280 * @param property The property being animated. Should not be null. 281 * @param values The values that the property will animate between. 282 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 283 */ ofFloat(Property<?, Float> property, float... values)284 public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) { 285 return new FloatPropertyValuesHolder(property, values); 286 } 287 288 /** 289 * Constructs and returns a PropertyValuesHolder with a given property name and 290 * set of <code>float[]</code> values. At least two <code>float[]</code> values must be supplied, 291 * a start and end value. If more values are supplied, the values will be animated from the 292 * start, through all intermediate values to the end value. When used with ObjectAnimator, 293 * the elements of the array represent the parameters of the setter function. 294 * 295 * @param propertyName The name of the property being animated. Can also be the 296 * case-sensitive name of the entire setter method. Should not be null. 297 * @param values The values that the property will animate between. 298 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 299 * @see FloatArrayEvaluator#FloatArrayEvaluator(float[]) 300 * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[]) 301 */ ofMultiFloat(String propertyName, float[][] values)302 public static PropertyValuesHolder ofMultiFloat(String propertyName, float[][] values) { 303 if (values.length < 2) { 304 throw new IllegalArgumentException("At least 2 values must be supplied"); 305 } 306 int numParameters = 0; 307 for (int i = 0; i < values.length; i++) { 308 if (values[i] == null) { 309 throw new IllegalArgumentException("values must not be null"); 310 } 311 int length = values[i].length; 312 if (i == 0) { 313 numParameters = length; 314 } else if (length != numParameters) { 315 throw new IllegalArgumentException("Values must all have the same length"); 316 } 317 } 318 FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[numParameters]); 319 return new MultiFloatValuesHolder(propertyName, null, evaluator, (Object[]) values); 320 } 321 322 /** 323 * Constructs and returns a PropertyValuesHolder with a given property name to use 324 * as a multi-float setter. The values are animated along the path, with the first 325 * parameter of the setter set to the x coordinate and the second set to the y coordinate. 326 * 327 * @param propertyName The name of the property being animated. Can also be the 328 * case-sensitive name of the entire setter method. Should not be null. 329 * The setter must take exactly two <code>float</code> parameters. 330 * @param path The Path along which the values should be animated. 331 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 332 * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...) 333 */ ofMultiFloat(String propertyName, Path path)334 public static PropertyValuesHolder ofMultiFloat(String propertyName, Path path) { 335 Keyframes keyframes = KeyframeSet.ofPath(path); 336 PointFToFloatArray converter = new PointFToFloatArray(); 337 return new MultiFloatValuesHolder(propertyName, converter, null, keyframes); 338 } 339 340 /** 341 * Constructs and returns a PropertyValuesHolder with a given property and 342 * set of Object values for use with ObjectAnimator multi-value setters. The Object 343 * values are converted to <code>float[]</code> using the converter. 344 * 345 * @param propertyName The property being animated or complete name of the setter. 346 * Should not be null. 347 * @param converter Used to convert the animated value to setter parameters. 348 * @param evaluator A TypeEvaluator that will be called on each animation frame to 349 * provide the necessary interpolation between the Object values to derive the animated 350 * value. 351 * @param values The values that the property will animate between. 352 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 353 * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[]) 354 */ ofMultiFloat(String propertyName, TypeConverter<V, float[]> converter, TypeEvaluator<V> evaluator, V... values)355 public static <V> PropertyValuesHolder ofMultiFloat(String propertyName, 356 TypeConverter<V, float[]> converter, TypeEvaluator<V> evaluator, V... values) { 357 return new MultiFloatValuesHolder(propertyName, converter, evaluator, values); 358 } 359 360 /** 361 * Constructs and returns a PropertyValuesHolder object with the specified property name or 362 * setter name for use in a multi-float setter function using ObjectAnimator. The values can be 363 * of any type, but the type should be consistent so that the supplied 364 * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The 365 * <code>converter</code> converts the values to parameters in the setter function. 366 * 367 * <p>At least two values must be supplied, a start and an end value.</p> 368 * 369 * @param propertyName The name of the property to associate with the set of values. This 370 * may also be the complete name of a setter function. 371 * @param converter Converts <code>values</code> into float parameters for the setter. 372 * Can be null if the Keyframes have float[] values. 373 * @param evaluator Used to interpolate between values. 374 * @param values The values at specific fractional times to evaluate between 375 * @return A PropertyValuesHolder for a multi-float parameter setter. 376 */ ofMultiFloat(String propertyName, TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, Keyframe... values)377 public static <T> PropertyValuesHolder ofMultiFloat(String propertyName, 378 TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, Keyframe... values) { 379 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); 380 return new MultiFloatValuesHolder(propertyName, converter, evaluator, keyframeSet); 381 } 382 383 /** 384 * Constructs and returns a PropertyValuesHolder with a given property name and 385 * set of Object values. This variant also takes a TypeEvaluator because the system 386 * cannot automatically interpolate between objects of unknown type. 387 * 388 * @param propertyName The name of the property being animated. 389 * @param evaluator A TypeEvaluator that will be called on each animation frame to 390 * provide the necessary interpolation between the Object values to derive the animated 391 * value. 392 * @param values The values that the named property will animate between. 393 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 394 */ ofObject(String propertyName, TypeEvaluator evaluator, Object... values)395 public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator, 396 Object... values) { 397 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); 398 pvh.setObjectValues(values); 399 pvh.setEvaluator(evaluator); 400 return pvh; 401 } 402 403 /** 404 * Constructs and returns a PropertyValuesHolder with a given property name and 405 * a Path along which the values should be animated. This variant supports a 406 * <code>TypeConverter</code> to convert from <code>PointF</code> to the target 407 * type. 408 * 409 * <p>The PointF passed to <code>converter</code> or <code>property</code>, if 410 * <code>converter</code> is <code>null</code>, is reused on each animation frame and should 411 * not be stored by the setter or TypeConverter.</p> 412 * 413 * @param propertyName The name of the property being animated. 414 * @param converter Converts a PointF to the type associated with the setter. May be 415 * null if conversion is unnecessary. 416 * @param path The Path along which the values should be animated. 417 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 418 */ ofObject(String propertyName, TypeConverter<PointF, ?> converter, Path path)419 public static PropertyValuesHolder ofObject(String propertyName, 420 TypeConverter<PointF, ?> converter, Path path) { 421 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); 422 pvh.mKeyframes = KeyframeSet.ofPath(path); 423 pvh.mValueType = PointF.class; 424 pvh.setConverter(converter); 425 return pvh; 426 } 427 428 /** 429 * Constructs and returns a PropertyValuesHolder with a given property and 430 * set of Object values. This variant also takes a TypeEvaluator because the system 431 * cannot automatically interpolate between objects of unknown type. 432 * 433 * @param property The property being animated. Should not be null. 434 * @param evaluator A TypeEvaluator that will be called on each animation frame to 435 * provide the necessary interpolation between the Object values to derive the animated 436 * value. 437 * @param values The values that the property will animate between. 438 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 439 */ ofObject(Property property, TypeEvaluator<V> evaluator, V... values)440 public static <V> PropertyValuesHolder ofObject(Property property, 441 TypeEvaluator<V> evaluator, V... values) { 442 PropertyValuesHolder pvh = new PropertyValuesHolder(property); 443 pvh.setObjectValues(values); 444 pvh.setEvaluator(evaluator); 445 return pvh; 446 } 447 448 /** 449 * Constructs and returns a PropertyValuesHolder with a given property and 450 * set of Object values. This variant also takes a TypeEvaluator because the system 451 * cannot automatically interpolate between objects of unknown type. This variant also 452 * takes a <code>TypeConverter</code> to convert from animated values to the type 453 * of the property. If only one value is supplied, the <code>TypeConverter</code> 454 * must be a {@link android.animation.BidirectionalTypeConverter} to retrieve the current 455 * value. 456 * 457 * @param property The property being animated. Should not be null. 458 * @param converter Converts the animated object to the Property type. 459 * @param evaluator A TypeEvaluator that will be called on each animation frame to 460 * provide the necessary interpolation between the Object values to derive the animated 461 * value. 462 * @param values The values that the property will animate between. 463 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 464 * @see #setConverter(TypeConverter) 465 * @see TypeConverter 466 */ ofObject(Property<?, V> property, TypeConverter<T, V> converter, TypeEvaluator<T> evaluator, T... values)467 public static <T, V> PropertyValuesHolder ofObject(Property<?, V> property, 468 TypeConverter<T, V> converter, TypeEvaluator<T> evaluator, T... values) { 469 PropertyValuesHolder pvh = new PropertyValuesHolder(property); 470 pvh.setConverter(converter); 471 pvh.setObjectValues(values); 472 pvh.setEvaluator(evaluator); 473 return pvh; 474 } 475 476 /** 477 * Constructs and returns a PropertyValuesHolder with a given property and 478 * a Path along which the values should be animated. This variant supports a 479 * <code>TypeConverter</code> to convert from <code>PointF</code> to the target 480 * type. 481 * 482 * <p>The PointF passed to <code>converter</code> or <code>property</code>, if 483 * <code>converter</code> is <code>null</code>, is reused on each animation frame and should 484 * not be stored by the setter or TypeConverter.</p> 485 * 486 * @param property The property being animated. Should not be null. 487 * @param converter Converts a PointF to the type associated with the setter. May be 488 * null if conversion is unnecessary. 489 * @param path The Path along which the values should be animated. 490 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 491 */ ofObject(Property<?, V> property, TypeConverter<PointF, V> converter, Path path)492 public static <V> PropertyValuesHolder ofObject(Property<?, V> property, 493 TypeConverter<PointF, V> converter, Path path) { 494 PropertyValuesHolder pvh = new PropertyValuesHolder(property); 495 pvh.mKeyframes = KeyframeSet.ofPath(path); 496 pvh.mValueType = PointF.class; 497 pvh.setConverter(converter); 498 return pvh; 499 } 500 501 /** 502 * Constructs and returns a PropertyValuesHolder object with the specified property name and set 503 * of values. These values can be of any type, but the type should be consistent so that 504 * an appropriate {@link android.animation.TypeEvaluator} can be found that matches 505 * the common type. 506 * <p>If there is only one value, it is assumed to be the end value of an animation, 507 * and an initial value will be derived, if possible, by calling a getter function 508 * on the object. Also, if any value is null, the value will be filled in when the animation 509 * starts in the same way. This mechanism of automatically getting null values only works 510 * if the PropertyValuesHolder object is used in conjunction 511 * {@link ObjectAnimator}, and with a getter function 512 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has 513 * no way of determining what the value should be. 514 * @param propertyName The name of the property associated with this set of values. This 515 * can be the actual property name to be used when using a ObjectAnimator object, or 516 * just a name used to get animated values, such as if this object is used with an 517 * ValueAnimator object. 518 * @param values The set of values to animate between. 519 */ ofKeyframe(String propertyName, Keyframe... values)520 public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) { 521 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); 522 return ofKeyframes(propertyName, keyframeSet); 523 } 524 525 /** 526 * Constructs and returns a PropertyValuesHolder object with the specified property and set 527 * of values. These values can be of any type, but the type should be consistent so that 528 * an appropriate {@link android.animation.TypeEvaluator} can be found that matches 529 * the common type. 530 * <p>If there is only one value, it is assumed to be the end value of an animation, 531 * and an initial value will be derived, if possible, by calling the property's 532 * {@link android.util.Property#get(Object)} function. 533 * Also, if any value is null, the value will be filled in when the animation 534 * starts in the same way. This mechanism of automatically getting null values only works 535 * if the PropertyValuesHolder object is used in conjunction with 536 * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has 537 * no way of determining what the value should be. 538 * @param property The property associated with this set of values. Should not be null. 539 * @param values The set of values to animate between. 540 */ ofKeyframe(Property property, Keyframe... values)541 public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) { 542 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); 543 return ofKeyframes(property, keyframeSet); 544 } 545 ofKeyframes(String propertyName, Keyframes keyframes)546 static PropertyValuesHolder ofKeyframes(String propertyName, Keyframes keyframes) { 547 if (keyframes instanceof Keyframes.IntKeyframes) { 548 return new IntPropertyValuesHolder(propertyName, (Keyframes.IntKeyframes) keyframes); 549 } else if (keyframes instanceof Keyframes.FloatKeyframes) { 550 return new FloatPropertyValuesHolder(propertyName, 551 (Keyframes.FloatKeyframes) keyframes); 552 } else { 553 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); 554 pvh.mKeyframes = keyframes; 555 pvh.mValueType = keyframes.getType(); 556 return pvh; 557 } 558 } 559 ofKeyframes(Property property, Keyframes keyframes)560 static PropertyValuesHolder ofKeyframes(Property property, Keyframes keyframes) { 561 if (keyframes instanceof Keyframes.IntKeyframes) { 562 return new IntPropertyValuesHolder(property, (Keyframes.IntKeyframes) keyframes); 563 } else if (keyframes instanceof Keyframes.FloatKeyframes) { 564 return new FloatPropertyValuesHolder(property, (Keyframes.FloatKeyframes) keyframes); 565 } else { 566 PropertyValuesHolder pvh = new PropertyValuesHolder(property); 567 pvh.mKeyframes = keyframes; 568 pvh.mValueType = keyframes.getType(); 569 return pvh; 570 } 571 } 572 573 /** 574 * Set the animated values for this object to this set of ints. 575 * If there is only one value, it is assumed to be the end value of an animation, 576 * and an initial value will be derived, if possible, by calling a getter function 577 * on the object. Also, if any value is null, the value will be filled in when the animation 578 * starts in the same way. This mechanism of automatically getting null values only works 579 * if the PropertyValuesHolder object is used in conjunction 580 * {@link ObjectAnimator}, and with a getter function 581 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has 582 * no way of determining what the value should be. 583 * 584 * @param values One or more values that the animation will animate between. 585 */ setIntValues(int... values)586 public void setIntValues(int... values) { 587 mValueType = int.class; 588 mKeyframes = KeyframeSet.ofInt(values); 589 } 590 591 /** 592 * Set the animated values for this object to this set of floats. 593 * If there is only one value, it is assumed to be the end value of an animation, 594 * and an initial value will be derived, if possible, by calling a getter function 595 * on the object. Also, if any value is null, the value will be filled in when the animation 596 * starts in the same way. This mechanism of automatically getting null values only works 597 * if the PropertyValuesHolder object is used in conjunction 598 * {@link ObjectAnimator}, and with a getter function 599 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has 600 * no way of determining what the value should be. 601 * 602 * @param values One or more values that the animation will animate between. 603 */ setFloatValues(float... values)604 public void setFloatValues(float... values) { 605 mValueType = float.class; 606 mKeyframes = KeyframeSet.ofFloat(values); 607 } 608 609 /** 610 * Set the animated values for this object to this set of Keyframes. 611 * 612 * @param values One or more values that the animation will animate between. 613 */ setKeyframes(Keyframe... values)614 public void setKeyframes(Keyframe... values) { 615 int numKeyframes = values.length; 616 Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)]; 617 mValueType = ((Keyframe)values[0]).getType(); 618 for (int i = 0; i < numKeyframes; ++i) { 619 keyframes[i] = (Keyframe)values[i]; 620 } 621 mKeyframes = new KeyframeSet(keyframes); 622 } 623 624 /** 625 * Set the animated values for this object to this set of Objects. 626 * If there is only one value, it is assumed to be the end value of an animation, 627 * and an initial value will be derived, if possible, by calling a getter function 628 * on the object. Also, if any value is null, the value will be filled in when the animation 629 * starts in the same way. This mechanism of automatically getting null values only works 630 * if the PropertyValuesHolder object is used in conjunction 631 * {@link ObjectAnimator}, and with a getter function 632 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has 633 * no way of determining what the value should be. 634 * 635 * @param values One or more values that the animation will animate between. 636 */ setObjectValues(Object... values)637 public void setObjectValues(Object... values) { 638 mValueType = values[0].getClass(); 639 mKeyframes = KeyframeSet.ofObject(values); 640 if (mEvaluator != null) { 641 mKeyframes.setEvaluator(mEvaluator); 642 } 643 } 644 645 /** 646 * Sets the converter to convert from the values type to the setter's parameter type. 647 * If only one value is supplied, <var>converter</var> must be a 648 * {@link android.animation.BidirectionalTypeConverter}. 649 * @param converter The converter to use to convert values. 650 */ setConverter(TypeConverter converter)651 public void setConverter(TypeConverter converter) { 652 mConverter = converter; 653 } 654 655 /** 656 * Determine the setter or getter function using the JavaBeans convention of setFoo or 657 * getFoo for a property named 'foo'. This function figures out what the name of the 658 * function should be and uses reflection to find the Method with that name on the 659 * target object. 660 * 661 * @param targetClass The class to search for the method 662 * @param prefix "set" or "get", depending on whether we need a setter or getter. 663 * @param valueType The type of the parameter (in the case of a setter). This type 664 * is derived from the values set on this PropertyValuesHolder. This type is used as 665 * a first guess at the parameter type, but we check for methods with several different 666 * types to avoid problems with slight mis-matches between supplied values and actual 667 * value types used on the setter. 668 * @return Method the method associated with mPropertyName. 669 */ getPropertyFunction(Class targetClass, String prefix, Class valueType)670 private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) { 671 // TODO: faster implementation... 672 Method returnVal = null; 673 String methodName = getMethodName(prefix, mPropertyName); 674 Class args[] = null; 675 if (valueType == null) { 676 try { 677 returnVal = targetClass.getMethod(methodName, args); 678 } catch (NoSuchMethodException e) { 679 // Swallow the error, log it later 680 } 681 } else { 682 args = new Class[1]; 683 Class typeVariants[]; 684 if (valueType.equals(Float.class)) { 685 typeVariants = FLOAT_VARIANTS; 686 } else if (valueType.equals(Integer.class)) { 687 typeVariants = INTEGER_VARIANTS; 688 } else if (valueType.equals(Double.class)) { 689 typeVariants = DOUBLE_VARIANTS; 690 } else { 691 typeVariants = new Class[1]; 692 typeVariants[0] = valueType; 693 } 694 for (Class typeVariant : typeVariants) { 695 args[0] = typeVariant; 696 try { 697 returnVal = targetClass.getMethod(methodName, args); 698 if (mConverter == null) { 699 // change the value type to suit 700 mValueType = typeVariant; 701 } 702 return returnVal; 703 } catch (NoSuchMethodException e) { 704 // Swallow the error and keep trying other variants 705 } 706 } 707 // If we got here, then no appropriate function was found 708 } 709 710 if (returnVal == null) { 711 Log.w("PropertyValuesHolder", "Method " + 712 getMethodName(prefix, mPropertyName) + "() with type " + valueType + 713 " not found on target class " + targetClass); 714 } 715 716 return returnVal; 717 } 718 719 720 /** 721 * Returns the setter or getter requested. This utility function checks whether the 722 * requested method exists in the propertyMapMap cache. If not, it calls another 723 * utility function to request the Method from the targetClass directly. 724 * @param targetClass The Class on which the requested method should exist. 725 * @param propertyMapMap The cache of setters/getters derived so far. 726 * @param prefix "set" or "get", for the setter or getter. 727 * @param valueType The type of parameter passed into the method (null for getter). 728 * @return Method the method associated with mPropertyName. 729 */ setupSetterOrGetter(Class targetClass, HashMap<Class, HashMap<String, Method>> propertyMapMap, String prefix, Class valueType)730 private Method setupSetterOrGetter(Class targetClass, 731 HashMap<Class, HashMap<String, Method>> propertyMapMap, 732 String prefix, Class valueType) { 733 Method setterOrGetter = null; 734 synchronized(propertyMapMap) { 735 // Have to lock property map prior to reading it, to guard against 736 // another thread putting something in there after we've checked it 737 // but before we've added an entry to it 738 HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass); 739 boolean wasInMap = false; 740 if (propertyMap != null) { 741 wasInMap = propertyMap.containsKey(mPropertyName); 742 if (wasInMap) { 743 setterOrGetter = propertyMap.get(mPropertyName); 744 } 745 } 746 if (!wasInMap) { 747 setterOrGetter = getPropertyFunction(targetClass, prefix, valueType); 748 if (propertyMap == null) { 749 propertyMap = new HashMap<String, Method>(); 750 propertyMapMap.put(targetClass, propertyMap); 751 } 752 propertyMap.put(mPropertyName, setterOrGetter); 753 } 754 } 755 return setterOrGetter; 756 } 757 758 /** 759 * Utility function to get the setter from targetClass 760 * @param targetClass The Class on which the requested method should exist. 761 */ setupSetter(Class targetClass)762 void setupSetter(Class targetClass) { 763 Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType(); 764 mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType); 765 } 766 767 /** 768 * Utility function to get the getter from targetClass 769 */ setupGetter(Class targetClass)770 private void setupGetter(Class targetClass) { 771 mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null); 772 } 773 774 /** 775 * Internal function (called from ObjectAnimator) to set up the setter and getter 776 * prior to running the animation. If the setter has not been manually set for this 777 * object, it will be derived automatically given the property name, target object, and 778 * types of values supplied. If no getter has been set, it will be supplied iff any of the 779 * supplied values was null. If there is a null value, then the getter (supplied or derived) 780 * will be called to set those null values to the current value of the property 781 * on the target object. 782 * @param target The object on which the setter (and possibly getter) exist. 783 */ setupSetterAndGetter(Object target)784 void setupSetterAndGetter(Object target) { 785 mKeyframes.invalidateCache(); 786 if (mProperty != null) { 787 // check to make sure that mProperty is on the class of target 788 try { 789 Object testValue = null; 790 List<Keyframe> keyframes = mKeyframes.getKeyframes(); 791 int keyframeCount = keyframes == null ? 0 : keyframes.size(); 792 for (int i = 0; i < keyframeCount; i++) { 793 Keyframe kf = keyframes.get(i); 794 if (!kf.hasValue() || kf.valueWasSetOnStart()) { 795 if (testValue == null) { 796 testValue = convertBack(mProperty.get(target)); 797 } 798 kf.setValue(testValue); 799 kf.setValueWasSetOnStart(true); 800 } 801 } 802 return; 803 } catch (ClassCastException e) { 804 Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() + 805 ") on target object " + target + ". Trying reflection instead"); 806 mProperty = null; 807 } 808 } 809 // We can't just say 'else' here because the catch statement sets mProperty to null. 810 if (mProperty == null) { 811 Class targetClass = target.getClass(); 812 if (mSetter == null) { 813 setupSetter(targetClass); 814 } 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 (mGetter == null) { 821 setupGetter(targetClass); 822 if (mGetter == null) { 823 // Already logged the error - just return to avoid NPE 824 return; 825 } 826 } 827 try { 828 Object value = convertBack(mGetter.invoke(target)); 829 kf.setValue(value); 830 kf.setValueWasSetOnStart(true); 831 } catch (InvocationTargetException e) { 832 Log.e("PropertyValuesHolder", e.toString()); 833 } catch (IllegalAccessException e) { 834 Log.e("PropertyValuesHolder", e.toString()); 835 } 836 } 837 } 838 } 839 } 840 convertBack(Object value)841 private Object convertBack(Object value) { 842 if (mConverter != null) { 843 if (!(mConverter instanceof BidirectionalTypeConverter)) { 844 throw new IllegalArgumentException("Converter " 845 + mConverter.getClass().getName() 846 + " must be a BidirectionalTypeConverter"); 847 } 848 value = ((BidirectionalTypeConverter) mConverter).convertBack(value); 849 } 850 return value; 851 } 852 853 /** 854 * Utility function to set the value stored in a particular Keyframe. The value used is 855 * whatever the value is for the property name specified in the keyframe on the target object. 856 * 857 * @param target The target object from which the current value should be extracted. 858 * @param kf The keyframe which holds the property name and value. 859 */ setupValue(Object target, Keyframe kf)860 private void setupValue(Object target, Keyframe kf) { 861 if (mProperty != null) { 862 Object value = convertBack(mProperty.get(target)); 863 kf.setValue(value); 864 } 865 try { 866 if (mGetter == null) { 867 Class targetClass = target.getClass(); 868 setupGetter(targetClass); 869 if (mGetter == null) { 870 // Already logged the error - just return to avoid NPE 871 return; 872 } 873 } 874 Object value = convertBack(mGetter.invoke(target)); 875 kf.setValue(value); 876 } catch (InvocationTargetException e) { 877 Log.e("PropertyValuesHolder", e.toString()); 878 } catch (IllegalAccessException e) { 879 Log.e("PropertyValuesHolder", e.toString()); 880 } 881 } 882 883 /** 884 * This function is called by ObjectAnimator when setting the start values for an animation. 885 * The start values are set according to the current values in the target object. The 886 * property whose value is extracted is whatever is specified by the propertyName of this 887 * PropertyValuesHolder object. 888 * 889 * @param target The object which holds the start values that should be set. 890 */ setupStartValue(Object target)891 void setupStartValue(Object target) { 892 List<Keyframe> keyframes = mKeyframes.getKeyframes(); 893 if (!keyframes.isEmpty()) { 894 setupValue(target, keyframes.get(0)); 895 } 896 } 897 898 /** 899 * This function is called by ObjectAnimator when setting the end values for an animation. 900 * The end values are set according to the current values in the target object. The 901 * property whose value is extracted is whatever is specified by the propertyName of this 902 * PropertyValuesHolder object. 903 * 904 * @param target The object which holds the start values that should be set. 905 */ setupEndValue(Object target)906 void setupEndValue(Object target) { 907 List<Keyframe> keyframes = mKeyframes.getKeyframes(); 908 if (!keyframes.isEmpty()) { 909 setupValue(target, keyframes.get(keyframes.size() - 1)); 910 } 911 } 912 913 @Override clone()914 public PropertyValuesHolder clone() { 915 try { 916 PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone(); 917 newPVH.mPropertyName = mPropertyName; 918 newPVH.mProperty = mProperty; 919 newPVH.mKeyframes = mKeyframes.clone(); 920 newPVH.mEvaluator = mEvaluator; 921 return newPVH; 922 } catch (CloneNotSupportedException e) { 923 // won't reach here 924 return null; 925 } 926 } 927 928 /** 929 * Internal function to set the value on the target object, using the setter set up 930 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 931 * to handle turning the value calculated by ValueAnimator into a value set on the object 932 * according to the name of the property. 933 * @param target The target object on which the value is set 934 */ setAnimatedValue(Object target)935 void setAnimatedValue(Object target) { 936 if (mProperty != null) { 937 mProperty.set(target, getAnimatedValue()); 938 } 939 if (mSetter != null) { 940 try { 941 mTmpValueArray[0] = getAnimatedValue(); 942 mSetter.invoke(target, mTmpValueArray); 943 } catch (InvocationTargetException e) { 944 Log.e("PropertyValuesHolder", e.toString()); 945 } catch (IllegalAccessException e) { 946 Log.e("PropertyValuesHolder", e.toString()); 947 } 948 } 949 } 950 951 /** 952 * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used 953 * to calculate animated values. 954 */ init()955 void init() { 956 if (mEvaluator == null) { 957 // We already handle int and float automatically, but not their Object 958 // equivalents 959 mEvaluator = (mValueType == Integer.class) ? sIntEvaluator : 960 (mValueType == Float.class) ? sFloatEvaluator : 961 null; 962 } 963 if (mEvaluator != null) { 964 // KeyframeSet knows how to evaluate the common types - only give it a custom 965 // evaluator if one has been set on this class 966 mKeyframes.setEvaluator(mEvaluator); 967 } 968 } 969 970 /** 971 * The TypeEvaluator will be automatically determined based on the type of values 972 * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so 973 * desired. This may be important in cases where either the type of the values supplied 974 * do not match the way that they should be interpolated between, or if the values 975 * are of a custom type or one not currently understood by the animation system. Currently, 976 * only values of type float and int (and their Object equivalents: Float 977 * and Integer) are correctly interpolated; all other types require setting a TypeEvaluator. 978 * @param evaluator 979 */ setEvaluator(TypeEvaluator evaluator)980 public void setEvaluator(TypeEvaluator evaluator) { 981 mEvaluator = evaluator; 982 mKeyframes.setEvaluator(evaluator); 983 } 984 985 /** 986 * Function used to calculate the value according to the evaluator set up for 987 * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue(). 988 * 989 * @param fraction The elapsed, interpolated fraction of the animation. 990 */ calculateValue(float fraction)991 void calculateValue(float fraction) { 992 Object value = mKeyframes.getValue(fraction); 993 mAnimatedValue = mConverter == null ? value : mConverter.convert(value); 994 } 995 996 /** 997 * Sets the name of the property that will be animated. This name is used to derive 998 * a setter function that will be called to set animated values. 999 * For example, a property name of <code>foo</code> will result 1000 * in a call to the function <code>setFoo()</code> on the target object. If either 1001 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 1002 * also be derived and called. 1003 * 1004 * <p>Note that the setter function derived from this property name 1005 * must take the same parameter type as the 1006 * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to 1007 * the setter function will fail.</p> 1008 * 1009 * @param propertyName The name of the property being animated. 1010 */ setPropertyName(String propertyName)1011 public void setPropertyName(String propertyName) { 1012 mPropertyName = propertyName; 1013 } 1014 1015 /** 1016 * Sets the property that will be animated. 1017 * 1018 * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property 1019 * must exist on the target object specified in that ObjectAnimator.</p> 1020 * 1021 * @param property The property being animated. 1022 */ setProperty(Property property)1023 public void setProperty(Property property) { 1024 mProperty = property; 1025 } 1026 1027 /** 1028 * Gets the name of the property that will be animated. This name will be used to derive 1029 * a setter function that will be called to set animated values. 1030 * For example, a property name of <code>foo</code> will result 1031 * in a call to the function <code>setFoo()</code> on the target object. If either 1032 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 1033 * also be derived and called. 1034 */ getPropertyName()1035 public String getPropertyName() { 1036 return mPropertyName; 1037 } 1038 1039 /** 1040 * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value 1041 * most recently calculated in calculateValue(). 1042 * @return 1043 */ getAnimatedValue()1044 Object getAnimatedValue() { 1045 return mAnimatedValue; 1046 } 1047 1048 @Override toString()1049 public String toString() { 1050 return mPropertyName + ": " + mKeyframes.toString(); 1051 } 1052 1053 /** 1054 * Utility method to derive a setter/getter method name from a property name, where the 1055 * prefix is typically "set" or "get" and the first letter of the property name is 1056 * capitalized. 1057 * 1058 * @param prefix The precursor to the method name, before the property name begins, typically 1059 * "set" or "get". 1060 * @param propertyName The name of the property that represents the bulk of the method name 1061 * after the prefix. The first letter of this word will be capitalized in the resulting 1062 * method name. 1063 * @return String the property name converted to a method name according to the conventions 1064 * specified above. 1065 */ getMethodName(String prefix, String propertyName)1066 static String getMethodName(String prefix, String propertyName) { 1067 if (propertyName == null || propertyName.length() == 0) { 1068 // shouldn't get here 1069 return prefix; 1070 } 1071 char firstLetter = Character.toUpperCase(propertyName.charAt(0)); 1072 String theRest = propertyName.substring(1); 1073 return prefix + firstLetter + theRest; 1074 } 1075 1076 static class IntPropertyValuesHolder extends PropertyValuesHolder { 1077 1078 // Cache JNI functions to avoid looking them up twice 1079 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap = 1080 new HashMap<Class, HashMap<String, Long>>(); 1081 long mJniSetter; 1082 private IntProperty mIntProperty; 1083 1084 Keyframes.IntKeyframes mIntKeyframes; 1085 int mIntAnimatedValue; 1086 IntPropertyValuesHolder(String propertyName, Keyframes.IntKeyframes keyframes)1087 public IntPropertyValuesHolder(String propertyName, Keyframes.IntKeyframes keyframes) { 1088 super(propertyName); 1089 mValueType = int.class; 1090 mKeyframes = keyframes; 1091 mIntKeyframes = keyframes; 1092 } 1093 IntPropertyValuesHolder(Property property, Keyframes.IntKeyframes keyframes)1094 public IntPropertyValuesHolder(Property property, Keyframes.IntKeyframes keyframes) { 1095 super(property); 1096 mValueType = int.class; 1097 mKeyframes = keyframes; 1098 mIntKeyframes = keyframes; 1099 if (property instanceof IntProperty) { 1100 mIntProperty = (IntProperty) mProperty; 1101 } 1102 } 1103 IntPropertyValuesHolder(String propertyName, int... values)1104 public IntPropertyValuesHolder(String propertyName, int... values) { 1105 super(propertyName); 1106 setIntValues(values); 1107 } 1108 IntPropertyValuesHolder(Property property, int... values)1109 public IntPropertyValuesHolder(Property property, int... values) { 1110 super(property); 1111 setIntValues(values); 1112 if (property instanceof IntProperty) { 1113 mIntProperty = (IntProperty) mProperty; 1114 } 1115 } 1116 1117 @Override setIntValues(int... values)1118 public void setIntValues(int... values) { 1119 super.setIntValues(values); 1120 mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes; 1121 } 1122 1123 @Override calculateValue(float fraction)1124 void calculateValue(float fraction) { 1125 mIntAnimatedValue = mIntKeyframes.getIntValue(fraction); 1126 } 1127 1128 @Override getAnimatedValue()1129 Object getAnimatedValue() { 1130 return mIntAnimatedValue; 1131 } 1132 1133 @Override clone()1134 public IntPropertyValuesHolder clone() { 1135 IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone(); 1136 newPVH.mIntKeyframes = (Keyframes.IntKeyframes) newPVH.mKeyframes; 1137 return newPVH; 1138 } 1139 1140 /** 1141 * Internal function to set the value on the target object, using the setter set up 1142 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 1143 * to handle turning the value calculated by ValueAnimator into a value set on the object 1144 * according to the name of the property. 1145 * @param target The target object on which the value is set 1146 */ 1147 @Override setAnimatedValue(Object target)1148 void setAnimatedValue(Object target) { 1149 if (mIntProperty != null) { 1150 mIntProperty.setValue(target, mIntAnimatedValue); 1151 return; 1152 } 1153 if (mProperty != null) { 1154 mProperty.set(target, mIntAnimatedValue); 1155 return; 1156 } 1157 if (mJniSetter != 0) { 1158 nCallIntMethod(target, mJniSetter, mIntAnimatedValue); 1159 return; 1160 } 1161 if (mSetter != null) { 1162 try { 1163 mTmpValueArray[0] = mIntAnimatedValue; 1164 mSetter.invoke(target, mTmpValueArray); 1165 } catch (InvocationTargetException e) { 1166 Log.e("PropertyValuesHolder", e.toString()); 1167 } catch (IllegalAccessException e) { 1168 Log.e("PropertyValuesHolder", e.toString()); 1169 } 1170 } 1171 } 1172 1173 @Override setupSetter(Class targetClass)1174 void setupSetter(Class targetClass) { 1175 if (mProperty != null) { 1176 return; 1177 } 1178 // Check new static hashmap<propName, int> for setter method 1179 synchronized(sJNISetterPropertyMap) { 1180 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); 1181 boolean wasInMap = false; 1182 if (propertyMap != null) { 1183 wasInMap = propertyMap.containsKey(mPropertyName); 1184 if (wasInMap) { 1185 Long jniSetter = propertyMap.get(mPropertyName); 1186 if (jniSetter != null) { 1187 mJniSetter = jniSetter; 1188 } 1189 } 1190 } 1191 if (!wasInMap) { 1192 String methodName = getMethodName("set", mPropertyName); 1193 try { 1194 mJniSetter = nGetIntMethod(targetClass, methodName); 1195 } catch (NoSuchMethodError e) { 1196 // Couldn't find it via JNI - try reflection next. Probably means the method 1197 // doesn't exist, or the type is wrong. An error will be logged later if 1198 // reflection fails as well. 1199 } 1200 if (propertyMap == null) { 1201 propertyMap = new HashMap<String, Long>(); 1202 sJNISetterPropertyMap.put(targetClass, propertyMap); 1203 } 1204 propertyMap.put(mPropertyName, mJniSetter); 1205 } 1206 } 1207 if (mJniSetter == 0) { 1208 // Couldn't find method through fast JNI approach - just use reflection 1209 super.setupSetter(targetClass); 1210 } 1211 } 1212 } 1213 1214 static class FloatPropertyValuesHolder extends PropertyValuesHolder { 1215 1216 // Cache JNI functions to avoid looking them up twice 1217 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap = 1218 new HashMap<Class, HashMap<String, Long>>(); 1219 long mJniSetter; 1220 private FloatProperty mFloatProperty; 1221 1222 Keyframes.FloatKeyframes mFloatKeyframes; 1223 float mFloatAnimatedValue; 1224 FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes)1225 public FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes) { 1226 super(propertyName); 1227 mValueType = float.class; 1228 mKeyframes = keyframes; 1229 mFloatKeyframes = keyframes; 1230 } 1231 FloatPropertyValuesHolder(Property property, Keyframes.FloatKeyframes keyframes)1232 public FloatPropertyValuesHolder(Property property, Keyframes.FloatKeyframes keyframes) { 1233 super(property); 1234 mValueType = float.class; 1235 mKeyframes = keyframes; 1236 mFloatKeyframes = keyframes; 1237 if (property instanceof FloatProperty) { 1238 mFloatProperty = (FloatProperty) mProperty; 1239 } 1240 } 1241 FloatPropertyValuesHolder(String propertyName, float... values)1242 public FloatPropertyValuesHolder(String propertyName, float... values) { 1243 super(propertyName); 1244 setFloatValues(values); 1245 } 1246 FloatPropertyValuesHolder(Property property, float... values)1247 public FloatPropertyValuesHolder(Property property, float... values) { 1248 super(property); 1249 setFloatValues(values); 1250 if (property instanceof FloatProperty) { 1251 mFloatProperty = (FloatProperty) mProperty; 1252 } 1253 } 1254 1255 @Override setFloatValues(float... values)1256 public void setFloatValues(float... values) { 1257 super.setFloatValues(values); 1258 mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes; 1259 } 1260 1261 @Override calculateValue(float fraction)1262 void calculateValue(float fraction) { 1263 mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction); 1264 } 1265 1266 @Override getAnimatedValue()1267 Object getAnimatedValue() { 1268 return mFloatAnimatedValue; 1269 } 1270 1271 @Override clone()1272 public FloatPropertyValuesHolder clone() { 1273 FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone(); 1274 newPVH.mFloatKeyframes = (Keyframes.FloatKeyframes) newPVH.mKeyframes; 1275 return newPVH; 1276 } 1277 1278 /** 1279 * Internal function to set the value on the target object, using the setter set up 1280 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 1281 * to handle turning the value calculated by ValueAnimator into a value set on the object 1282 * according to the name of the property. 1283 * @param target The target object on which the value is set 1284 */ 1285 @Override setAnimatedValue(Object target)1286 void setAnimatedValue(Object target) { 1287 if (mFloatProperty != null) { 1288 mFloatProperty.setValue(target, mFloatAnimatedValue); 1289 return; 1290 } 1291 if (mProperty != null) { 1292 mProperty.set(target, mFloatAnimatedValue); 1293 return; 1294 } 1295 if (mJniSetter != 0) { 1296 nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue); 1297 return; 1298 } 1299 if (mSetter != null) { 1300 try { 1301 mTmpValueArray[0] = mFloatAnimatedValue; 1302 mSetter.invoke(target, mTmpValueArray); 1303 } catch (InvocationTargetException e) { 1304 Log.e("PropertyValuesHolder", e.toString()); 1305 } catch (IllegalAccessException e) { 1306 Log.e("PropertyValuesHolder", e.toString()); 1307 } 1308 } 1309 } 1310 1311 @Override setupSetter(Class targetClass)1312 void setupSetter(Class targetClass) { 1313 if (mProperty != null) { 1314 return; 1315 } 1316 // Check new static hashmap<propName, int> for setter method 1317 synchronized (sJNISetterPropertyMap) { 1318 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); 1319 boolean wasInMap = false; 1320 if (propertyMap != null) { 1321 wasInMap = propertyMap.containsKey(mPropertyName); 1322 if (wasInMap) { 1323 Long jniSetter = propertyMap.get(mPropertyName); 1324 if (jniSetter != null) { 1325 mJniSetter = jniSetter; 1326 } 1327 } 1328 } 1329 if (!wasInMap) { 1330 String methodName = getMethodName("set", mPropertyName); 1331 try { 1332 mJniSetter = nGetFloatMethod(targetClass, methodName); 1333 } catch (NoSuchMethodError e) { 1334 // Couldn't find it via JNI - try reflection next. Probably means the method 1335 // doesn't exist, or the type is wrong. An error will be logged later if 1336 // reflection fails as well. 1337 } 1338 if (propertyMap == null) { 1339 propertyMap = new HashMap<String, Long>(); 1340 sJNISetterPropertyMap.put(targetClass, propertyMap); 1341 } 1342 propertyMap.put(mPropertyName, mJniSetter); 1343 } 1344 } 1345 if (mJniSetter == 0) { 1346 // Couldn't find method through fast JNI approach - just use reflection 1347 super.setupSetter(targetClass); 1348 } 1349 } 1350 1351 } 1352 1353 static class MultiFloatValuesHolder extends PropertyValuesHolder { 1354 private long mJniSetter; 1355 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap = 1356 new HashMap<Class, HashMap<String, Long>>(); 1357 MultiFloatValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Object... values)1358 public MultiFloatValuesHolder(String propertyName, TypeConverter converter, 1359 TypeEvaluator evaluator, Object... values) { 1360 super(propertyName); 1361 setConverter(converter); 1362 setObjectValues(values); 1363 setEvaluator(evaluator); 1364 } 1365 MultiFloatValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Keyframes keyframes)1366 public MultiFloatValuesHolder(String propertyName, TypeConverter converter, 1367 TypeEvaluator evaluator, Keyframes keyframes) { 1368 super(propertyName); 1369 setConverter(converter); 1370 mKeyframes = keyframes; 1371 setEvaluator(evaluator); 1372 } 1373 1374 /** 1375 * Internal function to set the value on the target object, using the setter set up 1376 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 1377 * to handle turning the value calculated by ValueAnimator into a value set on the object 1378 * according to the name of the property. 1379 * 1380 * @param target The target object on which the value is set 1381 */ 1382 @Override setAnimatedValue(Object target)1383 void setAnimatedValue(Object target) { 1384 float[] values = (float[]) getAnimatedValue(); 1385 int numParameters = values.length; 1386 if (mJniSetter != 0) { 1387 switch (numParameters) { 1388 case 1: 1389 nCallFloatMethod(target, mJniSetter, values[0]); 1390 break; 1391 case 2: 1392 nCallTwoFloatMethod(target, mJniSetter, values[0], values[1]); 1393 break; 1394 case 4: 1395 nCallFourFloatMethod(target, mJniSetter, values[0], values[1], 1396 values[2], values[3]); 1397 break; 1398 default: { 1399 nCallMultipleFloatMethod(target, mJniSetter, values); 1400 break; 1401 } 1402 } 1403 } 1404 } 1405 1406 /** 1407 * Internal function (called from ObjectAnimator) to set up the setter and getter 1408 * prior to running the animation. No getter can be used for multiple parameters. 1409 * 1410 * @param target The object on which the setter exists. 1411 */ 1412 @Override setupSetterAndGetter(Object target)1413 void setupSetterAndGetter(Object target) { 1414 setupSetter(target.getClass()); 1415 } 1416 1417 @Override setupSetter(Class targetClass)1418 void setupSetter(Class targetClass) { 1419 if (mJniSetter != 0) { 1420 return; 1421 } 1422 synchronized(sJNISetterPropertyMap) { 1423 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); 1424 boolean wasInMap = false; 1425 if (propertyMap != null) { 1426 wasInMap = propertyMap.containsKey(mPropertyName); 1427 if (wasInMap) { 1428 Long jniSetter = propertyMap.get(mPropertyName); 1429 if (jniSetter != null) { 1430 mJniSetter = jniSetter; 1431 } 1432 } 1433 } 1434 if (!wasInMap) { 1435 String methodName = getMethodName("set", mPropertyName); 1436 calculateValue(0f); 1437 float[] values = (float[]) getAnimatedValue(); 1438 int numParams = values.length; 1439 try { 1440 mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams); 1441 } catch (NoSuchMethodError e) { 1442 // try without the 'set' prefix 1443 try { 1444 mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName, 1445 numParams); 1446 } catch (NoSuchMethodError e2) { 1447 // just try reflection next 1448 } 1449 } 1450 if (propertyMap == null) { 1451 propertyMap = new HashMap<String, Long>(); 1452 sJNISetterPropertyMap.put(targetClass, propertyMap); 1453 } 1454 propertyMap.put(mPropertyName, mJniSetter); 1455 } 1456 } 1457 } 1458 } 1459 1460 static class MultiIntValuesHolder extends PropertyValuesHolder { 1461 private long mJniSetter; 1462 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap = 1463 new HashMap<Class, HashMap<String, Long>>(); 1464 MultiIntValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Object... values)1465 public MultiIntValuesHolder(String propertyName, TypeConverter converter, 1466 TypeEvaluator evaluator, Object... values) { 1467 super(propertyName); 1468 setConverter(converter); 1469 setObjectValues(values); 1470 setEvaluator(evaluator); 1471 } 1472 MultiIntValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Keyframes keyframes)1473 public MultiIntValuesHolder(String propertyName, TypeConverter converter, 1474 TypeEvaluator evaluator, Keyframes keyframes) { 1475 super(propertyName); 1476 setConverter(converter); 1477 mKeyframes = keyframes; 1478 setEvaluator(evaluator); 1479 } 1480 1481 /** 1482 * Internal function to set the value on the target object, using the setter set up 1483 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 1484 * to handle turning the value calculated by ValueAnimator into a value set on the object 1485 * according to the name of the property. 1486 * 1487 * @param target The target object on which the value is set 1488 */ 1489 @Override setAnimatedValue(Object target)1490 void setAnimatedValue(Object target) { 1491 int[] values = (int[]) getAnimatedValue(); 1492 int numParameters = values.length; 1493 if (mJniSetter != 0) { 1494 switch (numParameters) { 1495 case 1: 1496 nCallIntMethod(target, mJniSetter, values[0]); 1497 break; 1498 case 2: 1499 nCallTwoIntMethod(target, mJniSetter, values[0], values[1]); 1500 break; 1501 case 4: 1502 nCallFourIntMethod(target, mJniSetter, values[0], values[1], 1503 values[2], values[3]); 1504 break; 1505 default: { 1506 nCallMultipleIntMethod(target, mJniSetter, values); 1507 break; 1508 } 1509 } 1510 } 1511 } 1512 1513 /** 1514 * Internal function (called from ObjectAnimator) to set up the setter and getter 1515 * prior to running the animation. No getter can be used for multiple parameters. 1516 * 1517 * @param target The object on which the setter exists. 1518 */ 1519 @Override setupSetterAndGetter(Object target)1520 void setupSetterAndGetter(Object target) { 1521 setupSetter(target.getClass()); 1522 } 1523 1524 @Override setupSetter(Class targetClass)1525 void setupSetter(Class targetClass) { 1526 if (mJniSetter != 0) { 1527 return; 1528 } 1529 synchronized(sJNISetterPropertyMap) { 1530 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); 1531 boolean wasInMap = false; 1532 if (propertyMap != null) { 1533 wasInMap = propertyMap.containsKey(mPropertyName); 1534 if (wasInMap) { 1535 Long jniSetter = propertyMap.get(mPropertyName); 1536 if (jniSetter != null) { 1537 mJniSetter = jniSetter; 1538 } 1539 } 1540 } 1541 if (!wasInMap) { 1542 String methodName = getMethodName("set", mPropertyName); 1543 calculateValue(0f); 1544 int[] values = (int[]) getAnimatedValue(); 1545 int numParams = values.length; 1546 try { 1547 mJniSetter = nGetMultipleIntMethod(targetClass, methodName, numParams); 1548 } catch (NoSuchMethodError e) { 1549 // try without the 'set' prefix 1550 try { 1551 mJniSetter = nGetMultipleIntMethod(targetClass, mPropertyName, 1552 numParams); 1553 } catch (NoSuchMethodError e2) { 1554 // couldn't find it. 1555 } 1556 } 1557 if (propertyMap == null) { 1558 propertyMap = new HashMap<String, Long>(); 1559 sJNISetterPropertyMap.put(targetClass, propertyMap); 1560 } 1561 propertyMap.put(mPropertyName, mJniSetter); 1562 } 1563 } 1564 } 1565 } 1566 1567 /** 1568 * Convert from PointF to float[] for multi-float setters along a Path. 1569 */ 1570 private static class PointFToFloatArray extends TypeConverter<PointF, float[]> { 1571 private float[] mCoordinates = new float[2]; 1572 PointFToFloatArray()1573 public PointFToFloatArray() { 1574 super(PointF.class, float[].class); 1575 } 1576 1577 @Override convert(PointF value)1578 public float[] convert(PointF value) { 1579 mCoordinates[0] = value.x; 1580 mCoordinates[1] = value.y; 1581 return mCoordinates; 1582 } 1583 }; 1584 1585 /** 1586 * Convert from PointF to int[] for multi-int setters along a Path. 1587 */ 1588 private static class PointFToIntArray extends TypeConverter<PointF, int[]> { 1589 private int[] mCoordinates = new int[2]; 1590 PointFToIntArray()1591 public PointFToIntArray() { 1592 super(PointF.class, int[].class); 1593 } 1594 1595 @Override convert(PointF value)1596 public int[] convert(PointF value) { 1597 mCoordinates[0] = Math.round(value.x); 1598 mCoordinates[1] = Math.round(value.y); 1599 return mCoordinates; 1600 } 1601 }; 1602 nGetIntMethod(Class targetClass, String methodName)1603 native static private long nGetIntMethod(Class targetClass, String methodName); nGetFloatMethod(Class targetClass, String methodName)1604 native static private long nGetFloatMethod(Class targetClass, String methodName); nGetMultipleIntMethod(Class targetClass, String methodName, int numParams)1605 native static private long nGetMultipleIntMethod(Class targetClass, String methodName, 1606 int numParams); nGetMultipleFloatMethod(Class targetClass, String methodName, int numParams)1607 native static private long nGetMultipleFloatMethod(Class targetClass, String methodName, 1608 int numParams); nCallIntMethod(Object target, long methodID, int arg)1609 native static private void nCallIntMethod(Object target, long methodID, int arg); nCallFloatMethod(Object target, long methodID, float arg)1610 native static private void nCallFloatMethod(Object target, long methodID, float arg); nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2)1611 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)1612 native static private void nCallFourIntMethod(Object target, long methodID, int arg1, int arg2, 1613 int arg3, int arg4); nCallMultipleIntMethod(Object target, long methodID, int[] args)1614 native static private void nCallMultipleIntMethod(Object target, long methodID, int[] args); nCallTwoFloatMethod(Object target, long methodID, float arg1, float arg2)1615 native static private void nCallTwoFloatMethod(Object target, long methodID, float arg1, 1616 float arg2); nCallFourFloatMethod(Object target, long methodID, float arg1, float arg2, float arg3, float arg4)1617 native static private void nCallFourFloatMethod(Object target, long methodID, float arg1, 1618 float arg2, float arg3, float arg4); nCallMultipleFloatMethod(Object target, long methodID, float[] args)1619 native static private void nCallMultipleFloatMethod(Object target, long methodID, float[] args); 1620 } 1621