PropertyValuesHolder.java revision d422dc358f0100106dc07d7b903201eb9b043b11
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.ArrayList; 29import java.util.HashMap; 30import java.util.List; 31import 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 */ 39public 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 // This lock is used to ensure that only one thread is accessing the property maps 109 // at a time. 110 final ReentrantReadWriteLock mPropertyMapLock = new ReentrantReadWriteLock(); 111 112 // Used to pass single value to varargs parameter in setter invocation 113 final Object[] mTmpValueArray = new Object[1]; 114 115 /** 116 * The type evaluator used to calculate the animated values. This evaluator is determined 117 * automatically based on the type of the start/end objects passed into the constructor, 118 * but the system only knows about the primitive types int and float. Any other 119 * type will need to set the evaluator to a custom evaluator for that type. 120 */ 121 private TypeEvaluator mEvaluator; 122 123 /** 124 * The value most recently calculated by calculateValue(). This is set during 125 * that function and might be retrieved later either by ValueAnimator.animatedValue() or 126 * by the property-setting logic in ObjectAnimator.animatedValue(). 127 */ 128 private Object mAnimatedValue; 129 130 /** 131 * Converts from the source Object type to the setter Object type. 132 */ 133 private TypeConverter mConverter; 134 135 /** 136 * Internal utility constructor, used by the factory methods to set the property name. 137 * @param propertyName The name of the property for this holder. 138 */ 139 private PropertyValuesHolder(String propertyName) { 140 mPropertyName = propertyName; 141 } 142 143 /** 144 * Internal utility constructor, used by the factory methods to set the property. 145 * @param property The property for this holder. 146 */ 147 private PropertyValuesHolder(Property property) { 148 mProperty = property; 149 if (property != null) { 150 mPropertyName = property.getName(); 151 } 152 } 153 154 /** 155 * Constructs and returns a PropertyValuesHolder with a given property name and 156 * set of int values. 157 * @param propertyName The name of the property being animated. 158 * @param values The values that the named property will animate between. 159 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 160 */ 161 public static PropertyValuesHolder ofInt(String propertyName, int... values) { 162 return new IntPropertyValuesHolder(propertyName, values); 163 } 164 165 /** 166 * Constructs and returns a PropertyValuesHolder with a given property and 167 * set of int values. 168 * @param property The property being animated. Should not be null. 169 * @param values The values that the property will animate between. 170 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 171 */ 172 public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) { 173 return new IntPropertyValuesHolder(property, values); 174 } 175 176 /** 177 * Constructs and returns a PropertyValuesHolder with a given property name and 178 * set of <code>int[]</code> values. At least two <code>int[]</code> values must be supplied, 179 * a start and end value. If more values are supplied, the values will be animated from the 180 * start, through all intermediate values to the end value. When used with ObjectAnimator, 181 * the elements of the array represent the parameters of the setter function. 182 * 183 * @param propertyName The name of the property being animated. Can also be the 184 * case-sensitive name of the entire setter method. Should not be null. 185 * @param values The values that the property will animate between. 186 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 187 * @see IntArrayEvaluator#IntArrayEvaluator(int[]) 188 * @see ObjectAnimator#ofMultiInt(Object, String, TypeConverter, TypeEvaluator, Object[]) 189 */ 190 public static PropertyValuesHolder ofMultiInt(String propertyName, int[][] values) { 191 if (values.length < 2) { 192 throw new IllegalArgumentException("At least 2 values must be supplied"); 193 } 194 int numParameters = 0; 195 for (int i = 0; i < values.length; i++) { 196 if (values[i] == null) { 197 throw new IllegalArgumentException("values must not be null"); 198 } 199 int length = values[i].length; 200 if (i == 0) { 201 numParameters = length; 202 } else if (length != numParameters) { 203 throw new IllegalArgumentException("Values must all have the same length"); 204 } 205 } 206 IntArrayEvaluator evaluator = new IntArrayEvaluator(new int[numParameters]); 207 return new MultiIntValuesHolder(propertyName, null, evaluator, (Object[]) values); 208 } 209 210 /** 211 * Constructs and returns a PropertyValuesHolder with a given property name to use 212 * as a multi-int setter. The values are animated along the path, with the first 213 * parameter of the setter set to the x coordinate and the second set to the y coordinate. 214 * 215 * @param propertyName The name of the property being animated. Can also be the 216 * case-sensitive name of the entire setter method. Should not be null. 217 * The setter must take exactly two <code>int</code> parameters. 218 * @param path The Path along which the values should be animated. 219 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 220 * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...) 221 */ 222 public static PropertyValuesHolder ofMultiInt(String propertyName, Path path) { 223 Keyframes keyframes = KeyframeSet.ofPath(path); 224 PointFToIntArray converter = new PointFToIntArray(); 225 return new MultiIntValuesHolder(propertyName, converter, null, keyframes); 226 } 227 228 /** 229 * Constructs and returns a PropertyValuesHolder with a given property and 230 * set of Object values for use with ObjectAnimator multi-value setters. The Object 231 * values are converted to <code>int[]</code> using the converter. 232 * 233 * @param propertyName The property being animated or complete name of the setter. 234 * Should not be null. 235 * @param converter Used to convert the animated value to setter parameters. 236 * @param evaluator A TypeEvaluator that will be called on each animation frame to 237 * provide the necessary interpolation between the Object values to derive the animated 238 * value. 239 * @param values The values that the property will animate between. 240 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 241 * @see ObjectAnimator#ofMultiInt(Object, String, TypeConverter, TypeEvaluator, Object[]) 242 * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...) 243 */ 244 public static <V> PropertyValuesHolder ofMultiInt(String propertyName, 245 TypeConverter<V, int[]> converter, TypeEvaluator<V> evaluator, V... values) { 246 return new MultiIntValuesHolder(propertyName, converter, evaluator, values); 247 } 248 249 /** 250 * Constructs and returns a PropertyValuesHolder object with the specified property name or 251 * setter name for use in a multi-int setter function using ObjectAnimator. The values can be 252 * of any type, but the type should be consistent so that the supplied 253 * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The 254 * <code>converter</code> converts the values to parameters in the setter function. 255 * 256 * <p>At least two values must be supplied, a start and an end value.</p> 257 * 258 * @param propertyName The name of the property to associate with the set of values. This 259 * may also be the complete name of a setter function. 260 * @param converter Converts <code>values</code> into int parameters for the setter. 261 * Can be null if the Keyframes have int[] values. 262 * @param evaluator Used to interpolate between values. 263 * @param values The values at specific fractional times to evaluate between 264 * @return A PropertyValuesHolder for a multi-int parameter setter. 265 */ 266 public static <T> PropertyValuesHolder ofMultiInt(String propertyName, 267 TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, Keyframe... values) { 268 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); 269 return new MultiIntValuesHolder(propertyName, converter, evaluator, keyframeSet); 270 } 271 272 /** 273 * Constructs and returns a PropertyValuesHolder with a given property name and 274 * set of float values. 275 * @param propertyName The name of the property being animated. 276 * @param values The values that the named property will animate between. 277 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 278 */ 279 public static PropertyValuesHolder ofFloat(String propertyName, float... values) { 280 return new FloatPropertyValuesHolder(propertyName, values); 281 } 282 283 /** 284 * Constructs and returns a PropertyValuesHolder with a given property and 285 * set of float values. 286 * @param property The property being animated. Should not be null. 287 * @param values The values that the property will animate between. 288 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 289 */ 290 public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) { 291 return new FloatPropertyValuesHolder(property, values); 292 } 293 294 /** 295 * Constructs and returns a PropertyValuesHolder with a given property name and 296 * set of <code>float[]</code> values. At least two <code>float[]</code> values must be supplied, 297 * a start and end value. If more values are supplied, the values will be animated from the 298 * start, through all intermediate values to the end value. When used with ObjectAnimator, 299 * the elements of the array represent the parameters of the setter function. 300 * 301 * @param propertyName The name of the property being animated. Can also be the 302 * case-sensitive name of the entire setter method. Should not be null. 303 * @param values The values that the property will animate between. 304 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 305 * @see FloatArrayEvaluator#FloatArrayEvaluator(float[]) 306 * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[]) 307 */ 308 public static PropertyValuesHolder ofMultiFloat(String propertyName, float[][] values) { 309 if (values.length < 2) { 310 throw new IllegalArgumentException("At least 2 values must be supplied"); 311 } 312 int numParameters = 0; 313 for (int i = 0; i < values.length; i++) { 314 if (values[i] == null) { 315 throw new IllegalArgumentException("values must not be null"); 316 } 317 int length = values[i].length; 318 if (i == 0) { 319 numParameters = length; 320 } else if (length != numParameters) { 321 throw new IllegalArgumentException("Values must all have the same length"); 322 } 323 } 324 FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[numParameters]); 325 return new MultiFloatValuesHolder(propertyName, null, evaluator, (Object[]) values); 326 } 327 328 /** 329 * Constructs and returns a PropertyValuesHolder with a given property name to use 330 * as a multi-float setter. The values are animated along the path, with the first 331 * parameter of the setter set to the x coordinate and the second set to the y coordinate. 332 * 333 * @param propertyName The name of the property being animated. Can also be the 334 * case-sensitive name of the entire setter method. Should not be null. 335 * The setter must take exactly two <code>float</code> parameters. 336 * @param path The Path along which the values should be animated. 337 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 338 * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...) 339 */ 340 public static PropertyValuesHolder ofMultiFloat(String propertyName, Path path) { 341 Keyframes keyframes = KeyframeSet.ofPath(path); 342 PointFToFloatArray converter = new PointFToFloatArray(); 343 return new MultiFloatValuesHolder(propertyName, converter, null, keyframes); 344 } 345 346 /** 347 * Constructs and returns a PropertyValuesHolder with a given property and 348 * set of Object values for use with ObjectAnimator multi-value setters. The Object 349 * values are converted to <code>float[]</code> using the converter. 350 * 351 * @param propertyName The property being animated or complete name of the setter. 352 * Should not be null. 353 * @param converter Used to convert the animated value to setter parameters. 354 * @param evaluator A TypeEvaluator that will be called on each animation frame to 355 * provide the necessary interpolation between the Object values to derive the animated 356 * value. 357 * @param values The values that the property will animate between. 358 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 359 * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[]) 360 */ 361 public static <V> PropertyValuesHolder ofMultiFloat(String propertyName, 362 TypeConverter<V, float[]> converter, TypeEvaluator<V> evaluator, V... values) { 363 return new MultiFloatValuesHolder(propertyName, converter, evaluator, values); 364 } 365 366 /** 367 * Constructs and returns a PropertyValuesHolder object with the specified property name or 368 * setter name for use in a multi-float setter function using ObjectAnimator. The values can be 369 * of any type, but the type should be consistent so that the supplied 370 * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The 371 * <code>converter</code> converts the values to parameters in the setter function. 372 * 373 * <p>At least two values must be supplied, a start and an end value.</p> 374 * 375 * @param propertyName The name of the property to associate with the set of values. This 376 * may also be the complete name of a setter function. 377 * @param converter Converts <code>values</code> into float parameters for the setter. 378 * Can be null if the Keyframes have float[] values. 379 * @param evaluator Used to interpolate between values. 380 * @param values The values at specific fractional times to evaluate between 381 * @return A PropertyValuesHolder for a multi-float parameter setter. 382 */ 383 public static <T> PropertyValuesHolder ofMultiFloat(String propertyName, 384 TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, Keyframe... values) { 385 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); 386 return new MultiFloatValuesHolder(propertyName, converter, evaluator, keyframeSet); 387 } 388 389 /** 390 * Constructs and returns a PropertyValuesHolder with a given property name and 391 * set of Object values. This variant also takes a TypeEvaluator because the system 392 * cannot automatically interpolate between objects of unknown type. 393 * 394 * @param propertyName The name of the property being animated. 395 * @param evaluator A TypeEvaluator that will be called on each animation frame to 396 * provide the necessary interpolation between the Object values to derive the animated 397 * value. 398 * @param values The values that the named property will animate between. 399 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 400 */ 401 public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator, 402 Object... values) { 403 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); 404 pvh.setObjectValues(values); 405 pvh.setEvaluator(evaluator); 406 return pvh; 407 } 408 409 /** 410 * Constructs and returns a PropertyValuesHolder with a given property name and 411 * a Path along which the values should be animated. This variant supports a 412 * <code>TypeConverter</code> to convert from <code>PointF</code> to the target 413 * type. 414 * 415 * <p>The PointF passed to <code>converter</code> or <code>property</code>, if 416 * <code>converter</code> is <code>null</code>, is reused on each animation frame and should 417 * not be stored by the setter or TypeConverter.</p> 418 * 419 * @param propertyName The name of the property being animated. 420 * @param converter Converts a PointF to the type associated with the setter. May be 421 * null if conversion is unnecessary. 422 * @param path The Path along which the values should be animated. 423 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 424 */ 425 public static PropertyValuesHolder ofObject(String propertyName, 426 TypeConverter<PointF, ?> converter, Path path) { 427 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); 428 pvh.mKeyframes = KeyframeSet.ofPath(path); 429 pvh.mValueType = PointF.class; 430 pvh.setConverter(converter); 431 return pvh; 432 } 433 434 /** 435 * Constructs and returns a PropertyValuesHolder with a given property and 436 * set of Object values. This variant also takes a TypeEvaluator because the system 437 * cannot automatically interpolate between objects of unknown type. 438 * 439 * @param property The property being animated. Should not be null. 440 * @param evaluator A TypeEvaluator that will be called on each animation frame to 441 * provide the necessary interpolation between the Object values to derive the animated 442 * value. 443 * @param values The values that the property will animate between. 444 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 445 */ 446 public static <V> PropertyValuesHolder ofObject(Property property, 447 TypeEvaluator<V> evaluator, V... values) { 448 PropertyValuesHolder pvh = new PropertyValuesHolder(property); 449 pvh.setObjectValues(values); 450 pvh.setEvaluator(evaluator); 451 return pvh; 452 } 453 454 /** 455 * Constructs and returns a PropertyValuesHolder with a given property and 456 * set of Object values. This variant also takes a TypeEvaluator because the system 457 * cannot automatically interpolate between objects of unknown type. This variant also 458 * takes a <code>TypeConverter</code> to convert from animated values to the type 459 * of the property. If only one value is supplied, the <code>TypeConverter</code> 460 * must be a {@link android.animation.BidirectionalTypeConverter} to retrieve the current 461 * value. 462 * 463 * @param property The property being animated. Should not be null. 464 * @param converter Converts the animated object to the Property type. 465 * @param evaluator A TypeEvaluator that will be called on each animation frame to 466 * provide the necessary interpolation between the Object values to derive the animated 467 * value. 468 * @param values The values that the property will animate between. 469 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 470 * @see #setConverter(TypeConverter) 471 * @see TypeConverter 472 */ 473 public static <T, V> PropertyValuesHolder ofObject(Property<?, V> property, 474 TypeConverter<T, V> converter, TypeEvaluator<T> evaluator, T... values) { 475 PropertyValuesHolder pvh = new PropertyValuesHolder(property); 476 pvh.setConverter(converter); 477 pvh.setObjectValues(values); 478 pvh.setEvaluator(evaluator); 479 return pvh; 480 } 481 482 /** 483 * Constructs and returns a PropertyValuesHolder with a given property and 484 * a Path along which the values should be animated. This variant supports a 485 * <code>TypeConverter</code> to convert from <code>PointF</code> to the target 486 * type. 487 * 488 * <p>The PointF passed to <code>converter</code> or <code>property</code>, if 489 * <code>converter</code> is <code>null</code>, is reused on each animation frame and should 490 * not be stored by the setter or TypeConverter.</p> 491 * 492 * @param property The property being animated. Should not be null. 493 * @param converter Converts a PointF to the type associated with the setter. May be 494 * null if conversion is unnecessary. 495 * @param path The Path along which the values should be animated. 496 * @return PropertyValuesHolder The constructed PropertyValuesHolder object. 497 */ 498 public static <V> PropertyValuesHolder ofObject(Property<?, V> property, 499 TypeConverter<PointF, V> converter, Path path) { 500 PropertyValuesHolder pvh = new PropertyValuesHolder(property); 501 pvh.mKeyframes = KeyframeSet.ofPath(path); 502 pvh.mValueType = PointF.class; 503 pvh.setConverter(converter); 504 return pvh; 505 } 506 507 /** 508 * Constructs and returns a PropertyValuesHolder object with the specified property name and set 509 * of values. These values can be of any type, but the type should be consistent so that 510 * an appropriate {@link android.animation.TypeEvaluator} can be found that matches 511 * the common type. 512 * <p>If there is only one value, it is assumed to be the end value of an animation, 513 * and an initial value will be derived, if possible, by calling a getter function 514 * on the object. Also, if any value is null, the value will be filled in when the animation 515 * starts in the same way. This mechanism of automatically getting null values only works 516 * if the PropertyValuesHolder object is used in conjunction 517 * {@link ObjectAnimator}, and with a getter function 518 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has 519 * no way of determining what the value should be. 520 * @param propertyName The name of the property associated with this set of values. This 521 * can be the actual property name to be used when using a ObjectAnimator object, or 522 * just a name used to get animated values, such as if this object is used with an 523 * ValueAnimator object. 524 * @param values The set of values to animate between. 525 */ 526 public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) { 527 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); 528 return ofKeyframes(propertyName, keyframeSet); 529 } 530 531 /** 532 * Constructs and returns a PropertyValuesHolder object with the specified property and set 533 * of values. These values can be of any type, but the type should be consistent so that 534 * an appropriate {@link android.animation.TypeEvaluator} can be found that matches 535 * the common type. 536 * <p>If there is only one value, it is assumed to be the end value of an animation, 537 * and an initial value will be derived, if possible, by calling the property's 538 * {@link android.util.Property#get(Object)} function. 539 * Also, if any value is null, the value will be filled in when the animation 540 * starts in the same way. This mechanism of automatically getting null values only works 541 * if the PropertyValuesHolder object is used in conjunction with 542 * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has 543 * no way of determining what the value should be. 544 * @param property The property associated with this set of values. Should not be null. 545 * @param values The set of values to animate between. 546 */ 547 public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) { 548 KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); 549 return ofKeyframes(property, keyframeSet); 550 } 551 552 static PropertyValuesHolder ofKeyframes(String propertyName, Keyframes keyframes) { 553 if (keyframes instanceof Keyframes.IntKeyframes) { 554 return new IntPropertyValuesHolder(propertyName, (Keyframes.IntKeyframes) keyframes); 555 } else if (keyframes instanceof Keyframes.FloatKeyframes) { 556 return new FloatPropertyValuesHolder(propertyName, 557 (Keyframes.FloatKeyframes) keyframes); 558 } else { 559 PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); 560 pvh.mKeyframes = keyframes; 561 pvh.mValueType = keyframes.getType(); 562 return pvh; 563 } 564 } 565 566 static PropertyValuesHolder ofKeyframes(Property property, Keyframes keyframes) { 567 if (keyframes instanceof Keyframes.IntKeyframes) { 568 return new IntPropertyValuesHolder(property, (Keyframes.IntKeyframes) keyframes); 569 } else if (keyframes instanceof Keyframes.FloatKeyframes) { 570 return new FloatPropertyValuesHolder(property, (Keyframes.FloatKeyframes) keyframes); 571 } else { 572 PropertyValuesHolder pvh = new PropertyValuesHolder(property); 573 pvh.mKeyframes = keyframes; 574 pvh.mValueType = keyframes.getType(); 575 return pvh; 576 } 577 } 578 579 /** 580 * Set the animated values for this object to this set of ints. 581 * If there is only one value, it is assumed to be the end value of an animation, 582 * and an initial value will be derived, if possible, by calling a getter function 583 * on the object. Also, if any value is null, the value will be filled in when the animation 584 * starts in the same way. This mechanism of automatically getting null values only works 585 * if the PropertyValuesHolder object is used in conjunction 586 * {@link ObjectAnimator}, and with a getter function 587 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has 588 * no way of determining what the value should be. 589 * 590 * @param values One or more values that the animation will animate between. 591 */ 592 public void setIntValues(int... values) { 593 mValueType = int.class; 594 mKeyframes = KeyframeSet.ofInt(values); 595 } 596 597 /** 598 * Set the animated values for this object to this set of floats. 599 * If there is only one value, it is assumed to be the end value of an animation, 600 * and an initial value will be derived, if possible, by calling a getter function 601 * on the object. Also, if any value is null, the value will be filled in when the animation 602 * starts in the same way. This mechanism of automatically getting null values only works 603 * if the PropertyValuesHolder object is used in conjunction 604 * {@link ObjectAnimator}, and with a getter function 605 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has 606 * no way of determining what the value should be. 607 * 608 * @param values One or more values that the animation will animate between. 609 */ 610 public void setFloatValues(float... values) { 611 mValueType = float.class; 612 mKeyframes = KeyframeSet.ofFloat(values); 613 } 614 615 /** 616 * Set the animated values for this object to this set of Keyframes. 617 * 618 * @param values One or more values that the animation will animate between. 619 */ 620 public void setKeyframes(Keyframe... values) { 621 int numKeyframes = values.length; 622 Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)]; 623 mValueType = ((Keyframe)values[0]).getType(); 624 for (int i = 0; i < numKeyframes; ++i) { 625 keyframes[i] = (Keyframe)values[i]; 626 } 627 mKeyframes = new KeyframeSet(keyframes); 628 } 629 630 /** 631 * Set the animated values for this object to this set of Objects. 632 * If there is only one value, it is assumed to be the end value of an animation, 633 * and an initial value will be derived, if possible, by calling a getter function 634 * on the object. Also, if any value is null, the value will be filled in when the animation 635 * starts in the same way. This mechanism of automatically getting null values only works 636 * if the PropertyValuesHolder object is used in conjunction 637 * {@link ObjectAnimator}, and with a getter function 638 * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has 639 * no way of determining what the value should be. 640 * 641 * @param values One or more values that the animation will animate between. 642 */ 643 public void setObjectValues(Object... values) { 644 mValueType = values[0].getClass(); 645 mKeyframes = KeyframeSet.ofObject(values); 646 if (mEvaluator != null) { 647 mKeyframes.setEvaluator(mEvaluator); 648 } 649 } 650 651 /** 652 * Sets the converter to convert from the values type to the setter's parameter type. 653 * If only one value is supplied, <var>converter</var> must be a 654 * {@link android.animation.BidirectionalTypeConverter}. 655 * @param converter The converter to use to convert values. 656 */ 657 public void setConverter(TypeConverter converter) { 658 mConverter = converter; 659 } 660 661 /** 662 * Determine the setter or getter function using the JavaBeans convention of setFoo or 663 * getFoo for a property named 'foo'. This function figures out what the name of the 664 * function should be and uses reflection to find the Method with that name on the 665 * target object. 666 * 667 * @param targetClass The class to search for the method 668 * @param prefix "set" or "get", depending on whether we need a setter or getter. 669 * @param valueType The type of the parameter (in the case of a setter). This type 670 * is derived from the values set on this PropertyValuesHolder. This type is used as 671 * a first guess at the parameter type, but we check for methods with several different 672 * types to avoid problems with slight mis-matches between supplied values and actual 673 * value types used on the setter. 674 * @return Method the method associated with mPropertyName. 675 */ 676 private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) { 677 // TODO: faster implementation... 678 Method returnVal = null; 679 String methodName = getMethodName(prefix, mPropertyName); 680 Class args[] = null; 681 if (valueType == null) { 682 try { 683 returnVal = targetClass.getMethod(methodName, args); 684 } catch (NoSuchMethodException e) { 685 // Swallow the error, log it later 686 } 687 } else { 688 args = new Class[1]; 689 Class typeVariants[]; 690 if (valueType.equals(Float.class)) { 691 typeVariants = FLOAT_VARIANTS; 692 } else if (valueType.equals(Integer.class)) { 693 typeVariants = INTEGER_VARIANTS; 694 } else if (valueType.equals(Double.class)) { 695 typeVariants = DOUBLE_VARIANTS; 696 } else { 697 typeVariants = new Class[1]; 698 typeVariants[0] = valueType; 699 } 700 for (Class typeVariant : typeVariants) { 701 args[0] = typeVariant; 702 try { 703 returnVal = targetClass.getMethod(methodName, args); 704 if (mConverter == null) { 705 // change the value type to suit 706 mValueType = typeVariant; 707 } 708 return returnVal; 709 } catch (NoSuchMethodException e) { 710 // Swallow the error and keep trying other variants 711 } 712 } 713 // If we got here, then no appropriate function was found 714 } 715 716 if (returnVal == null) { 717 Log.w("PropertyValuesHolder", "Method " + 718 getMethodName(prefix, mPropertyName) + "() with type " + valueType + 719 " not found on target class " + targetClass); 720 } 721 722 return returnVal; 723 } 724 725 726 /** 727 * Returns the setter or getter requested. This utility function checks whether the 728 * requested method exists in the propertyMapMap cache. If not, it calls another 729 * utility function to request the Method from the targetClass directly. 730 * @param targetClass The Class on which the requested method should exist. 731 * @param propertyMapMap The cache of setters/getters derived so far. 732 * @param prefix "set" or "get", for the setter or getter. 733 * @param valueType The type of parameter passed into the method (null for getter). 734 * @return Method the method associated with mPropertyName. 735 */ 736 private Method setupSetterOrGetter(Class targetClass, 737 HashMap<Class, HashMap<String, Method>> propertyMapMap, 738 String prefix, Class valueType) { 739 Method setterOrGetter = null; 740 try { 741 // Have to lock property map prior to reading it, to guard against 742 // another thread putting something in there after we've checked it 743 // but before we've added an entry to it 744 mPropertyMapLock.writeLock().lock(); 745 HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass); 746 if (propertyMap != null) { 747 setterOrGetter = propertyMap.get(mPropertyName); 748 } 749 if (setterOrGetter == null) { 750 setterOrGetter = getPropertyFunction(targetClass, prefix, valueType); 751 if (propertyMap == null) { 752 propertyMap = new HashMap<String, Method>(); 753 propertyMapMap.put(targetClass, propertyMap); 754 } 755 propertyMap.put(mPropertyName, setterOrGetter); 756 } 757 } finally { 758 mPropertyMapLock.writeLock().unlock(); 759 } 760 return setterOrGetter; 761 } 762 763 /** 764 * Utility function to get the setter from targetClass 765 * @param targetClass The Class on which the requested method should exist. 766 */ 767 void setupSetter(Class targetClass) { 768 Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType(); 769 mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType); 770 } 771 772 /** 773 * Utility function to get the getter from targetClass 774 */ 775 private void setupGetter(Class targetClass) { 776 mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null); 777 } 778 779 /** 780 * Internal function (called from ObjectAnimator) to set up the setter and getter 781 * prior to running the animation. If the setter has not been manually set for this 782 * object, it will be derived automatically given the property name, target object, and 783 * types of values supplied. If no getter has been set, it will be supplied iff any of the 784 * supplied values was null. If there is a null value, then the getter (supplied or derived) 785 * will be called to set those null values to the current value of the property 786 * on the target object. 787 * @param target The object on which the setter (and possibly getter) exist. 788 */ 789 void setupSetterAndGetter(Object target) { 790 mKeyframes.invalidateCache(); 791 if (mProperty != null) { 792 // check to make sure that mProperty is on the class of target 793 try { 794 Object testValue = null; 795 List<Keyframe> keyframes = mKeyframes.getKeyframes(); 796 int keyframeCount = keyframes == null ? 0 : keyframes.size(); 797 for (int i = 0; i < keyframeCount; i++) { 798 Keyframe kf = keyframes.get(i); 799 if (!kf.hasValue() || kf.valueWasSetOnStart()) { 800 if (testValue == null) { 801 testValue = convertBack(mProperty.get(target)); 802 } 803 kf.setValue(testValue); 804 kf.setValueWasSetOnStart(true); 805 } 806 } 807 return; 808 } catch (ClassCastException e) { 809 Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() + 810 ") on target object " + target + ". Trying reflection instead"); 811 mProperty = null; 812 } 813 } 814 Class targetClass = target.getClass(); 815 if (mSetter == null) { 816 setupSetter(targetClass); 817 } 818 List<Keyframe> keyframes = mKeyframes.getKeyframes(); 819 int keyframeCount = keyframes == null ? 0 : keyframes.size(); 820 for (int i = 0; i < keyframeCount; i++) { 821 Keyframe kf = keyframes.get(i); 822 if (!kf.hasValue() || kf.valueWasSetOnStart()) { 823 if (mGetter == null) { 824 setupGetter(targetClass); 825 if (mGetter == null) { 826 // Already logged the error - just return to avoid NPE 827 return; 828 } 829 } 830 try { 831 Object value = convertBack(mGetter.invoke(target)); 832 kf.setValue(value); 833 kf.setValueWasSetOnStart(true); 834 } catch (InvocationTargetException e) { 835 Log.e("PropertyValuesHolder", e.toString()); 836 } catch (IllegalAccessException e) { 837 Log.e("PropertyValuesHolder", e.toString()); 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 try { 1182 mPropertyMapLock.writeLock().lock(); 1183 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); 1184 if (propertyMap != null) { 1185 Long jniSetter = propertyMap.get(mPropertyName); 1186 if (jniSetter != null) { 1187 mJniSetter = jniSetter; 1188 } 1189 } 1190 if (mJniSetter == 0) { 1191 String methodName = getMethodName("set", mPropertyName); 1192 mJniSetter = nGetIntMethod(targetClass, methodName); 1193 if (mJniSetter != 0) { 1194 if (propertyMap == null) { 1195 propertyMap = new HashMap<String, Long>(); 1196 sJNISetterPropertyMap.put(targetClass, propertyMap); 1197 } 1198 propertyMap.put(mPropertyName, mJniSetter); 1199 } 1200 } 1201 } catch (NoSuchMethodError e) { 1202 // Couldn't find it via JNI - try reflection next. Probably means the method 1203 // doesn't exist, or the type is wrong. An error will be logged later if 1204 // reflection fails as well. 1205 } finally { 1206 mPropertyMapLock.writeLock().unlock(); 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 try { 1319 mPropertyMapLock.writeLock().lock(); 1320 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); 1321 if (propertyMap != null) { 1322 Long jniSetter = propertyMap.get(mPropertyName); 1323 if (jniSetter != null) { 1324 mJniSetter = jniSetter; 1325 } 1326 } 1327 if (mJniSetter == 0) { 1328 String methodName = getMethodName("set", mPropertyName); 1329 mJniSetter = nGetFloatMethod(targetClass, methodName); 1330 if (mJniSetter != 0) { 1331 if (propertyMap == null) { 1332 propertyMap = new HashMap<String, Long>(); 1333 sJNISetterPropertyMap.put(targetClass, propertyMap); 1334 } 1335 propertyMap.put(mPropertyName, mJniSetter); 1336 } 1337 } 1338 } catch (NoSuchMethodError e) { 1339 // Couldn't find it via JNI - try reflection next. Probably means the method 1340 // doesn't exist, or the type is wrong. An error will be logged later if 1341 // reflection fails as well. 1342 } finally { 1343 mPropertyMapLock.writeLock().unlock(); 1344 } 1345 if (mJniSetter == 0) { 1346 // Couldn't find method through fast JNI approach - just use reflection 1347 super.setupSetter(targetClass); 1348 } 1349 } 1350 1351 } 1352 1353 static class MultiFloatValuesHolder extends PropertyValuesHolder { 1354 private long mJniSetter; 1355 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap = 1356 new HashMap<Class, HashMap<String, Long>>(); 1357 1358 public MultiFloatValuesHolder(String propertyName, TypeConverter converter, 1359 TypeEvaluator evaluator, Object... values) { 1360 super(propertyName); 1361 setConverter(converter); 1362 setObjectValues(values); 1363 setEvaluator(evaluator); 1364 } 1365 1366 public MultiFloatValuesHolder(String propertyName, TypeConverter converter, 1367 TypeEvaluator evaluator, Keyframes keyframes) { 1368 super(propertyName); 1369 setConverter(converter); 1370 mKeyframes = keyframes; 1371 setEvaluator(evaluator); 1372 } 1373 1374 /** 1375 * Internal function to set the value on the target object, using the setter set up 1376 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 1377 * to handle turning the value calculated by ValueAnimator into a value set on the object 1378 * according to the name of the property. 1379 * 1380 * @param target The target object on which the value is set 1381 */ 1382 @Override 1383 void setAnimatedValue(Object target) { 1384 float[] values = (float[]) getAnimatedValue(); 1385 int numParameters = values.length; 1386 if (mJniSetter != 0) { 1387 switch (numParameters) { 1388 case 1: 1389 nCallFloatMethod(target, mJniSetter, values[0]); 1390 break; 1391 case 2: 1392 nCallTwoFloatMethod(target, mJniSetter, values[0], values[1]); 1393 break; 1394 case 4: 1395 nCallFourFloatMethod(target, mJniSetter, values[0], values[1], 1396 values[2], values[3]); 1397 break; 1398 default: { 1399 nCallMultipleFloatMethod(target, mJniSetter, values); 1400 break; 1401 } 1402 } 1403 } 1404 } 1405 1406 /** 1407 * Internal function (called from ObjectAnimator) to set up the setter and getter 1408 * prior to running the animation. No getter can be used for multiple parameters. 1409 * 1410 * @param target The object on which the setter exists. 1411 */ 1412 @Override 1413 void setupSetterAndGetter(Object target) { 1414 setupSetter(target.getClass()); 1415 } 1416 1417 @Override 1418 void setupSetter(Class targetClass) { 1419 if (mJniSetter != 0) { 1420 return; 1421 } 1422 try { 1423 mPropertyMapLock.writeLock().lock(); 1424 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); 1425 if (propertyMap != null) { 1426 Long jniSetterLong = propertyMap.get(mPropertyName); 1427 if (jniSetterLong != null) { 1428 mJniSetter = jniSetterLong; 1429 } 1430 } 1431 if (mJniSetter == 0) { 1432 String methodName = getMethodName("set", mPropertyName); 1433 calculateValue(0f); 1434 float[] values = (float[]) getAnimatedValue(); 1435 int numParams = values.length; 1436 try { 1437 mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams); 1438 } catch (NoSuchMethodError e) { 1439 // try without the 'set' prefix 1440 mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName, numParams); 1441 } 1442 if (mJniSetter != 0) { 1443 if (propertyMap == null) { 1444 propertyMap = new HashMap<String, Long>(); 1445 sJNISetterPropertyMap.put(targetClass, propertyMap); 1446 } 1447 propertyMap.put(mPropertyName, mJniSetter); 1448 } 1449 } 1450 } finally { 1451 mPropertyMapLock.writeLock().unlock(); 1452 } 1453 } 1454 } 1455 1456 static class MultiIntValuesHolder extends PropertyValuesHolder { 1457 private long mJniSetter; 1458 private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap = 1459 new HashMap<Class, HashMap<String, Long>>(); 1460 1461 public MultiIntValuesHolder(String propertyName, TypeConverter converter, 1462 TypeEvaluator evaluator, Object... values) { 1463 super(propertyName); 1464 setConverter(converter); 1465 setObjectValues(values); 1466 setEvaluator(evaluator); 1467 } 1468 1469 public MultiIntValuesHolder(String propertyName, TypeConverter converter, 1470 TypeEvaluator evaluator, Keyframes keyframes) { 1471 super(propertyName); 1472 setConverter(converter); 1473 mKeyframes = keyframes; 1474 setEvaluator(evaluator); 1475 } 1476 1477 /** 1478 * Internal function to set the value on the target object, using the setter set up 1479 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator 1480 * to handle turning the value calculated by ValueAnimator into a value set on the object 1481 * according to the name of the property. 1482 * 1483 * @param target The target object on which the value is set 1484 */ 1485 @Override 1486 void setAnimatedValue(Object target) { 1487 int[] values = (int[]) getAnimatedValue(); 1488 int numParameters = values.length; 1489 if (mJniSetter != 0) { 1490 switch (numParameters) { 1491 case 1: 1492 nCallIntMethod(target, mJniSetter, values[0]); 1493 break; 1494 case 2: 1495 nCallTwoIntMethod(target, mJniSetter, values[0], values[1]); 1496 break; 1497 case 4: 1498 nCallFourIntMethod(target, mJniSetter, values[0], values[1], 1499 values[2], values[3]); 1500 break; 1501 default: { 1502 nCallMultipleIntMethod(target, mJniSetter, values); 1503 break; 1504 } 1505 } 1506 } 1507 } 1508 1509 /** 1510 * Internal function (called from ObjectAnimator) to set up the setter and getter 1511 * prior to running the animation. No getter can be used for multiple parameters. 1512 * 1513 * @param target The object on which the setter exists. 1514 */ 1515 @Override 1516 void setupSetterAndGetter(Object target) { 1517 setupSetter(target.getClass()); 1518 } 1519 1520 @Override 1521 void setupSetter(Class targetClass) { 1522 if (mJniSetter != 0) { 1523 return; 1524 } 1525 try { 1526 mPropertyMapLock.writeLock().lock(); 1527 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); 1528 if (propertyMap != null) { 1529 Long jniSetterLong = propertyMap.get(mPropertyName); 1530 if (jniSetterLong != null) { 1531 mJniSetter = jniSetterLong; 1532 } 1533 } 1534 if (mJniSetter == 0) { 1535 String methodName = getMethodName("set", mPropertyName); 1536 calculateValue(0f); 1537 int[] values = (int[]) getAnimatedValue(); 1538 int numParams = values.length; 1539 try { 1540 mJniSetter = nGetMultipleIntMethod(targetClass, methodName, numParams); 1541 } catch (NoSuchMethodError e) { 1542 // try without the 'set' prefix 1543 mJniSetter = nGetMultipleIntMethod(targetClass, mPropertyName, numParams); 1544 } 1545 if (mJniSetter != 0) { 1546 if (propertyMap == null) { 1547 propertyMap = new HashMap<String, Long>(); 1548 sJNISetterPropertyMap.put(targetClass, propertyMap); 1549 } 1550 propertyMap.put(mPropertyName, mJniSetter); 1551 } 1552 } 1553 } finally { 1554 mPropertyMapLock.writeLock().unlock(); 1555 } 1556 } 1557 } 1558 1559 /** 1560 * Convert from PointF to float[] for multi-float setters along a Path. 1561 */ 1562 private static class PointFToFloatArray extends TypeConverter<PointF, float[]> { 1563 private float[] mCoordinates = new float[2]; 1564 1565 public PointFToFloatArray() { 1566 super(PointF.class, float[].class); 1567 } 1568 1569 @Override 1570 public float[] convert(PointF value) { 1571 mCoordinates[0] = value.x; 1572 mCoordinates[1] = value.y; 1573 return mCoordinates; 1574 } 1575 }; 1576 1577 /** 1578 * Convert from PointF to int[] for multi-int setters along a Path. 1579 */ 1580 private static class PointFToIntArray extends TypeConverter<PointF, int[]> { 1581 private int[] mCoordinates = new int[2]; 1582 1583 public PointFToIntArray() { 1584 super(PointF.class, int[].class); 1585 } 1586 1587 @Override 1588 public int[] convert(PointF value) { 1589 mCoordinates[0] = Math.round(value.x); 1590 mCoordinates[1] = Math.round(value.y); 1591 return mCoordinates; 1592 } 1593 }; 1594 1595 native static private long nGetIntMethod(Class targetClass, String methodName); 1596 native static private long nGetFloatMethod(Class targetClass, String methodName); 1597 native static private long nGetMultipleIntMethod(Class targetClass, String methodName, 1598 int numParams); 1599 native static private long nGetMultipleFloatMethod(Class targetClass, String methodName, 1600 int numParams); 1601 native static private void nCallIntMethod(Object target, long methodID, int arg); 1602 native static private void nCallFloatMethod(Object target, long methodID, float arg); 1603 native static private void nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2); 1604 native static private void nCallFourIntMethod(Object target, long methodID, int arg1, int arg2, 1605 int arg3, int arg4); 1606 native static private void nCallMultipleIntMethod(Object target, long methodID, int[] args); 1607 native static private void nCallTwoFloatMethod(Object target, long methodID, float arg1, 1608 float arg2); 1609 native static private void nCallFourFloatMethod(Object target, long methodID, float arg1, 1610 float arg2, float arg3, float arg4); 1611 native static private void nCallMultipleFloatMethod(Object target, long methodID, float[] args); 1612} 1613