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