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