PropertyValuesHolder.java revision c96c7b2e54965e30c8fb82295f1ca9f891ebd5e7
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 implement {@link TypeConverter#convertBack(Object)} 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 } 635 636 /** 637 * Sets the converter to convert from the values type to the setter's parameter type. 638 * @param converter The converter to use to convert values. 639 */ 640 public void setConverter(TypeConverter converter) { 641 mConverter = converter; 642 } 643 644 /** 645 * Determine the setter or getter function using the JavaBeans convention of setFoo or 646 * getFoo for a property named 'foo'. This function figures out what the name of the 647 * function should be and uses reflection to find the Method with that name on the 648 * target object. 649 * 650 * @param targetClass The class to search for the method 651 * @param prefix "set" or "get", depending on whether we need a setter or getter. 652 * @param valueType The type of the parameter (in the case of a setter). This type 653 * is derived from the values set on this PropertyValuesHolder. This type is used as 654 * a first guess at the parameter type, but we check for methods with several different 655 * types to avoid problems with slight mis-matches between supplied values and actual 656 * value types used on the setter. 657 * @return Method the method associated with mPropertyName. 658 */ 659 private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) { 660 // TODO: faster implementation... 661 Method returnVal = null; 662 String methodName = getMethodName(prefix, mPropertyName); 663 Class args[] = null; 664 if (valueType == null) { 665 try { 666 returnVal = targetClass.getMethod(methodName, args); 667 } catch (NoSuchMethodException e) { 668 // Swallow the error, log it later 669 } 670 } else { 671 args = new Class[1]; 672 Class typeVariants[]; 673 if (valueType.equals(Float.class)) { 674 typeVariants = FLOAT_VARIANTS; 675 } else if (valueType.equals(Integer.class)) { 676 typeVariants = INTEGER_VARIANTS; 677 } else if (valueType.equals(Double.class)) { 678 typeVariants = DOUBLE_VARIANTS; 679 } else { 680 typeVariants = new Class[1]; 681 typeVariants[0] = valueType; 682 } 683 for (Class typeVariant : typeVariants) { 684 args[0] = typeVariant; 685 try { 686 returnVal = targetClass.getMethod(methodName, args); 687 if (mConverter == null) { 688 // change the value type to suit 689 mValueType = typeVariant; 690 } 691 return returnVal; 692 } catch (NoSuchMethodException e) { 693 // Swallow the error and keep trying other variants 694 } 695 } 696 // If we got here, then no appropriate function was found 697 } 698 699 if (returnVal == null) { 700 Log.w("PropertyValuesHolder", "Method " + 701 getMethodName(prefix, mPropertyName) + "() with type " + valueType + 702 " not found on target class " + targetClass); 703 } 704 705 return returnVal; 706 } 707 708 709 /** 710 * Returns the setter or getter requested. This utility function checks whether the 711 * requested method exists in the propertyMapMap cache. If not, it calls another 712 * utility function to request the Method from the targetClass directly. 713 * @param targetClass The Class on which the requested method should exist. 714 * @param propertyMapMap The cache of setters/getters derived so far. 715 * @param prefix "set" or "get", for the setter or getter. 716 * @param valueType The type of parameter passed into the method (null for getter). 717 * @return Method the method associated with mPropertyName. 718 */ 719 private Method setupSetterOrGetter(Class targetClass, 720 HashMap<Class, HashMap<String, Method>> propertyMapMap, 721 String prefix, Class valueType) { 722 Method setterOrGetter = null; 723 try { 724 // Have to lock property map prior to reading it, to guard against 725 // another thread putting something in there after we've checked it 726 // but before we've added an entry to it 727 mPropertyMapLock.writeLock().lock(); 728 HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass); 729 if (propertyMap != null) { 730 setterOrGetter = propertyMap.get(mPropertyName); 731 } 732 if (setterOrGetter == null) { 733 setterOrGetter = getPropertyFunction(targetClass, prefix, valueType); 734 if (propertyMap == null) { 735 propertyMap = new HashMap<String, Method>(); 736 propertyMapMap.put(targetClass, propertyMap); 737 } 738 propertyMap.put(mPropertyName, setterOrGetter); 739 } 740 } finally { 741 mPropertyMapLock.writeLock().unlock(); 742 } 743 return setterOrGetter; 744 } 745 746 /** 747 * Utility function to get the setter from targetClass 748 * @param targetClass The Class on which the requested method should exist. 749 */ 750 void setupSetter(Class targetClass) { 751 Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType(); 752 mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType); 753 } 754 755 /** 756 * Utility function to get the getter from targetClass 757 */ 758 private void setupGetter(Class targetClass) { 759 mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null); 760 } 761 762 /** 763 * Internal function (called from ObjectAnimator) to set up the setter and getter 764 * prior to running the animation. If the setter has not been manually set for this 765 * object, it will be derived automatically given the property name, target object, and 766 * types of values supplied. If no getter has been set, it will be supplied iff any of the 767 * supplied values was null. If there is a null value, then the getter (supplied or derived) 768 * will be called to set those null values to the current value of the property 769 * on the target object. 770 * @param target The object on which the setter (and possibly getter) exist. 771 */ 772 void setupSetterAndGetter(Object target) { 773 if (mProperty != null) { 774 // check to make sure that mProperty is on the class of target 775 try { 776 Object testValue = null; 777 for (Keyframe kf : mKeyframeSet.mKeyframes) { 778 if (!kf.hasValue()) { 779 if (testValue == null) { 780 testValue = convertBack(mProperty.get(target)); 781 } 782 kf.setValue(testValue); 783 } 784 } 785 return; 786 } catch (ClassCastException e) { 787 Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() + 788 ") on target object " + target + ". Trying reflection instead"); 789 mProperty = null; 790 } 791 } 792 Class targetClass = target.getClass(); 793 if (mSetter == null) { 794 setupSetter(targetClass); 795 } 796 for (Keyframe kf : mKeyframeSet.mKeyframes) { 797 if (!kf.hasValue()) { 798 if (mGetter == null) { 799 setupGetter(targetClass); 800 if (mGetter == null) { 801 // Already logged the error - just return to avoid NPE 802 return; 803 } 804 } 805 try { 806 Object value = convertBack(mGetter.invoke(target)); 807 kf.setValue(value); 808 } catch (InvocationTargetException e) { 809 Log.e("PropertyValuesHolder", e.toString()); 810 } catch (IllegalAccessException e) { 811 Log.e("PropertyValuesHolder", e.toString()); 812 } 813 } 814 } 815 } 816 817 private Object convertBack(Object value) { 818 if (mConverter != null) { 819 value = mConverter.convertBack(value); 820 if (value == null) { 821 throw new IllegalArgumentException("Converter " 822 + mConverter.getClass().getName() 823 + " must implement convertBack and not return null."); 824 } 825 } 826 return value; 827 } 828 829 /** 830 * Utility function to set the value stored in a particular Keyframe. The value used is 831 * whatever the value is for the property name specified in the keyframe on the target object. 832 * 833 * @param target The target object from which the current value should be extracted. 834 * @param kf The keyframe which holds the property name and value. 835 */ 836 private void setupValue(Object target, Keyframe kf) { 837 if (mProperty != null) { 838 Object value = convertBack(mProperty.get(target)); 839 kf.setValue(value); 840 } 841 try { 842 if (mGetter == null) { 843 Class targetClass = target.getClass(); 844 setupGetter(targetClass); 845 if (mGetter == null) { 846 // Already logged the error - just return to avoid NPE 847 return; 848 } 849 } 850 Object value = convertBack(mGetter.invoke(target)); 851 kf.setValue(value); 852 } catch (InvocationTargetException e) { 853 Log.e("PropertyValuesHolder", e.toString()); 854 } catch (IllegalAccessException e) { 855 Log.e("PropertyValuesHolder", e.toString()); 856 } 857 } 858 859 /** 860 * This function is called by ObjectAnimator when setting the start values for an animation. 861 * The start values are set according to the current values in the target object. The 862 * property whose value is extracted is whatever is specified by the propertyName of this 863 * PropertyValuesHolder object. 864 * 865 * @param target The object which holds the start values that should be set. 866 */ 867 void setupStartValue(Object target) { 868 setupValue(target, mKeyframeSet.mKeyframes.get(0)); 869 } 870 871 /** 872 * This function is called by ObjectAnimator when setting the end values for an animation. 873 * The end values are set according to the current values in the target object. The 874 * property whose value is extracted is whatever is specified by the propertyName of this 875 * PropertyValuesHolder object. 876 * 877 * @param target The object which holds the start values that should be set. 878 */ 879 void setupEndValue(Object target) { 880 setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1)); 881 } 882 883 @Override 884 public PropertyValuesHolder clone() { 885 try { 886 PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone(); 887 newPVH.mPropertyName = mPropertyName; 888 newPVH.mProperty = mProperty; 889 newPVH.mKeyframeSet = mKeyframeSet.clone(); 890 newPVH.mEvaluator = mEvaluator; 891 return newPVH; 892 } catch (CloneNotSupportedException e) { 893 // won't reach here 894 return null; 895 } 896 } 897 898 /** 899 * Internal function to set the value on the target object, using the setter set up 900 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 901 * to handle turning the value calculated by ValueAnimator into a value set on the object 902 * according to the name of the property. 903 * @param target The target object on which the value is set 904 */ 905 void setAnimatedValue(Object target) { 906 if (mProperty != null) { 907 mProperty.set(target, getAnimatedValue()); 908 } 909 if (mSetter != null) { 910 try { 911 mTmpValueArray[0] = getAnimatedValue(); 912 mSetter.invoke(target, mTmpValueArray); 913 } catch (InvocationTargetException e) { 914 Log.e("PropertyValuesHolder", e.toString()); 915 } catch (IllegalAccessException e) { 916 Log.e("PropertyValuesHolder", e.toString()); 917 } 918 } 919 } 920 921 /** 922 * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used 923 * to calculate animated values. 924 */ 925 void init() { 926 if (mEvaluator == null) { 927 // We already handle int and float automatically, but not their Object 928 // equivalents 929 mEvaluator = (mValueType == Integer.class) ? sIntEvaluator : 930 (mValueType == Float.class) ? sFloatEvaluator : 931 null; 932 } 933 if (mEvaluator != null) { 934 // KeyframeSet knows how to evaluate the common types - only give it a custom 935 // evaluator if one has been set on this class 936 mKeyframeSet.setEvaluator(mEvaluator); 937 } 938 } 939 940 /** 941 * The TypeEvaluator will be automatically determined based on the type of values 942 * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so 943 * desired. This may be important in cases where either the type of the values supplied 944 * do not match the way that they should be interpolated between, or if the values 945 * are of a custom type or one not currently understood by the animation system. Currently, 946 * only values of type float and int (and their Object equivalents: Float 947 * and Integer) are correctly interpolated; all other types require setting a TypeEvaluator. 948 * @param evaluator 949 */ 950 public void setEvaluator(TypeEvaluator evaluator) { 951 mEvaluator = evaluator; 952 mKeyframeSet.setEvaluator(evaluator); 953 } 954 955 /** 956 * Function used to calculate the value according to the evaluator set up for 957 * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue(). 958 * 959 * @param fraction The elapsed, interpolated fraction of the animation. 960 */ 961 void calculateValue(float fraction) { 962 Object value = mKeyframeSet.getValue(fraction); 963 mAnimatedValue = mConverter == null ? value : mConverter.convert(value); 964 } 965 966 /** 967 * Sets the name of the property that will be animated. This name is used to derive 968 * a setter function that will be called to set animated values. 969 * For example, a property name of <code>foo</code> will result 970 * in a call to the function <code>setFoo()</code> on the target object. If either 971 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 972 * also be derived and called. 973 * 974 * <p>Note that the setter function derived from this property name 975 * must take the same parameter type as the 976 * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to 977 * the setter function will fail.</p> 978 * 979 * @param propertyName The name of the property being animated. 980 */ 981 public void setPropertyName(String propertyName) { 982 mPropertyName = propertyName; 983 } 984 985 /** 986 * Sets the property that will be animated. 987 * 988 * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property 989 * must exist on the target object specified in that ObjectAnimator.</p> 990 * 991 * @param property The property being animated. 992 */ 993 public void setProperty(Property property) { 994 mProperty = property; 995 } 996 997 /** 998 * Gets the name of the property that will be animated. This name will be used to derive 999 * a setter function that will be called to set animated values. 1000 * For example, a property name of <code>foo</code> will result 1001 * in a call to the function <code>setFoo()</code> on the target object. If either 1002 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 1003 * also be derived and called. 1004 */ 1005 public String getPropertyName() { 1006 return mPropertyName; 1007 } 1008 1009 /** 1010 * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value 1011 * most recently calculated in calculateValue(). 1012 * @return 1013 */ 1014 Object getAnimatedValue() { 1015 return mAnimatedValue; 1016 } 1017 1018 @Override 1019 public String toString() { 1020 return mPropertyName + ": " + mKeyframeSet.toString(); 1021 } 1022 1023 /** 1024 * Utility method to derive a setter/getter method name from a property name, where the 1025 * prefix is typically "set" or "get" and the first letter of the property name is 1026 * capitalized. 1027 * 1028 * @param prefix The precursor to the method name, before the property name begins, typically 1029 * "set" or "get". 1030 * @param propertyName The name of the property that represents the bulk of the method name 1031 * after the prefix. The first letter of this word will be capitalized in the resulting 1032 * method name. 1033 * @return String the property name converted to a method name according to the conventions 1034 * specified above. 1035 */ 1036 static String getMethodName(String prefix, String propertyName) { 1037 if (propertyName == null || propertyName.length() == 0) { 1038 // shouldn't get here 1039 return prefix; 1040 } 1041 char firstLetter = Character.toUpperCase(propertyName.charAt(0)); 1042 String theRest = propertyName.substring(1); 1043 return prefix + firstLetter + theRest; 1044 } 1045 1046 static class IntPropertyValuesHolder extends PropertyValuesHolder { 1047 1048 // Cache JNI functions to avoid looking them up twice 1049 private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap = 1050 new HashMap<Class, HashMap<String, Integer>>(); 1051 int mJniSetter; 1052 private IntProperty mIntProperty; 1053 1054 IntKeyframeSet mIntKeyframeSet; 1055 int mIntAnimatedValue; 1056 1057 public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) { 1058 super(propertyName); 1059 mValueType = int.class; 1060 mKeyframeSet = keyframeSet; 1061 mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet; 1062 } 1063 1064 public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) { 1065 super(property); 1066 mValueType = int.class; 1067 mKeyframeSet = keyframeSet; 1068 mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet; 1069 if (property instanceof IntProperty) { 1070 mIntProperty = (IntProperty) mProperty; 1071 } 1072 } 1073 1074 public IntPropertyValuesHolder(String propertyName, int... values) { 1075 super(propertyName); 1076 setIntValues(values); 1077 } 1078 1079 public IntPropertyValuesHolder(Property property, int... values) { 1080 super(property); 1081 setIntValues(values); 1082 if (property instanceof IntProperty) { 1083 mIntProperty = (IntProperty) mProperty; 1084 } 1085 } 1086 1087 @Override 1088 public void setIntValues(int... values) { 1089 super.setIntValues(values); 1090 mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet; 1091 } 1092 1093 @Override 1094 void calculateValue(float fraction) { 1095 mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction); 1096 } 1097 1098 @Override 1099 Object getAnimatedValue() { 1100 return mIntAnimatedValue; 1101 } 1102 1103 @Override 1104 public IntPropertyValuesHolder clone() { 1105 IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone(); 1106 newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet; 1107 return newPVH; 1108 } 1109 1110 /** 1111 * Internal function to set the value on the target object, using the setter set up 1112 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 1113 * to handle turning the value calculated by ValueAnimator into a value set on the object 1114 * according to the name of the property. 1115 * @param target The target object on which the value is set 1116 */ 1117 @Override 1118 void setAnimatedValue(Object target) { 1119 if (mIntProperty != null) { 1120 mIntProperty.setValue(target, mIntAnimatedValue); 1121 return; 1122 } 1123 if (mProperty != null) { 1124 mProperty.set(target, mIntAnimatedValue); 1125 return; 1126 } 1127 if (mJniSetter != 0) { 1128 nCallIntMethod(target, mJniSetter, mIntAnimatedValue); 1129 return; 1130 } 1131 if (mSetter != null) { 1132 try { 1133 mTmpValueArray[0] = mIntAnimatedValue; 1134 mSetter.invoke(target, mTmpValueArray); 1135 } catch (InvocationTargetException e) { 1136 Log.e("PropertyValuesHolder", e.toString()); 1137 } catch (IllegalAccessException e) { 1138 Log.e("PropertyValuesHolder", e.toString()); 1139 } 1140 } 1141 } 1142 1143 @Override 1144 void setupSetter(Class targetClass) { 1145 if (mProperty != null) { 1146 return; 1147 } 1148 // Check new static hashmap<propName, int> for setter method 1149 try { 1150 mPropertyMapLock.writeLock().lock(); 1151 HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass); 1152 if (propertyMap != null) { 1153 Integer mJniSetterInteger = propertyMap.get(mPropertyName); 1154 if (mJniSetterInteger != null) { 1155 mJniSetter = mJniSetterInteger; 1156 } 1157 } 1158 if (mJniSetter == 0) { 1159 String methodName = getMethodName("set", mPropertyName); 1160 mJniSetter = nGetIntMethod(targetClass, methodName); 1161 if (mJniSetter != 0) { 1162 if (propertyMap == null) { 1163 propertyMap = new HashMap<String, Integer>(); 1164 sJNISetterPropertyMap.put(targetClass, propertyMap); 1165 } 1166 propertyMap.put(mPropertyName, mJniSetter); 1167 } 1168 } 1169 } catch (NoSuchMethodError e) { 1170 // Couldn't find it via JNI - try reflection next. Probably means the method 1171 // doesn't exist, or the type is wrong. An error will be logged later if 1172 // reflection fails as well. 1173 } finally { 1174 mPropertyMapLock.writeLock().unlock(); 1175 } 1176 if (mJniSetter == 0) { 1177 // Couldn't find method through fast JNI approach - just use reflection 1178 super.setupSetter(targetClass); 1179 } 1180 } 1181 } 1182 1183 static class FloatPropertyValuesHolder extends PropertyValuesHolder { 1184 1185 // Cache JNI functions to avoid looking them up twice 1186 private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap = 1187 new HashMap<Class, HashMap<String, Integer>>(); 1188 int mJniSetter; 1189 private FloatProperty mFloatProperty; 1190 1191 FloatKeyframeSet mFloatKeyframeSet; 1192 float mFloatAnimatedValue; 1193 1194 public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) { 1195 super(propertyName); 1196 mValueType = float.class; 1197 mKeyframeSet = keyframeSet; 1198 mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet; 1199 } 1200 1201 public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) { 1202 super(property); 1203 mValueType = float.class; 1204 mKeyframeSet = keyframeSet; 1205 mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet; 1206 if (property instanceof FloatProperty) { 1207 mFloatProperty = (FloatProperty) mProperty; 1208 } 1209 } 1210 1211 public FloatPropertyValuesHolder(String propertyName, float... values) { 1212 super(propertyName); 1213 setFloatValues(values); 1214 } 1215 1216 public FloatPropertyValuesHolder(Property property, float... values) { 1217 super(property); 1218 setFloatValues(values); 1219 if (property instanceof FloatProperty) { 1220 mFloatProperty = (FloatProperty) mProperty; 1221 } 1222 } 1223 1224 @Override 1225 public void setFloatValues(float... values) { 1226 super.setFloatValues(values); 1227 mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet; 1228 } 1229 1230 @Override 1231 void calculateValue(float fraction) { 1232 mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction); 1233 } 1234 1235 @Override 1236 Object getAnimatedValue() { 1237 return mFloatAnimatedValue; 1238 } 1239 1240 @Override 1241 public FloatPropertyValuesHolder clone() { 1242 FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone(); 1243 newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet; 1244 return newPVH; 1245 } 1246 1247 /** 1248 * Internal function to set the value on the target object, using the setter set up 1249 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 1250 * to handle turning the value calculated by ValueAnimator into a value set on the object 1251 * according to the name of the property. 1252 * @param target The target object on which the value is set 1253 */ 1254 @Override 1255 void setAnimatedValue(Object target) { 1256 if (mFloatProperty != null) { 1257 mFloatProperty.setValue(target, mFloatAnimatedValue); 1258 return; 1259 } 1260 if (mProperty != null) { 1261 mProperty.set(target, mFloatAnimatedValue); 1262 return; 1263 } 1264 if (mJniSetter != 0) { 1265 nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue); 1266 return; 1267 } 1268 if (mSetter != null) { 1269 try { 1270 mTmpValueArray[0] = mFloatAnimatedValue; 1271 mSetter.invoke(target, mTmpValueArray); 1272 } catch (InvocationTargetException e) { 1273 Log.e("PropertyValuesHolder", e.toString()); 1274 } catch (IllegalAccessException e) { 1275 Log.e("PropertyValuesHolder", e.toString()); 1276 } 1277 } 1278 } 1279 1280 @Override 1281 void setupSetter(Class targetClass) { 1282 if (mProperty != null) { 1283 return; 1284 } 1285 // Check new static hashmap<propName, int> for setter method 1286 try { 1287 mPropertyMapLock.writeLock().lock(); 1288 HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass); 1289 if (propertyMap != null) { 1290 Integer mJniSetterInteger = propertyMap.get(mPropertyName); 1291 if (mJniSetterInteger != null) { 1292 mJniSetter = mJniSetterInteger; 1293 } 1294 } 1295 if (mJniSetter == 0) { 1296 String methodName = getMethodName("set", mPropertyName); 1297 mJniSetter = nGetFloatMethod(targetClass, methodName); 1298 if (mJniSetter != 0) { 1299 if (propertyMap == null) { 1300 propertyMap = new HashMap<String, Integer>(); 1301 sJNISetterPropertyMap.put(targetClass, propertyMap); 1302 } 1303 propertyMap.put(mPropertyName, mJniSetter); 1304 } 1305 } 1306 } catch (NoSuchMethodError e) { 1307 // Couldn't find it via JNI - try reflection next. Probably means the method 1308 // doesn't exist, or the type is wrong. An error will be logged later if 1309 // reflection fails as well. 1310 } finally { 1311 mPropertyMapLock.writeLock().unlock(); 1312 } 1313 if (mJniSetter == 0) { 1314 // Couldn't find method through fast JNI approach - just use reflection 1315 super.setupSetter(targetClass); 1316 } 1317 } 1318 1319 } 1320 1321 static class MultiFloatValuesHolder extends PropertyValuesHolder { 1322 private int mJniSetter; 1323 private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap = 1324 new HashMap<Class, HashMap<String, Integer>>(); 1325 1326 public MultiFloatValuesHolder(String propertyName, TypeConverter converter, 1327 TypeEvaluator evaluator, Object... values) { 1328 super(propertyName); 1329 setConverter(converter); 1330 setObjectValues(values); 1331 setEvaluator(evaluator); 1332 } 1333 1334 public MultiFloatValuesHolder(String propertyName, TypeConverter converter, 1335 TypeEvaluator evaluator, KeyframeSet keyframeSet) { 1336 super(propertyName); 1337 setConverter(converter); 1338 mKeyframeSet = keyframeSet; 1339 setEvaluator(evaluator); 1340 } 1341 1342 /** 1343 * Internal function to set the value on the target object, using the setter set up 1344 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 1345 * to handle turning the value calculated by ValueAnimator into a value set on the object 1346 * according to the name of the property. 1347 * 1348 * @param target The target object on which the value is set 1349 */ 1350 @Override 1351 void setAnimatedValue(Object target) { 1352 float[] values = (float[]) getAnimatedValue(); 1353 int numParameters = values.length; 1354 if (mJniSetter != 0) { 1355 switch (numParameters) { 1356 case 1: 1357 nCallFloatMethod(target, mJniSetter, values[0]); 1358 break; 1359 case 2: 1360 nCallTwoFloatMethod(target, mJniSetter, values[0], values[1]); 1361 break; 1362 case 4: 1363 nCallFourFloatMethod(target, mJniSetter, values[0], values[1], 1364 values[2], values[3]); 1365 break; 1366 default: { 1367 nCallMultipleFloatMethod(target, mJniSetter, values); 1368 break; 1369 } 1370 } 1371 } 1372 } 1373 1374 /** 1375 * Internal function (called from ObjectAnimator) to set up the setter and getter 1376 * prior to running the animation. No getter can be used for multiple parameters. 1377 * 1378 * @param target The object on which the setter exists. 1379 */ 1380 @Override 1381 void setupSetterAndGetter(Object target) { 1382 setupSetter(target.getClass()); 1383 } 1384 1385 @Override 1386 void setupSetter(Class targetClass) { 1387 if (mJniSetter != 0) { 1388 return; 1389 } 1390 try { 1391 mPropertyMapLock.writeLock().lock(); 1392 HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass); 1393 if (propertyMap != null) { 1394 Integer jniSetterInteger = propertyMap.get(mPropertyName); 1395 if (jniSetterInteger != null) { 1396 mJniSetter = jniSetterInteger; 1397 } 1398 } 1399 if (mJniSetter == 0) { 1400 String methodName = getMethodName("set", mPropertyName); 1401 calculateValue(0f); 1402 float[] values = (float[]) getAnimatedValue(); 1403 int numParams = values.length; 1404 try { 1405 mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams); 1406 } catch (NoSuchMethodError e) { 1407 // try without the 'set' prefix 1408 mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName, numParams); 1409 } 1410 if (mJniSetter != 0) { 1411 if (propertyMap == null) { 1412 propertyMap = new HashMap<String, Integer>(); 1413 sJNISetterPropertyMap.put(targetClass, propertyMap); 1414 } 1415 propertyMap.put(mPropertyName, mJniSetter); 1416 } 1417 } 1418 } finally { 1419 mPropertyMapLock.writeLock().unlock(); 1420 } 1421 } 1422 } 1423 1424 static class MultiIntValuesHolder extends PropertyValuesHolder { 1425 private int mJniSetter; 1426 private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap = 1427 new HashMap<Class, HashMap<String, Integer>>(); 1428 1429 public MultiIntValuesHolder(String propertyName, TypeConverter converter, 1430 TypeEvaluator evaluator, Object... values) { 1431 super(propertyName); 1432 setConverter(converter); 1433 setObjectValues(values); 1434 setEvaluator(evaluator); 1435 } 1436 1437 public MultiIntValuesHolder(String propertyName, TypeConverter converter, 1438 TypeEvaluator evaluator, KeyframeSet keyframeSet) { 1439 super(propertyName); 1440 setConverter(converter); 1441 mKeyframeSet = keyframeSet; 1442 setEvaluator(evaluator); 1443 } 1444 1445 /** 1446 * Internal function to set the value on the target object, using the setter set up 1447 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 1448 * to handle turning the value calculated by ValueAnimator into a value set on the object 1449 * according to the name of the property. 1450 * 1451 * @param target The target object on which the value is set 1452 */ 1453 @Override 1454 void setAnimatedValue(Object target) { 1455 int[] values = (int[]) getAnimatedValue(); 1456 int numParameters = values.length; 1457 if (mJniSetter != 0) { 1458 switch (numParameters) { 1459 case 1: 1460 nCallIntMethod(target, mJniSetter, values[0]); 1461 break; 1462 case 2: 1463 nCallTwoIntMethod(target, mJniSetter, values[0], values[1]); 1464 break; 1465 case 4: 1466 nCallFourIntMethod(target, mJniSetter, values[0], values[1], 1467 values[2], values[3]); 1468 break; 1469 default: { 1470 nCallMultipleIntMethod(target, mJniSetter, values); 1471 break; 1472 } 1473 } 1474 } 1475 } 1476 1477 /** 1478 * Internal function (called from ObjectAnimator) to set up the setter and getter 1479 * prior to running the animation. No getter can be used for multiple parameters. 1480 * 1481 * @param target The object on which the setter exists. 1482 */ 1483 @Override 1484 void setupSetterAndGetter(Object target) { 1485 setupSetter(target.getClass()); 1486 } 1487 1488 @Override 1489 void setupSetter(Class targetClass) { 1490 if (mJniSetter != 0) { 1491 return; 1492 } 1493 try { 1494 mPropertyMapLock.writeLock().lock(); 1495 HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass); 1496 if (propertyMap != null) { 1497 Integer jniSetterInteger = propertyMap.get(mPropertyName); 1498 if (jniSetterInteger != null) { 1499 mJniSetter = jniSetterInteger; 1500 } 1501 } 1502 if (mJniSetter == 0) { 1503 String methodName = getMethodName("set", mPropertyName); 1504 calculateValue(0f); 1505 int[] values = (int[]) getAnimatedValue(); 1506 int numParams = values.length; 1507 try { 1508 mJniSetter = nGetMultipleIntMethod(targetClass, methodName, numParams); 1509 } catch (NoSuchMethodError e) { 1510 // try without the 'set' prefix 1511 mJniSetter = nGetMultipleIntMethod(targetClass, mPropertyName, numParams); 1512 } 1513 if (mJniSetter != 0) { 1514 if (propertyMap == null) { 1515 propertyMap = new HashMap<String, Integer>(); 1516 sJNISetterPropertyMap.put(targetClass, propertyMap); 1517 } 1518 propertyMap.put(mPropertyName, mJniSetter); 1519 } 1520 } 1521 } finally { 1522 mPropertyMapLock.writeLock().unlock(); 1523 } 1524 } 1525 } 1526 1527 /* Path interpolation relies on approximating the Path as a series of line segments. 1528 The line segments are recursively divided until there is less than 1/2 pixel error 1529 between the lines and the curve. Each point of the line segment is converted 1530 to a Keyframe and a linear interpolation between Keyframes creates a good approximation 1531 of the curve. 1532 1533 The fraction for each Keyframe is the length along the Path to the point, divided by 1534 the total Path length. Two points may have the same fraction in the case of a move 1535 command causing a disjoint Path. 1536 1537 The value for each Keyframe is either the point as a PointF or one of the x or y 1538 coordinates as an int or float. In the latter case, two Keyframes are generated for 1539 each point that have the same fraction. */ 1540 1541 /** 1542 * Returns separate Keyframes arrays for the x and y coordinates along a Path. If 1543 * isInt is true, the Keyframes will be IntKeyframes, otherwise they will be FloatKeyframes. 1544 * The element at index 0 are the x coordinate Keyframes and element at index 1 are the 1545 * y coordinate Keyframes. The returned values can be linearly interpolated and get less 1546 * than 1/2 pixel error. 1547 */ 1548 static Keyframe[][] createKeyframes(Path path, boolean isInt) { 1549 if (path == null || path.isEmpty()) { 1550 throw new IllegalArgumentException("The path must not be null or empty"); 1551 } 1552 float[] pointComponents = path.approximate(0.5f); 1553 1554 int numPoints = pointComponents.length / 3; 1555 1556 Keyframe[][] keyframes = new Keyframe[2][]; 1557 keyframes[0] = new Keyframe[numPoints]; 1558 keyframes[1] = new Keyframe[numPoints]; 1559 int componentIndex = 0; 1560 for (int i = 0; i < numPoints; i++) { 1561 float fraction = pointComponents[componentIndex++]; 1562 float x = pointComponents[componentIndex++]; 1563 float y = pointComponents[componentIndex++]; 1564 if (isInt) { 1565 keyframes[0][i] = Keyframe.ofInt(fraction, Math.round(x)); 1566 keyframes[1][i] = Keyframe.ofInt(fraction, Math.round(y)); 1567 } else { 1568 keyframes[0][i] = Keyframe.ofFloat(fraction, x); 1569 keyframes[1][i] = Keyframe.ofFloat(fraction, y); 1570 } 1571 } 1572 return keyframes; 1573 } 1574 1575 /** 1576 * Returns PointF Keyframes for a Path. The resulting points can be linearly interpolated 1577 * with less than 1/2 pixel in error. 1578 */ 1579 private static Keyframe[] createKeyframes(Path path) { 1580 if (path == null || path.isEmpty()) { 1581 throw new IllegalArgumentException("The path must not be null or empty"); 1582 } 1583 float[] pointComponents = path.approximate(0.5f); 1584 1585 int numPoints = pointComponents.length / 3; 1586 1587 Keyframe[] keyframes = new Keyframe[numPoints]; 1588 int componentIndex = 0; 1589 for (int i = 0; i < numPoints; i++) { 1590 float fraction = pointComponents[componentIndex++]; 1591 float x = pointComponents[componentIndex++]; 1592 float y = pointComponents[componentIndex++]; 1593 keyframes[i] = Keyframe.ofObject(fraction, new PointF(x, y)); 1594 } 1595 return keyframes; 1596 } 1597 1598 /** 1599 * Convert from PointF to float[] for multi-float setters along a Path. 1600 */ 1601 private static class PointFToFloatArray extends TypeConverter<PointF, float[]> { 1602 private float[] mCoordinates = new float[2]; 1603 1604 public PointFToFloatArray() { 1605 super(PointF.class, float[].class); 1606 } 1607 1608 @Override 1609 public float[] convert(PointF value) { 1610 mCoordinates[0] = value.x; 1611 mCoordinates[1] = value.y; 1612 return mCoordinates; 1613 } 1614 }; 1615 1616 /** 1617 * Convert from PointF to int[] for multi-int setters along a Path. 1618 */ 1619 private static class PointFToIntArray extends TypeConverter<PointF, int[]> { 1620 private int[] mCoordinates = new int[2]; 1621 1622 public PointFToIntArray() { 1623 super(PointF.class, int[].class); 1624 } 1625 1626 @Override 1627 public int[] convert(PointF value) { 1628 mCoordinates[0] = Math.round(value.x); 1629 mCoordinates[1] = Math.round(value.y); 1630 return mCoordinates; 1631 } 1632 }; 1633 1634 native static private int nGetIntMethod(Class targetClass, String methodName); 1635 native static private int nGetFloatMethod(Class targetClass, String methodName); 1636 native static private int nGetMultipleIntMethod(Class targetClass, String methodName, 1637 int numParams); 1638 native static private int nGetMultipleFloatMethod(Class targetClass, String methodName, 1639 int numParams); 1640 native static private void nCallIntMethod(Object target, int methodID, int arg); 1641 native static private void nCallFloatMethod(Object target, int methodID, float arg); 1642 native static private void nCallTwoIntMethod(Object target, int methodID, int arg1, int arg2); 1643 native static private void nCallFourIntMethod(Object target, int methodID, int arg1, int arg2, 1644 int arg3, int arg4); 1645 native static private void nCallMultipleIntMethod(Object target, int methodID, int[] args); 1646 native static private void nCallTwoFloatMethod(Object target, int methodID, float arg1, 1647 float arg2); 1648 native static private void nCallFourFloatMethod(Object target, int methodID, float arg1, 1649 float arg2, float arg3, float arg4); 1650 native static private void nCallMultipleFloatMethod(Object target, int methodID, float[] args); 1651} 1652