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