PropertyValuesHolder.java revision 16d2c9cc6bd67131d9921fbc14a69d88f48f48ca
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 float values. 175 * @param propertyName The name of the property being animated. 176 * @param values The values that the named property will animate between. 177 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 178 */ 179 public static PropertyValuesHolder ofFloat(String propertyName, float... values) { 180 return new FloatPropertyValuesHolder(propertyName, values); 181 } 182 183 /** 184 * Constructs and returns a PropertyValuesHolder with a given property and 185 * set of float values. 186 * @param property The property being animated. Should not be null. 187 * @param values The values that the property will animate between. 188 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 189 */ 190 public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) { 191 return new FloatPropertyValuesHolder(property, values); 192 } 193 194 /** 195 * Constructs and returns a PropertyValuesHolder with a given property name and 196 * set of Object values. This variant also takes a TypeEvaluator because the system 197 * cannot automatically interpolate between objects of unknown type. 198 * 199 * @param propertyName The name of the property being animated. 200 * @param evaluator A TypeEvaluator that will be called on each animation frame to 201 * provide the necessary interpolation between the Object values to derive the animated 202 * value. 203 * @param values The values that the named property will animate between. 204 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 205 */ 206 public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator, 207 Object... values) { 208 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); 209 pvh.setObjectValues(values); 210 pvh.setEvaluator(evaluator); 211 return pvh; 212 } 213 214 /** 215 * Constructs and returns a PropertyValuesHolder with a given property and 216 * set of Object values. This variant also takes a TypeEvaluator because the system 217 * cannot automatically interpolate between objects of unknown type. 218 * 219 * @param property The property being animated. Should not be null. 220 * @param evaluator A TypeEvaluator that will be called on each animation frame to 221 * provide the necessary interpolation between the Object values to derive the animated 222 * value. 223 * @param values The values that the property will animate between. 224 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 225 */ 226 public static <V> PropertyValuesHolder ofObject(Property property, 227 TypeEvaluator<V> evaluator, V... values) { 228 PropertyValuesHolder pvh = new PropertyValuesHolder(property); 229 pvh.setObjectValues(values); 230 pvh.setEvaluator(evaluator); 231 return pvh; 232 } 233 234 /** 235 * Constructs and returns a PropertyValuesHolder with a given property and 236 * set of Object values. This variant also takes a TypeEvaluator because the system 237 * cannot automatically interpolate between objects of unknown type. This variant also 238 * takes a <code>TypeConverter</code> to convert from animated values to the type 239 * of the property. If only one value is supplied, the <code>TypeConverter</code> 240 * must implement {@link TypeConverter#convertBack(Object)} to retrieve the current 241 * value. 242 * 243 * @param property The property being animated. Should not be null. 244 * @param converter Converts the animated object to the Property type. 245 * @param evaluator A TypeEvaluator that will be called on each animation frame to 246 * provide the necessary interpolation between the Object values to derive the animated 247 * value. 248 * @param values The values that the property will animate between. 249 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 250 * @see #setConverter(TypeConverter) 251 * @see TypeConverter 252 */ 253 public static <T, V> PropertyValuesHolder ofObject(Property<?, V> property, 254 TypeConverter<T, V> converter, TypeEvaluator<T> evaluator, T... values) { 255 PropertyValuesHolder pvh = new PropertyValuesHolder(property); 256 pvh.setConverter(converter); 257 pvh.setObjectValues(values); 258 pvh.setEvaluator(evaluator); 259 return pvh; 260 } 261 262 /** 263 * Constructs and returns a PropertyValuesHolder object with the specified property name and set 264 * of values. These values can be of any type, but the type should be consistent so that 265 * an appropriate {@link android.animation.TypeEvaluator} can be found that matches 266 * the common type. 267 * <p>If there is only one value, it is assumed to be the end value of an animation, 268 * and an initial value will be derived, if possible, by calling a getter function 269 * on the object. Also, if any value is null, the value will be filled in when the animation 270 * starts in the same way. This mechanism of automatically getting null values only works 271 * if the PropertyValuesHolder object is used in conjunction 272 * {@link ObjectAnimator}, and with a getter function 273 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has 274 * no way of determining what the value should be. 275 * @param propertyName The name of the property associated with this set of values. This 276 * can be the actual property name to be used when using a ObjectAnimator object, or 277 * just a name used to get animated values, such as if this object is used with an 278 * ValueAnimator object. 279 * @param values The set of values to animate between. 280 */ 281 public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) { 282 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); 283 if (keyframeSet instanceof IntKeyframeSet) { 284 return new IntPropertyValuesHolder(propertyName, (IntKeyframeSet) keyframeSet); 285 } else if (keyframeSet instanceof FloatKeyframeSet) { 286 return new FloatPropertyValuesHolder(propertyName, (FloatKeyframeSet) keyframeSet); 287 } 288 else { 289 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); 290 pvh.mKeyframeSet = keyframeSet; 291 pvh.mValueType = ((Keyframe)values[0]).getType(); 292 return pvh; 293 } 294 } 295 296 /** 297 * Constructs and returns a PropertyValuesHolder object with the specified property and set 298 * of values. These values can be of any type, but the type should be consistent so that 299 * an appropriate {@link android.animation.TypeEvaluator} can be found that matches 300 * the common type. 301 * <p>If there is only one value, it is assumed to be the end value of an animation, 302 * and an initial value will be derived, if possible, by calling the property's 303 * {@link android.util.Property#get(Object)} function. 304 * Also, if any value is null, the value will be filled in when the animation 305 * starts in the same way. This mechanism of automatically getting null values only works 306 * if the PropertyValuesHolder object is used in conjunction with 307 * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has 308 * no way of determining what the value should be. 309 * @param property The property associated with this set of values. Should not be null. 310 * @param values The set of values to animate between. 311 */ 312 public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) { 313 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); 314 if (keyframeSet instanceof IntKeyframeSet) { 315 return new IntPropertyValuesHolder(property, (IntKeyframeSet) keyframeSet); 316 } else if (keyframeSet instanceof FloatKeyframeSet) { 317 return new FloatPropertyValuesHolder(property, (FloatKeyframeSet) keyframeSet); 318 } 319 else { 320 PropertyValuesHolder pvh = new PropertyValuesHolder(property); 321 pvh.mKeyframeSet = keyframeSet; 322 pvh.mValueType = ((Keyframe)values[0]).getType(); 323 return pvh; 324 } 325 } 326 327 /** 328 * Set the animated values for this object to this set of ints. 329 * If there is only one value, it is assumed to be the end value of an animation, 330 * and an initial value will be derived, if possible, by calling a getter function 331 * on the object. Also, if any value is null, the value will be filled in when the animation 332 * starts in the same way. This mechanism of automatically getting null values only works 333 * if the PropertyValuesHolder object is used in conjunction 334 * {@link ObjectAnimator}, and with a getter function 335 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has 336 * no way of determining what the value should be. 337 * 338 * @param values One or more values that the animation will animate between. 339 */ 340 public void setIntValues(int... values) { 341 mValueType = int.class; 342 mKeyframeSet = KeyframeSet.ofInt(values); 343 } 344 345 /** 346 * Set the animated values for this object to this set of floats. 347 * If there is only one value, it is assumed to be the end value of an animation, 348 * and an initial value will be derived, if possible, by calling a getter function 349 * on the object. Also, if any value is null, the value will be filled in when the animation 350 * starts in the same way. This mechanism of automatically getting null values only works 351 * if the PropertyValuesHolder object is used in conjunction 352 * {@link ObjectAnimator}, and with a getter function 353 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has 354 * no way of determining what the value should be. 355 * 356 * @param values One or more values that the animation will animate between. 357 */ 358 public void setFloatValues(float... values) { 359 mValueType = float.class; 360 mKeyframeSet = KeyframeSet.ofFloat(values); 361 } 362 363 /** 364 * Set the animated values for this object to this set of Keyframes. 365 * 366 * @param values One or more values that the animation will animate between. 367 */ 368 public void setKeyframes(Keyframe... values) { 369 int numKeyframes = values.length; 370 Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)]; 371 mValueType = ((Keyframe)values[0]).getType(); 372 for (int i = 0; i < numKeyframes; ++i) { 373 keyframes[i] = (Keyframe)values[i]; 374 } 375 mKeyframeSet = new KeyframeSet(keyframes); 376 } 377 378 /** 379 * Set the animated values for this object to this set of Objects. 380 * If there is only one value, it is assumed to be the end value of an animation, 381 * and an initial value will be derived, if possible, by calling a getter function 382 * on the object. Also, if any value is null, the value will be filled in when the animation 383 * starts in the same way. This mechanism of automatically getting null values only works 384 * if the PropertyValuesHolder object is used in conjunction 385 * {@link ObjectAnimator}, and with a getter function 386 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has 387 * no way of determining what the value should be. 388 * 389 * @param values One or more values that the animation will animate between. 390 */ 391 public void setObjectValues(Object... values) { 392 mValueType = values[0].getClass(); 393 mKeyframeSet = KeyframeSet.ofObject(values); 394 } 395 396 /** 397 * Sets the converter to convert from the values type to the setter's parameter type. 398 * @param converter The converter to use to convert values. 399 */ 400 public void setConverter(TypeConverter converter) { 401 mConverter = converter; 402 } 403 404 /** 405 * Determine the setter or getter function using the JavaBeans convention of setFoo or 406 * getFoo for a property named 'foo'. This function figures out what the name of the 407 * function should be and uses reflection to find the Method with that name on the 408 * target object. 409 * 410 * @param targetClass The class to search for the method 411 * @param prefix "set" or "get", depending on whether we need a setter or getter. 412 * @param valueType The type of the parameter (in the case of a setter). This type 413 * is derived from the values set on this PropertyValuesHolder. This type is used as 414 * a first guess at the parameter type, but we check for methods with several different 415 * types to avoid problems with slight mis-matches between supplied values and actual 416 * value types used on the setter. 417 * @return Method the method associated with mPropertyName. 418 */ 419 private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) { 420 // TODO: faster implementation... 421 Method returnVal = null; 422 String methodName = getMethodName(prefix, mPropertyName); 423 Class args[] = null; 424 if (valueType == null) { 425 try { 426 returnVal = targetClass.getMethod(methodName, args); 427 } catch (NoSuchMethodException e) { 428 // Swallow the error, log it later 429 } 430 } else { 431 args = new Class[1]; 432 Class typeVariants[]; 433 if (valueType.equals(Float.class)) { 434 typeVariants = FLOAT_VARIANTS; 435 } else if (valueType.equals(Integer.class)) { 436 typeVariants = INTEGER_VARIANTS; 437 } else if (valueType.equals(Double.class)) { 438 typeVariants = DOUBLE_VARIANTS; 439 } else { 440 typeVariants = new Class[1]; 441 typeVariants[0] = valueType; 442 } 443 for (Class typeVariant : typeVariants) { 444 args[0] = typeVariant; 445 try { 446 returnVal = targetClass.getMethod(methodName, args); 447 if (mConverter == null) { 448 // change the value type to suit 449 mValueType = typeVariant; 450 } 451 return returnVal; 452 } catch (NoSuchMethodException e) { 453 // Swallow the error and keep trying other variants 454 } 455 } 456 // If we got here, then no appropriate function was found 457 } 458 459 if (returnVal == null) { 460 Log.w("PropertyValuesHolder", "Method " + 461 getMethodName(prefix, mPropertyName) + "() with type " + valueType + 462 " not found on target class " + targetClass); 463 } 464 465 return returnVal; 466 } 467 468 469 /** 470 * Returns the setter or getter requested. This utility function checks whether the 471 * requested method exists in the propertyMapMap cache. If not, it calls another 472 * utility function to request the Method from the targetClass directly. 473 * @param targetClass The Class on which the requested method should exist. 474 * @param propertyMapMap The cache of setters/getters derived so far. 475 * @param prefix "set" or "get", for the setter or getter. 476 * @param valueType The type of parameter passed into the method (null for getter). 477 * @return Method the method associated with mPropertyName. 478 */ 479 private Method setupSetterOrGetter(Class targetClass, 480 HashMap<Class, HashMap<String, Method>> propertyMapMap, 481 String prefix, Class valueType) { 482 Method setterOrGetter = null; 483 try { 484 // Have to lock property map prior to reading it, to guard against 485 // another thread putting something in there after we've checked it 486 // but before we've added an entry to it 487 mPropertyMapLock.writeLock().lock(); 488 HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass); 489 if (propertyMap != null) { 490 setterOrGetter = propertyMap.get(mPropertyName); 491 } 492 if (setterOrGetter == null) { 493 setterOrGetter = getPropertyFunction(targetClass, prefix, valueType); 494 if (propertyMap == null) { 495 propertyMap = new HashMap<String, Method>(); 496 propertyMapMap.put(targetClass, propertyMap); 497 } 498 propertyMap.put(mPropertyName, setterOrGetter); 499 } 500 } finally { 501 mPropertyMapLock.writeLock().unlock(); 502 } 503 return setterOrGetter; 504 } 505 506 /** 507 * Utility function to get the setter from targetClass 508 * @param targetClass The Class on which the requested method should exist. 509 */ 510 void setupSetter(Class targetClass) { 511 Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType(); 512 mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType); 513 } 514 515 /** 516 * Utility function to get the getter from targetClass 517 */ 518 private void setupGetter(Class targetClass) { 519 mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null); 520 } 521 522 /** 523 * Internal function (called from ObjectAnimator) to set up the setter and getter 524 * prior to running the animation. If the setter has not been manually set for this 525 * object, it will be derived automatically given the property name, target object, and 526 * types of values supplied. If no getter has been set, it will be supplied iff any of the 527 * supplied values was null. If there is a null value, then the getter (supplied or derived) 528 * will be called to set those null values to the current value of the property 529 * on the target object. 530 * @param target The object on which the setter (and possibly getter) exist. 531 */ 532 void setupSetterAndGetter(Object target) { 533 if (mProperty != null) { 534 // check to make sure that mProperty is on the class of target 535 try { 536 Object testValue = null; 537 for (Keyframe kf : mKeyframeSet.mKeyframes) { 538 if (!kf.hasValue()) { 539 if (testValue == null) { 540 testValue = convertBack(mProperty.get(target)); 541 } 542 kf.setValue(testValue); 543 } 544 } 545 return; 546 } catch (ClassCastException e) { 547 Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() + 548 ") on target object " + target + ". Trying reflection instead"); 549 mProperty = null; 550 } 551 } 552 Class targetClass = target.getClass(); 553 if (mSetter == null) { 554 setupSetter(targetClass); 555 } 556 for (Keyframe kf : mKeyframeSet.mKeyframes) { 557 if (!kf.hasValue()) { 558 if (mGetter == null) { 559 setupGetter(targetClass); 560 if (mGetter == null) { 561 // Already logged the error - just return to avoid NPE 562 return; 563 } 564 } 565 try { 566 Object value = convertBack(mGetter.invoke(target)); 567 kf.setValue(value); 568 } catch (InvocationTargetException e) { 569 Log.e("PropertyValuesHolder", e.toString()); 570 } catch (IllegalAccessException e) { 571 Log.e("PropertyValuesHolder", e.toString()); 572 } 573 } 574 } 575 } 576 577 private Object convertBack(Object value) { 578 if (mConverter != null) { 579 value = mConverter.convertBack(value); 580 if (value == null) { 581 throw new IllegalArgumentException("Converter " 582 + mConverter.getClass().getName() 583 + " must implement convertBack and not return null."); 584 } 585 } 586 return value; 587 } 588 589 /** 590 * Utility function to set the value stored in a particular Keyframe. The value used is 591 * whatever the value is for the property name specified in the keyframe on the target object. 592 * 593 * @param target The target object from which the current value should be extracted. 594 * @param kf The keyframe which holds the property name and value. 595 */ 596 private void setupValue(Object target, Keyframe kf) { 597 if (mProperty != null) { 598 Object value = convertBack(mProperty.get(target)); 599 kf.setValue(value); 600 } 601 try { 602 if (mGetter == null) { 603 Class targetClass = target.getClass(); 604 setupGetter(targetClass); 605 if (mGetter == null) { 606 // Already logged the error - just return to avoid NPE 607 return; 608 } 609 } 610 Object value = convertBack(mGetter.invoke(target)); 611 kf.setValue(value); 612 } catch (InvocationTargetException e) { 613 Log.e("PropertyValuesHolder", e.toString()); 614 } catch (IllegalAccessException e) { 615 Log.e("PropertyValuesHolder", e.toString()); 616 } 617 } 618 619 /** 620 * This function is called by ObjectAnimator when setting the start values for an animation. 621 * The start values are set according to the current values in the target object. The 622 * property whose value is extracted is whatever is specified by the propertyName of this 623 * PropertyValuesHolder object. 624 * 625 * @param target The object which holds the start values that should be set. 626 */ 627 void setupStartValue(Object target) { 628 setupValue(target, mKeyframeSet.mKeyframes.get(0)); 629 } 630 631 /** 632 * This function is called by ObjectAnimator when setting the end values for an animation. 633 * The end values are set according to the current values in the target object. The 634 * property whose value is extracted is whatever is specified by the propertyName of this 635 * PropertyValuesHolder object. 636 * 637 * @param target The object which holds the start values that should be set. 638 */ 639 void setupEndValue(Object target) { 640 setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1)); 641 } 642 643 @Override 644 public PropertyValuesHolder clone() { 645 try { 646 PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone(); 647 newPVH.mPropertyName = mPropertyName; 648 newPVH.mProperty = mProperty; 649 newPVH.mKeyframeSet = mKeyframeSet.clone(); 650 newPVH.mEvaluator = mEvaluator; 651 return newPVH; 652 } catch (CloneNotSupportedException e) { 653 // won't reach here 654 return null; 655 } 656 } 657 658 /** 659 * Internal function to set the value on the target object, using the setter set up 660 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 661 * to handle turning the value calculated by ValueAnimator into a value set on the object 662 * according to the name of the property. 663 * @param target The target object on which the value is set 664 */ 665 void setAnimatedValue(Object target) { 666 if (mProperty != null) { 667 mProperty.set(target, getAnimatedValue()); 668 } 669 if (mSetter != null) { 670 try { 671 mTmpValueArray[0] = getAnimatedValue(); 672 mSetter.invoke(target, mTmpValueArray); 673 } catch (InvocationTargetException e) { 674 Log.e("PropertyValuesHolder", e.toString()); 675 } catch (IllegalAccessException e) { 676 Log.e("PropertyValuesHolder", e.toString()); 677 } 678 } 679 } 680 681 /** 682 * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used 683 * to calculate animated values. 684 */ 685 void init() { 686 if (mEvaluator == null) { 687 // We already handle int and float automatically, but not their Object 688 // equivalents 689 mEvaluator = (mValueType == Integer.class) ? sIntEvaluator : 690 (mValueType == Float.class) ? sFloatEvaluator : 691 null; 692 } 693 if (mEvaluator != null) { 694 // KeyframeSet knows how to evaluate the common types - only give it a custom 695 // evaluator if one has been set on this class 696 mKeyframeSet.setEvaluator(mEvaluator); 697 } 698 } 699 700 /** 701 * The TypeEvaluator will be automatically determined based on the type of values 702 * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so 703 * desired. This may be important in cases where either the type of the values supplied 704 * do not match the way that they should be interpolated between, or if the values 705 * are of a custom type or one not currently understood by the animation system. Currently, 706 * only values of type float and int (and their Object equivalents: Float 707 * and Integer) are correctly interpolated; all other types require setting a TypeEvaluator. 708 * @param evaluator 709 */ 710 public void setEvaluator(TypeEvaluator evaluator) { 711 mEvaluator = evaluator; 712 mKeyframeSet.setEvaluator(evaluator); 713 } 714 715 /** 716 * Function used to calculate the value according to the evaluator set up for 717 * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue(). 718 * 719 * @param fraction The elapsed, interpolated fraction of the animation. 720 */ 721 void calculateValue(float fraction) { 722 Object value = mKeyframeSet.getValue(fraction); 723 mAnimatedValue = mConverter == null ? value : mConverter.convert(value); 724 } 725 726 /** 727 * Sets the name of the property that will be animated. This name is used to derive 728 * a setter function that will be called to set animated values. 729 * For example, a property name of <code>foo</code> will result 730 * in a call to the function <code>setFoo()</code> on the target object. If either 731 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 732 * also be derived and called. 733 * 734 * <p>Note that the setter function derived from this property name 735 * must take the same parameter type as the 736 * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to 737 * the setter function will fail.</p> 738 * 739 * @param propertyName The name of the property being animated. 740 */ 741 public void setPropertyName(String propertyName) { 742 mPropertyName = propertyName; 743 } 744 745 /** 746 * Sets the property that will be animated. 747 * 748 * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property 749 * must exist on the target object specified in that ObjectAnimator.</p> 750 * 751 * @param property The property being animated. 752 */ 753 public void setProperty(Property property) { 754 mProperty = property; 755 } 756 757 /** 758 * Gets the name of the property that will be animated. This name will be used to derive 759 * a setter function that will be called to set animated values. 760 * For example, a property name of <code>foo</code> will result 761 * in a call to the function <code>setFoo()</code> on the target object. If either 762 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 763 * also be derived and called. 764 */ 765 public String getPropertyName() { 766 return mPropertyName; 767 } 768 769 /** 770 * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value 771 * most recently calculated in calculateValue(). 772 * @return 773 */ 774 Object getAnimatedValue() { 775 return mAnimatedValue; 776 } 777 778 @Override 779 public String toString() { 780 return mPropertyName + ": " + mKeyframeSet.toString(); 781 } 782 783 /** 784 * Utility method to derive a setter/getter method name from a property name, where the 785 * prefix is typically "set" or "get" and the first letter of the property name is 786 * capitalized. 787 * 788 * @param prefix The precursor to the method name, before the property name begins, typically 789 * "set" or "get". 790 * @param propertyName The name of the property that represents the bulk of the method name 791 * after the prefix. The first letter of this word will be capitalized in the resulting 792 * method name. 793 * @return String the property name converted to a method name according to the conventions 794 * specified above. 795 */ 796 static String getMethodName(String prefix, String propertyName) { 797 if (propertyName == null || propertyName.length() == 0) { 798 // shouldn't get here 799 return prefix; 800 } 801 char firstLetter = Character.toUpperCase(propertyName.charAt(0)); 802 String theRest = propertyName.substring(1); 803 return prefix + firstLetter + theRest; 804 } 805 806 static class IntPropertyValuesHolder extends PropertyValuesHolder { 807 808 // Cache JNI functions to avoid looking them up twice 809 private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap = 810 new HashMap<Class, HashMap<String, Integer>>(); 811 int mJniSetter; 812 private IntProperty mIntProperty; 813 814 IntKeyframeSet mIntKeyframeSet; 815 int mIntAnimatedValue; 816 817 public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) { 818 super(propertyName); 819 mValueType = int.class; 820 mKeyframeSet = keyframeSet; 821 mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet; 822 } 823 824 public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) { 825 super(property); 826 mValueType = int.class; 827 mKeyframeSet = keyframeSet; 828 mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet; 829 if (property instanceof IntProperty) { 830 mIntProperty = (IntProperty) mProperty; 831 } 832 } 833 834 public IntPropertyValuesHolder(String propertyName, int... values) { 835 super(propertyName); 836 setIntValues(values); 837 } 838 839 public IntPropertyValuesHolder(Property property, int... values) { 840 super(property); 841 setIntValues(values); 842 if (property instanceof IntProperty) { 843 mIntProperty = (IntProperty) mProperty; 844 } 845 } 846 847 @Override 848 public void setIntValues(int... values) { 849 super.setIntValues(values); 850 mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet; 851 } 852 853 @Override 854 void calculateValue(float fraction) { 855 mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction); 856 } 857 858 @Override 859 Object getAnimatedValue() { 860 return mIntAnimatedValue; 861 } 862 863 @Override 864 public IntPropertyValuesHolder clone() { 865 IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone(); 866 newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet; 867 return newPVH; 868 } 869 870 /** 871 * Internal function to set the value on the target object, using the setter set up 872 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 873 * to handle turning the value calculated by ValueAnimator into a value set on the object 874 * according to the name of the property. 875 * @param target The target object on which the value is set 876 */ 877 @Override 878 void setAnimatedValue(Object target) { 879 if (mIntProperty != null) { 880 mIntProperty.setValue(target, mIntAnimatedValue); 881 return; 882 } 883 if (mProperty != null) { 884 mProperty.set(target, mIntAnimatedValue); 885 return; 886 } 887 if (mJniSetter != 0) { 888 nCallIntMethod(target, mJniSetter, mIntAnimatedValue); 889 return; 890 } 891 if (mSetter != null) { 892 try { 893 mTmpValueArray[0] = mIntAnimatedValue; 894 mSetter.invoke(target, mTmpValueArray); 895 } catch (InvocationTargetException e) { 896 Log.e("PropertyValuesHolder", e.toString()); 897 } catch (IllegalAccessException e) { 898 Log.e("PropertyValuesHolder", e.toString()); 899 } 900 } 901 } 902 903 @Override 904 void setupSetter(Class targetClass) { 905 if (mProperty != null) { 906 return; 907 } 908 // Check new static hashmap<propName, int> for setter method 909 try { 910 mPropertyMapLock.writeLock().lock(); 911 HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass); 912 if (propertyMap != null) { 913 Integer mJniSetterInteger = propertyMap.get(mPropertyName); 914 if (mJniSetterInteger != null) { 915 mJniSetter = mJniSetterInteger; 916 } 917 } 918 if (mJniSetter == 0) { 919 String methodName = getMethodName("set", mPropertyName); 920 mJniSetter = nGetIntMethod(targetClass, methodName); 921 if (mJniSetter != 0) { 922 if (propertyMap == null) { 923 propertyMap = new HashMap<String, Integer>(); 924 sJNISetterPropertyMap.put(targetClass, propertyMap); 925 } 926 propertyMap.put(mPropertyName, mJniSetter); 927 } 928 } 929 } catch (NoSuchMethodError e) { 930 // Couldn't find it via JNI - try reflection next. Probably means the method 931 // doesn't exist, or the type is wrong. An error will be logged later if 932 // reflection fails as well. 933 } finally { 934 mPropertyMapLock.writeLock().unlock(); 935 } 936 if (mJniSetter == 0) { 937 // Couldn't find method through fast JNI approach - just use reflection 938 super.setupSetter(targetClass); 939 } 940 } 941 } 942 943 static class FloatPropertyValuesHolder extends PropertyValuesHolder { 944 945 // Cache JNI functions to avoid looking them up twice 946 private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap = 947 new HashMap<Class, HashMap<String, Integer>>(); 948 int mJniSetter; 949 private FloatProperty mFloatProperty; 950 951 FloatKeyframeSet mFloatKeyframeSet; 952 float mFloatAnimatedValue; 953 954 public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) { 955 super(propertyName); 956 mValueType = float.class; 957 mKeyframeSet = keyframeSet; 958 mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet; 959 } 960 961 public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) { 962 super(property); 963 mValueType = float.class; 964 mKeyframeSet = keyframeSet; 965 mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet; 966 if (property instanceof FloatProperty) { 967 mFloatProperty = (FloatProperty) mProperty; 968 } 969 } 970 971 public FloatPropertyValuesHolder(String propertyName, float... values) { 972 super(propertyName); 973 setFloatValues(values); 974 } 975 976 public FloatPropertyValuesHolder(Property property, float... values) { 977 super(property); 978 setFloatValues(values); 979 if (property instanceof FloatProperty) { 980 mFloatProperty = (FloatProperty) mProperty; 981 } 982 } 983 984 @Override 985 public void setFloatValues(float... values) { 986 super.setFloatValues(values); 987 mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet; 988 } 989 990 @Override 991 void calculateValue(float fraction) { 992 mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction); 993 } 994 995 @Override 996 Object getAnimatedValue() { 997 return mFloatAnimatedValue; 998 } 999 1000 @Override 1001 public FloatPropertyValuesHolder clone() { 1002 FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone(); 1003 newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet; 1004 return newPVH; 1005 } 1006 1007 /** 1008 * Internal function to set the value on the target object, using the setter set up 1009 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 1010 * to handle turning the value calculated by ValueAnimator into a value set on the object 1011 * according to the name of the property. 1012 * @param target The target object on which the value is set 1013 */ 1014 @Override 1015 void setAnimatedValue(Object target) { 1016 if (mFloatProperty != null) { 1017 mFloatProperty.setValue(target, mFloatAnimatedValue); 1018 return; 1019 } 1020 if (mProperty != null) { 1021 mProperty.set(target, mFloatAnimatedValue); 1022 return; 1023 } 1024 if (mJniSetter != 0) { 1025 nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue); 1026 return; 1027 } 1028 if (mSetter != null) { 1029 try { 1030 mTmpValueArray[0] = mFloatAnimatedValue; 1031 mSetter.invoke(target, mTmpValueArray); 1032 } catch (InvocationTargetException e) { 1033 Log.e("PropertyValuesHolder", e.toString()); 1034 } catch (IllegalAccessException e) { 1035 Log.e("PropertyValuesHolder", e.toString()); 1036 } 1037 } 1038 } 1039 1040 @Override 1041 void setupSetter(Class targetClass) { 1042 if (mProperty != null) { 1043 return; 1044 } 1045 // Check new static hashmap<propName, int> for setter method 1046 try { 1047 mPropertyMapLock.writeLock().lock(); 1048 HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass); 1049 if (propertyMap != null) { 1050 Integer mJniSetterInteger = propertyMap.get(mPropertyName); 1051 if (mJniSetterInteger != null) { 1052 mJniSetter = mJniSetterInteger; 1053 } 1054 } 1055 if (mJniSetter == 0) { 1056 String methodName = getMethodName("set", mPropertyName); 1057 mJniSetter = nGetFloatMethod(targetClass, methodName); 1058 if (mJniSetter != 0) { 1059 if (propertyMap == null) { 1060 propertyMap = new HashMap<String, Integer>(); 1061 sJNISetterPropertyMap.put(targetClass, propertyMap); 1062 } 1063 propertyMap.put(mPropertyName, mJniSetter); 1064 } 1065 } 1066 } catch (NoSuchMethodError e) { 1067 // Couldn't find it via JNI - try reflection next. Probably means the method 1068 // doesn't exist, or the type is wrong. An error will be logged later if 1069 // reflection fails as well. 1070 } finally { 1071 mPropertyMapLock.writeLock().unlock(); 1072 } 1073 if (mJniSetter == 0) { 1074 // Couldn't find method through fast JNI approach - just use reflection 1075 super.setupSetter(targetClass); 1076 } 1077 } 1078 1079 } 1080 1081 native static private int nGetIntMethod(Class targetClass, String methodName); 1082 native static private int nGetFloatMethod(Class targetClass, String methodName); 1083 native static private void nCallIntMethod(Object target, int methodID, int arg); 1084 native static private void nCallFloatMethod(Object target, int methodID, float arg); 1085}