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