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