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