PropertyValuesHolder.java revision 41f041d9986f8a5d45b6cb0b86e881c81a412168
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.Log; 20 21import java.lang.reflect.InvocationTargetException; 22import java.lang.reflect.Method; 23import java.util.ArrayList; 24import java.util.HashMap; 25import java.util.concurrent.locks.ReentrantReadWriteLock; 26 27/** 28 * This class holds information about a property and the values that that property 29 * should take on during an animation. PropertyValuesHolder objects can be used to create 30 * animations with ValueAnimator or ObjectAnimator that operate on several different properties 31 * in parallel. 32 */ 33public class PropertyValuesHolder implements Cloneable { 34 35 /** 36 * The name of the property associated with the values. This need not be a real property, 37 * unless this object is being used with ObjectAnimator. But this is the name by which 38 * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator. 39 */ 40 private String mPropertyName; 41 42 /** 43 * The setter function, if needed. ObjectAnimator hands off this functionality to 44 * PropertyValuesHolder, since it holds all of the per-property information. This 45 * property can be manually set via setSetter(). Otherwise, it is automatically 46 * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator. 47 */ 48 private Method mSetter = null; 49 50 /** 51 * The getter function, if needed. ObjectAnimator hands off this functionality to 52 * PropertyValuesHolder, since it holds all of the per-property information. This 53 * property can be manually set via setSetter(). Otherwise, it is automatically 54 * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator. 55 * The getter is only derived and used if one of the values is null. 56 */ 57 private Method mGetter = null; 58 59 /** 60 * The type of values supplied. This information is used both in deriving the setter/getter 61 * functions and in deriving the type of TypeEvaluator. 62 */ 63 private Class mValueType; 64 65 /** 66 * The set of keyframes (time/value pairs) that define this animation. 67 */ 68 private KeyframeSet mKeyframeSet = null; 69 70 71 // type evaluators for the three primitive types handled by this implementation 72 private static final TypeEvaluator sIntEvaluator = new IntEvaluator(); 73 private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator(); 74 private static final TypeEvaluator sDoubleEvaluator = new DoubleEvaluator(); 75 76 // We try several different types when searching for appropriate setter/getter functions. 77 // The caller may have supplied values in a type that does not match the setter/getter 78 // functions (such as the integers 0 and 1 to represent floating point values for alpha). 79 // Also, the use of generics in constructors means that we end up with the Object versions 80 // of primitive types (Float vs. float). But most likely, the setter/getter functions 81 // will take primitive types instead. 82 // So we supply an ordered array of other types to try before giving up. 83 private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class, 84 Double.class, Integer.class}; 85 private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class, 86 Float.class, Double.class}; 87 private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class, 88 Float.class, Integer.class}; 89 90 // These maps hold all property entries for a particular class. This map 91 // is used to speed up property/setter/getter lookups for a given class/property 92 // combination. No need to use reflection on the combination more than once. 93 private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap = 94 new HashMap<Class, HashMap<String, Method>>(); 95 private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap = 96 new HashMap<Class, HashMap<String, Method>>(); 97 98 // This lock is used to ensure that only one thread is accessing the property maps 99 // at a time. 100 private ReentrantReadWriteLock propertyMapLock = new ReentrantReadWriteLock(); 101 102 // Used to pass single value to varargs parameter in setter invocation 103 private Object[] mTmpValueArray = new Object[1]; 104 105 /** 106 * The type evaluator used to calculate the animated values. This evaluator is determined 107 * automatically based on the type of the start/end objects passed into the constructor, 108 * but the system only knows about the primitive types int, double, and float. Any other 109 * type will need to set the evaluator to a custom evaluator for that type. 110 */ 111 private TypeEvaluator mEvaluator; 112 113 /** 114 * The value most recently calculated by calculateValue(). This is set during 115 * that function and might be retrieved later either by ValueAnimator.animatedValue() or 116 * by the property-setting logic in ObjectAnimator.animatedValue(). 117 */ 118 private Object mAnimatedValue; 119 120 /** 121 * Internal utility constructor, used by the factory methods to set the property name. 122 * @param propertyName The name of the property for this holder. 123 */ 124 private PropertyValuesHolder(String propertyName) { 125 mPropertyName = propertyName; 126 } 127 128 /** 129 * Constructs and returns a PropertyValuesHolder with a given property name and 130 * set of int values. 131 * @param propertyName The name of the property being animated. 132 * @param values The values that the named property will animate between. 133 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 134 */ 135 public static PropertyValuesHolder ofInt(String propertyName, int... values) { 136 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); 137 pvh.setIntValues(values); 138 return pvh; 139 } 140 141 /** 142 * Constructs and returns a PropertyValuesHolder with a given property name and 143 * set of float values. 144 * @param propertyName The name of the property being animated. 145 * @param values The values that the named property will animate between. 146 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 147 */ 148 public static PropertyValuesHolder ofFloat(String propertyName, float... values) { 149 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); 150 pvh.setFloatValues(values); 151 return pvh; 152 } 153 154 /** 155 * Constructs and returns a PropertyValuesHolder with a given property name and 156 * set of double values. 157 * @param propertyName The name of the property being animated. 158 * @param values The values that the named property will animate between. 159 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 160 */ 161 public static PropertyValuesHolder ofDouble(String propertyName, double... values) { 162 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); 163 pvh.setDoubleValues(values); 164 return pvh; 165 } 166 167 /** 168 * Constructs and returns a PropertyValuesHolder with a given property name and 169 * set of long values. 170 * @param propertyName The name of the property being animated. 171 * @param values The values that the named property will animate between. 172 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 173 */ 174 public static PropertyValuesHolder ofLong(String propertyName, long... values) { 175 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); 176 pvh.setLongValues(values); 177 return pvh; 178 } 179 180 /** 181 * Constructs and returns a PropertyValuesHolder with a given property name and 182 * set of Object values. This variant also takes a TypeEvaluator because the system 183 * cannot interpolate between objects of unknown type. 184 * 185 * @param propertyName The name of the property being animated. 186 * @param evaluator A TypeEvaluator that will be called on each animation frame to 187 * provide the ncessry interpolation between the Object values to derive the animated 188 * value. 189 * @param values The values that the named property will animate between. 190 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 191 */ 192 public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator, 193 Object... values) { 194 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); 195 pvh.setObjectValues(values); 196 pvh.setEvaluator(evaluator); 197 return pvh; 198 } 199 200 /** 201 * Constructs and returns a PropertyValuesHolder object with the specified property name and set 202 * of values. These values can be of any type, but the type should be consistent so that 203 * an appropriate {@link android.animation.TypeEvaluator} can be found that matches 204 * the common type. 205 * <p>If there is only one value, it is assumed to be the end value of an animation, 206 * and an initial value will be derived, if possible, by calling a getter function 207 * on the object. Also, if any value is null, the value will be filled in when the animation 208 * starts in the same way. This mechanism of automatically getting null values only works 209 * if the PropertyValuesHolder object is used in conjunction 210 * {@link ObjectAnimator}, and with a getter function either 211 * derived automatically from <code>propertyName</code> or set explicitly via 212 * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has 213 * no way of determining what the value should be. 214 * @param propertyName The name of the property associated with this set of values. This 215 * can be the actual property name to be used when using a ObjectAnimator object, or 216 * just a name used to get animated values, such as if this object is used with an 217 * ValueAnimator object. 218 * @param values The set of values to animate between. 219 */ 220 public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) { 221 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); 222 pvh.setKeyframes(values); 223 return pvh; 224 } 225 226 /** 227 * Set the animated values for this object to this set of ints. 228 * If there is only one value, it is assumed to be the end value of an animation, 229 * and an initial value will be derived, if possible, by calling a getter function 230 * on the object. Also, if any value is null, the value will be filled in when the animation 231 * starts in the same way. This mechanism of automatically getting null values only works 232 * if the PropertyValuesHolder object is used in conjunction 233 * {@link ObjectAnimator}, and with a getter function either 234 * derived automatically from <code>propertyName</code> or set explicitly via 235 * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has 236 * no way of determining what the value should be. 237 * 238 * @param values One or more values that the animation will animate between. 239 */ 240 public void setIntValues(int... values) { 241 mValueType = int.class; 242 mKeyframeSet = KeyframeSet.ofInt(values); 243 } 244 245 /** 246 * Set the animated values for this object to this set of floats. 247 * If there is only one value, it is assumed to be the end value of an animation, 248 * and an initial value will be derived, if possible, by calling a getter function 249 * on the object. Also, if any value is null, the value will be filled in when the animation 250 * starts in the same way. This mechanism of automatically getting null values only works 251 * if the PropertyValuesHolder object is used in conjunction 252 * {@link ObjectAnimator}, and with a getter function either 253 * derived automatically from <code>propertyName</code> or set explicitly via 254 * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has 255 * no way of determining what the value should be. 256 * 257 * @param values One or more values that the animation will animate between. 258 */ 259 public void setFloatValues(float... values) { 260 mValueType = float.class; 261 mKeyframeSet = KeyframeSet.ofFloat(values); 262 } 263 264 /** 265 * Set the animated values for this object to this set of doubles. 266 * If there is only one value, it is assumed to be the end value of an animation, 267 * and an initial value will be derived, if possible, by calling a getter function 268 * on the object. Also, if any value is null, the value will be filled in when the animation 269 * starts in the same way. This mechanism of automatically getting null values only works 270 * if the PropertyValuesHolder object is used in conjunction 271 * {@link ObjectAnimator}, and with a getter function either 272 * derived automatically from <code>propertyName</code> or set explicitly via 273 * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has 274 * no way of determining what the value should be. 275 * 276 * @param values One or more values that the animation will animate between. 277 */ 278 public void setDoubleValues(double... values) { 279 mValueType = double.class; 280 mKeyframeSet = KeyframeSet.ofDouble(values); 281 } 282 283 /** 284 * Set the animated values for this object to this set of longs. 285 * If there is only one value, it is assumed to be the end value of an animation, 286 * and an initial value will be derived, if possible, by calling a getter function 287 * on the object. Also, if any value is null, the value will be filled in when the animation 288 * starts in the same way. This mechanism of automatically getting null values only works 289 * if the PropertyValuesHolder object is used in conjunction 290 * {@link ObjectAnimator}, and with a getter function either 291 * derived automatically from <code>propertyName</code> or set explicitly via 292 * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has 293 * no way of determining what the value should be. 294 * 295 * @param values One or more values that the animation will animate between. 296 */ 297 public void setLongValues(long... values) { 298 mValueType = long.class; 299 mKeyframeSet = KeyframeSet.ofLong(values); 300 } 301 302 /** 303 * Set the animated values for this object to this set of Keyframes. 304 * 305 * @param values One or more values that the animation will animate between. 306 */ 307 public void setKeyframes(Keyframe... values) { 308 int numKeyframes = values.length; 309 Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)]; 310 mValueType = ((Keyframe)values[0]).getType(); 311 for (int i = 0; i < numKeyframes; ++i) { 312 keyframes[i] = (Keyframe)values[i]; 313 } 314 mKeyframeSet = new KeyframeSet(keyframes); 315 } 316 317 /** 318 * Set the animated values for this object to this set of Objects. 319 * If there is only one value, it is assumed to be the end value of an animation, 320 * and an initial value will be derived, if possible, by calling a getter function 321 * on the object. Also, if any value is null, the value will be filled in when the animation 322 * starts in the same way. This mechanism of automatically getting null values only works 323 * if the PropertyValuesHolder object is used in conjunction 324 * {@link ObjectAnimator}, and with a getter function either 325 * derived automatically from <code>propertyName</code> or set explicitly via 326 * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has 327 * no way of determining what the value should be. 328 * 329 * @param values One or more values that the animation will animate between. 330 */ 331 public void setObjectValues(Object... values) { 332 mValueType = values[0].getClass(); 333 mKeyframeSet = KeyframeSet.ofObject(values); 334 } 335 336 /** 337 * Sets the values being animated between. 338 * If there is only one value, it is assumed to be the end value of an animation, 339 * and an initial value will be derived, if possible, by calling a getter function 340 * on the object. Also, if any value is null, the value will be filled in when the animation 341 * starts in the same way. This mechanism of automatically getting null values only works 342 * if the PropertyValuesHolder object is used in conjunction 343 * {@link ObjectAnimator}, and with a getter function either 344 * derived automatically from <code>propertyName</code> or set explicitly via 345 * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has 346 * no way of determining what the value should be. 347 * @param values The set of values to animate between. 348 */ 349 /* 350 public void setValues(T... values) { 351 int numKeyframes = values.length; 352 for (int i = 0; i < numKeyframes; ++i) { 353 if (values[i] != null) { 354 Class thisValueType = values[i].getClass(); 355 if (mValueType == null) { 356 mValueType = thisValueType; 357 } else { 358 if (thisValueType != mValueType) { 359 if (mValueType == Integer.class && 360 (thisValueType == Float.class || thisValueType == Double.class)) { 361 mValueType = thisValueType; 362 } else if (mValueType == Float.class && thisValueType == Double.class) { 363 mValueType = thisValueType; 364 } 365 } 366 } 367 } 368 } 369 Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)]; 370 if (mValueType.equals(Keyframe.class)) { 371 mValueType = ((Keyframe)values[0]).getType(); 372 for (int i = 0; i < numKeyframes; ++i) { 373 keyframes[i] = (Keyframe)values[i]; 374 } 375 } else { 376 if (numKeyframes == 1) { 377 keyframes[0] = new Keyframe(0f, (Object) null); 378 keyframes[1] = new Keyframe(1f, values[0]); 379 } else { 380 keyframes[0] = new Keyframe(0f, values[0]); 381 for (int i = 1; i < numKeyframes; ++i) { 382 if (values[i] != null && (values[i].getClass() != mValueType)) { 383 384 } 385 keyframes[i] = new Keyframe((float) i / (numKeyframes - 1), values[i]); 386 } 387 } 388 } 389 mKeyframeSet = new KeyframeSet(keyframes); 390 } 391 */ 392 393 394 395 /** 396 * Determine the setter or getter function using the JavaBeans convention of setFoo or 397 * getFoo for a property named 'foo'. This function figures out what the name of the 398 * function should be and uses reflection to find the Method with that name on the 399 * target object. 400 * 401 * @param targetClass The class to search for the method 402 * @param prefix "set" or "get", depending on whether we need a setter or getter. 403 * @param valueType The type of the parameter (in the case of a setter). This type 404 * is derived from the values set on this PropertyValuesHolder. This type is used as 405 * a first guess at the parameter type, but we check for methods with several different 406 * types to avoid problems with slight mis-matches between supplied values and actual 407 * value types used on the setter. 408 * @return Method the method associated with mPropertyName. 409 */ 410 private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) { 411 // TODO: faster implementation... 412 Method returnVal = null; 413 String firstLetter = mPropertyName.substring(0, 1); 414 String theRest = mPropertyName.substring(1); 415 firstLetter = firstLetter.toUpperCase(); 416 String methodName = prefix + firstLetter + theRest; 417 Class args[] = null; 418 if (valueType == null) { 419 try { 420 returnVal = targetClass.getMethod(methodName, args); 421 } catch (NoSuchMethodException e) { 422 Log.e("PropertyValuesHolder", 423 "Couldn't find no-arg method for property " + mPropertyName + ": " + e); 424 } 425 } else { 426 args = new Class[1]; 427 Class typeVariants[]; 428 if (mValueType.equals(Float.class)) { 429 typeVariants = FLOAT_VARIANTS; 430 } else if (mValueType.equals(Integer.class)) { 431 typeVariants = INTEGER_VARIANTS; 432 } else if (mValueType.equals(Double.class)) { 433 typeVariants = DOUBLE_VARIANTS; 434 } else { 435 typeVariants = new Class[1]; 436 typeVariants[0] = mValueType; 437 } 438 for (Class typeVariant : typeVariants) { 439 args[0] = typeVariant; 440 try { 441 returnVal = targetClass.getMethod(methodName, args); 442 // change the value type to suit 443 mValueType = typeVariant; 444 return returnVal; 445 } catch (NoSuchMethodException e) { 446 // Swallow the error and keep trying other variants 447 } 448 } 449 // If we got here, then no appropriate function was found 450 Log.e("PropertyValuesHolder", 451 "Couldn't find setter/getter for property " + mPropertyName + 452 "with value type "+ mValueType); 453 } 454 455 return returnVal; 456 } 457 458 459 /** 460 * Returns the setter or getter requested. This utility function checks whether the 461 * requested method exists in the propertyMapMap cache. If not, it calls another 462 * utility function to request the Method from the targetClass directly. 463 * @param targetClass The Class on which the requested method should exist. 464 * @param propertyMapMap The cache of setters/getters derived so far. 465 * @param prefix "set" or "get", for the setter or getter. 466 * @param valueType The type of parameter passed into the method (null for getter). 467 * @return Method the method associated with mPropertyName. 468 */ 469 private Method setupSetterOrGetter(Class targetClass, 470 HashMap<Class, HashMap<String, Method>> propertyMapMap, 471 String prefix, Class valueType) { 472 Method setterOrGetter = null; 473 try { 474 // Have to lock property map prior to reading it, to guard against 475 // another thread putting something in there after we've checked it 476 // but before we've added an entry to it 477 // TODO: can we store the setter/getter per Class instead of per Object? 478 propertyMapLock.writeLock().lock(); 479 HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass); 480 if (propertyMap != null) { 481 setterOrGetter = propertyMap.get(mPropertyName); 482 } 483 if (setterOrGetter == null) { 484 setterOrGetter = getPropertyFunction(targetClass, prefix, valueType); 485 if (propertyMap == null) { 486 propertyMap = new HashMap<String, Method>(); 487 propertyMapMap.put(targetClass, propertyMap); 488 } 489 propertyMap.put(mPropertyName, setterOrGetter); 490 } 491 } finally { 492 propertyMapLock.writeLock().unlock(); 493 } 494 return setterOrGetter; 495 } 496 497 /** 498 * Utility function to get the setter from targetClass 499 * @param targetClass The Class on which the requested method should exist. 500 */ 501 private void setupSetter(Class targetClass) { 502 mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType); 503 } 504 505 /** 506 * Utility function to get the getter from targetClass 507 */ 508 private void setupGetter(Class targetClass) { 509 mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null); 510 } 511 512 /** 513 * Internal function (called from ObjectAnimator) to set up the setter and getter 514 * prior to running the animation. If the setter has not been manually set for this 515 * object, it will be derived automatically given the property name, target object, and 516 * types of values supplied. If no getter has been set, it will be supplied iff any of the 517 * supplied values was null. If there is a null value, then the getter (supplied or derived) 518 * will be called to set those null values to the current value of the property 519 * on the target object. 520 * @param target The object on which the setter (and possibly getter) exist. 521 */ 522 void setupSetterAndGetter(Object target) { 523 Class targetClass = target.getClass(); 524 if (mSetter == null) { 525 setupSetter(targetClass); 526 } 527 for (Keyframe kf : mKeyframeSet.mKeyframes) { 528 if (kf.getValue() == null) { 529 if (mGetter == null) { 530 setupGetter(targetClass); 531 } 532 try { 533 kf.setValue(mGetter.invoke(target)); 534 } catch (InvocationTargetException e) { 535 Log.e("PropertyValuesHolder", e.toString()); 536 } catch (IllegalAccessException e) { 537 Log.e("PropertyValuesHolder", e.toString()); 538 } 539 } 540 } 541 } 542 543 /** 544 * Utility function to set the value stored in a particular Keyframe. The value used is 545 * whatever the value is for the property name specified in the keyframe on the target object. 546 * 547 * @param target The target object from which the current value should be extracted. 548 * @param kf The keyframe which holds the property name and value. 549 */ 550 private void setupValue(Object target, Keyframe kf) { 551 try { 552 if (mGetter == null) { 553 Class targetClass = target.getClass(); 554 setupGetter(targetClass); 555 } 556 kf.setValue(mGetter.invoke(target)); 557 } catch (InvocationTargetException e) { 558 Log.e("PropertyValuesHolder", e.toString()); 559 } catch (IllegalAccessException e) { 560 Log.e("PropertyValuesHolder", e.toString()); 561 } 562 } 563 564 /** 565 * This function is called by ObjectAnimator when setting the start values for an animation. 566 * The start values are set according to the current values in the target object. The 567 * property whose value is extracted is whatever is specified by the propertyName of this 568 * PropertyValuesHolder object. 569 * 570 * @param target The object which holds the start values that should be set. 571 */ 572 void setupStartValue(Object target) { 573 setupValue(target, mKeyframeSet.mKeyframes.get(0)); 574 } 575 576 /** 577 * This function is called by ObjectAnimator when setting the end values for an animation. 578 * The end values are set according to the current values in the target object. The 579 * property whose value is extracted is whatever is specified by the propertyName of this 580 * PropertyValuesHolder object. 581 * 582 * @param target The object which holds the start values that should be set. 583 */ 584 void setupEndValue(Object target) { 585 setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1)); 586 } 587 588 @Override 589 public PropertyValuesHolder clone() { 590 ArrayList<Keyframe> keyframes = mKeyframeSet.mKeyframes; 591 int numKeyframes = mKeyframeSet.mKeyframes.size(); 592 Keyframe[] newKeyframes = new Keyframe[numKeyframes]; 593 for (int i = 0; i < numKeyframes; ++i) { 594 newKeyframes[i] = keyframes.get(i).clone(); 595 } 596 return PropertyValuesHolder.ofKeyframe(mPropertyName, newKeyframes); 597 } 598 /** 599 * Internal function to set the value on the target object, using the setter set up 600 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 601 * to handle turning the value calculated by ValueAnimator into a value set on the object 602 * according to the name of the property. 603 * @param target The target object on which the value is set 604 */ 605 void setAnimatedValue(Object target) { 606 if (mSetter != null) { 607 try { 608 mTmpValueArray[0] = mAnimatedValue; 609 mSetter.invoke(target, mTmpValueArray); 610 } catch (InvocationTargetException e) { 611 Log.e("PropertyValuesHolder", e.toString()); 612 } catch (IllegalAccessException e) { 613 Log.e("PropertyValuesHolder", e.toString()); 614 } 615 } 616 } 617 618 /** 619 * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used 620 * to calculate animated values. 621 */ 622 void init() { 623 if (mEvaluator == null) { 624 mEvaluator = (mValueType == int.class || mValueType == Integer.class) ? sIntEvaluator : 625 (mValueType == double.class || mValueType == Double.class) ? sDoubleEvaluator : 626 sFloatEvaluator; 627 } 628 } 629 630 /** 631 * The TypeEvaluator will the automatically determined based on the type of values 632 * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so 633 * desired. This may be important in cases where either the type of the values supplied 634 * do not match the way that they should be interpolated between, or if the values 635 * are of a custom type or one not currently understood by the animation system. Currently, 636 * only values of type float, double, and int (and their Object equivalents, Float, Double, 637 * and Integer) are correctly interpolated; all other types require setting a TypeEvaluator. 638 * @param evaluator 639 */ 640 public void setEvaluator(TypeEvaluator evaluator) { 641 mEvaluator = evaluator; 642 } 643 644 /** 645 * Function used to calculate the value according to the evaluator set up for 646 * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue(). 647 * 648 * @param fraction The elapsed, interpolated fraction of the animation. 649 * @return The calculated value at this point in the animation. 650 */ 651 Object calculateValue(float fraction) { 652 mAnimatedValue = mKeyframeSet.getValue(fraction, mEvaluator); 653 return mAnimatedValue; 654 } 655 656 /** 657 * Sets the <code>Method</code> that is called with the animated values calculated 658 * during the animation. Setting the setter method is an alternative to supplying a 659 * {@link #setPropertyName(String) propertyName} from which the method is derived. This 660 * approach is more direct, and is especially useful when a function must be called that does 661 * not correspond to the convention of <code>setName()</code>. For example, if a function 662 * called <code>offset()</code> is to be called with the animated values, there is no way 663 * to tell <code>ObjectAnimator</code> how to call that function simply through a property 664 * name, so a setter method should be supplied instead. 665 * 666 * <p>Note that the setter function must take the same parameter type as the 667 * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to 668 * the setter function will fail.</p> 669 * 670 * @param setter The setter method that should be called with the animated values. 671 */ 672 public void setSetter(Method setter) { 673 mSetter = setter; 674 } 675 676 /** 677 * Gets the <code>Method</code> that is called with the animated values calculated 678 * during the animation. 679 */ 680 public Method getSetter() { 681 return mSetter; 682 } 683 684 /** 685 * Sets the <code>Method</code> that is called to get unsupplied <code>valueFrom</code> or 686 * <code>valueTo</code> properties. Setting the getter method is an alternative to supplying a 687 * {@link #setPropertyName(String) propertyName} from which the method is derived. This 688 * approach is more direct, and is especially useful when a function must be called that does 689 * not correspond to the convention of <code>setName()</code>. For example, if a function 690 * called <code>offset()</code> is to be called to get an initial value, there is no way 691 * to tell <code>ObjectAnimator</code> how to call that function simply through a property 692 * name, so a getter method should be supplied instead. 693 * 694 * <p>Note that the getter method is only called whether supplied here or derived 695 * from the property name, if one of <code>valueFrom</code> or <code>valueTo</code> are 696 * null. If both of those values are non-null, then there is no need to get one of the 697 * values and the getter is not called. 698 * 699 * <p>Note that the getter function must return the same parameter type as the 700 * <code>valueFrom</code> and <code>valueTo</code> properties (whichever of them are 701 * non-null), otherwise the call to the getter function will fail.</p> 702 * 703 * @param getter The getter method that should be called to get initial animation values. 704 */ 705 public void setGetter(Method getter) { 706 mGetter = getter; 707 } 708 709 /** 710 * Gets the <code>Method</code> that is called to get unsupplied <code>valueFrom</code> or 711 * <code>valueTo</code> properties. 712 */ 713 public Method getGetter() { 714 return mGetter; 715 } 716 717 /** 718 * Sets the name of the property that will be animated. This name is used to derive 719 * a setter function that will be called to set animated values. 720 * For example, a property name of <code>foo</code> will result 721 * in a call to the function <code>setFoo()</code> on the target object. If either 722 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 723 * also be derived and called. 724 * 725 * <p>Note that the setter function derived from this property name 726 * must take the same parameter type as the 727 * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to 728 * the setter function will fail.</p> 729 * 730 * @param propertyName The name of the property being animated. 731 */ 732 public void setPropertyName(String propertyName) { 733 mPropertyName = propertyName; 734 } 735 736 /** 737 * Gets the name of the property that will be animated. This name will be used to derive 738 * a setter function that will be called to set animated values. 739 * For example, a property name of <code>foo</code> will result 740 * in a call to the function <code>setFoo()</code> on the target object. If either 741 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 742 * also be derived and called. 743 */ 744 public String getPropertyName() { 745 return mPropertyName; 746 } 747 748 /** 749 * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value 750 * most recently calculated in calculateValue(). 751 * @return 752 */ 753 Object getAnimatedValue() { 754 return mAnimatedValue; 755 } 756}