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