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