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.util.Log; 20import android.util.Property; 21 22import java.lang.reflect.Method; 23import java.util.ArrayList; 24 25/** 26 * This subclass of {@link ValueAnimator} provides support for animating properties on target objects. 27 * The constructors of this class take parameters to define the target object that will be animated 28 * as well as the name of the property that will be animated. Appropriate set/get functions 29 * are then determined internally and the animation will call these functions as necessary to 30 * animate the property. 31 * 32 * <div class="special reference"> 33 * <h3>Developer Guides</h3> 34 * <p>For more information about animating with {@code ObjectAnimator}, read the 35 * <a href="{@docRoot}guide/topics/graphics/prop-animation.html#object-animator">Property 36 * Animation</a> developer guide.</p> 37 * </div> 38 * 39 * @see #setPropertyName(String) 40 * 41 */ 42public final class ObjectAnimator extends ValueAnimator { 43 private static final boolean DBG = false; 44 45 // The target object on which the property exists, set in the constructor 46 private Object mTarget; 47 48 private String mPropertyName; 49 50 private Property mProperty; 51 52 /** 53 * Sets the name of the property that will be animated. This name is used to derive 54 * a setter function that will be called to set animated values. 55 * For example, a property name of <code>foo</code> will result 56 * in a call to the function <code>setFoo()</code> on the target object. If either 57 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 58 * also be derived and called. 59 * 60 * <p>For best performance of the mechanism that calls the setter function determined by the 61 * name of the property being animated, use <code>float</code> or <code>int</code> typed values, 62 * and make the setter function for those properties have a <code>void</code> return value. This 63 * will cause the code to take an optimized path for these constrained circumstances. Other 64 * property types and return types will work, but will have more overhead in processing 65 * the requests due to normal reflection mechanisms.</p> 66 * 67 * <p>Note that the setter function derived from this property name 68 * must take the same parameter type as the 69 * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to 70 * the setter function will fail.</p> 71 * 72 * <p>If this ObjectAnimator has been set up to animate several properties together, 73 * using more than one PropertyValuesHolder objects, then setting the propertyName simply 74 * sets the propertyName in the first of those PropertyValuesHolder objects.</p> 75 * 76 * @param propertyName The name of the property being animated. Should not be null. 77 */ 78 public void setPropertyName(String propertyName) { 79 // mValues could be null if this is being constructed piecemeal. Just record the 80 // propertyName to be used later when setValues() is called if so. 81 if (mValues != null) { 82 PropertyValuesHolder valuesHolder = mValues[0]; 83 String oldName = valuesHolder.getPropertyName(); 84 valuesHolder.setPropertyName(propertyName); 85 mValuesMap.remove(oldName); 86 mValuesMap.put(propertyName, valuesHolder); 87 } 88 mPropertyName = propertyName; 89 // New property/values/target should cause re-initialization prior to starting 90 mInitialized = false; 91 } 92 93 /** 94 * Sets the property that will be animated. Property objects will take precedence over 95 * properties specified by the {@link #setPropertyName(String)} method. Animations should 96 * be set up to use one or the other, not both. 97 * 98 * @param property The property being animated. Should not be null. 99 */ 100 public void setProperty(Property property) { 101 // mValues could be null if this is being constructed piecemeal. Just record the 102 // propertyName to be used later when setValues() is called if so. 103 if (mValues != null) { 104 PropertyValuesHolder valuesHolder = mValues[0]; 105 String oldName = valuesHolder.getPropertyName(); 106 valuesHolder.setProperty(property); 107 mValuesMap.remove(oldName); 108 mValuesMap.put(mPropertyName, valuesHolder); 109 } 110 if (mProperty != null) { 111 mPropertyName = property.getName(); 112 } 113 mProperty = property; 114 // New property/values/target should cause re-initialization prior to starting 115 mInitialized = false; 116 } 117 118 /** 119 * Gets the name of the property that will be animated. This name will be used to derive 120 * a setter function that will be called to set animated values. 121 * For example, a property name of <code>foo</code> will result 122 * in a call to the function <code>setFoo()</code> on the target object. If either 123 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 124 * also be derived and called. 125 */ 126 public String getPropertyName() { 127 return mPropertyName; 128 } 129 130 /** 131 * Creates a new ObjectAnimator object. This default constructor is primarily for 132 * use internally; the other constructors which take parameters are more generally 133 * useful. 134 */ 135 public ObjectAnimator() { 136 } 137 138 /** 139 * Private utility constructor that initializes the target object and name of the 140 * property being animated. 141 * 142 * @param target The object whose property is to be animated. This object should 143 * have a public method on it called <code>setName()</code>, where <code>name</code> is 144 * the value of the <code>propertyName</code> parameter. 145 * @param propertyName The name of the property being animated. 146 */ 147 private ObjectAnimator(Object target, String propertyName) { 148 mTarget = target; 149 setPropertyName(propertyName); 150 } 151 152 /** 153 * Private utility constructor that initializes the target object and property being animated. 154 * 155 * @param target The object whose property is to be animated. 156 * @param property The property being animated. 157 */ 158 private <T> ObjectAnimator(T target, Property<T, ?> property) { 159 mTarget = target; 160 setProperty(property); 161 } 162 163 /** 164 * Constructs and returns an ObjectAnimator that animates between int values. A single 165 * value implies that that value is the one being animated to. Two values imply a starting 166 * and ending values. More than two values imply a starting value, values to animate through 167 * along the way, and an ending value (these values will be distributed evenly across 168 * the duration of the animation). 169 * 170 * @param target The object whose property is to be animated. This object should 171 * have a public method on it called <code>setName()</code>, where <code>name</code> is 172 * the value of the <code>propertyName</code> parameter. 173 * @param propertyName The name of the property being animated. 174 * @param values A set of values that the animation will animate between over time. 175 * @return An ObjectAnimator object that is set up to animate between the given values. 176 */ 177 public static ObjectAnimator ofInt(Object target, String propertyName, int... values) { 178 ObjectAnimator anim = new ObjectAnimator(target, propertyName); 179 anim.setIntValues(values); 180 return anim; 181 } 182 183 /** 184 * Constructs and returns an ObjectAnimator that animates between int values. A single 185 * value implies that that value is the one being animated to. Two values imply a starting 186 * and ending values. More than two values imply a starting value, values to animate through 187 * along the way, and an ending value (these values will be distributed evenly across 188 * the duration of the animation). 189 * 190 * @param target The object whose property is to be animated. 191 * @param property The property being animated. 192 * @param values A set of values that the animation will animate between over time. 193 * @return An ObjectAnimator object that is set up to animate between the given values. 194 */ 195 public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values) { 196 ObjectAnimator anim = new ObjectAnimator(target, property); 197 anim.setIntValues(values); 198 return anim; 199 } 200 201 /** 202 * Constructs and returns an ObjectAnimator that animates between float values. A single 203 * value implies that that value is the one being animated to. Two values imply a starting 204 * and ending values. More than two values imply a starting value, values to animate through 205 * along the way, and an ending value (these values will be distributed evenly across 206 * the duration of the animation). 207 * 208 * @param target The object whose property is to be animated. This object should 209 * have a public method on it called <code>setName()</code>, where <code>name</code> is 210 * the value of the <code>propertyName</code> parameter. 211 * @param propertyName The name of the property being animated. 212 * @param values A set of values that the animation will animate between over time. 213 * @return An ObjectAnimator object that is set up to animate between the given values. 214 */ 215 public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) { 216 ObjectAnimator anim = new ObjectAnimator(target, propertyName); 217 anim.setFloatValues(values); 218 return anim; 219 } 220 221 /** 222 * Constructs and returns an ObjectAnimator that animates between float values. A single 223 * value implies that that value is the one being animated to. Two values imply a starting 224 * and ending values. More than two values imply a starting value, values to animate through 225 * along the way, and an ending value (these values will be distributed evenly across 226 * the duration of the animation). 227 * 228 * @param target The object whose property is to be animated. 229 * @param property The property being animated. 230 * @param values A set of values that the animation will animate between over time. 231 * @return An ObjectAnimator object that is set up to animate between the given values. 232 */ 233 public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property, 234 float... values) { 235 ObjectAnimator anim = new ObjectAnimator(target, property); 236 anim.setFloatValues(values); 237 return anim; 238 } 239 240 /** 241 * Constructs and returns an ObjectAnimator that animates between Object values. A single 242 * value implies that that value is the one being animated to. Two values imply a starting 243 * and ending values. More than two values imply a starting value, values to animate through 244 * along the way, and an ending value (these values will be distributed evenly across 245 * the duration of the animation). 246 * 247 * @param target The object whose property is to be animated. This object should 248 * have a public method on it called <code>setName()</code>, where <code>name</code> is 249 * the value of the <code>propertyName</code> parameter. 250 * @param propertyName The name of the property being animated. 251 * @param evaluator A TypeEvaluator that will be called on each animation frame to 252 * provide the necessary interpolation between the Object values to derive the animated 253 * value. 254 * @param values A set of values that the animation will animate between over time. 255 * @return An ObjectAnimator object that is set up to animate between the given values. 256 */ 257 public static ObjectAnimator ofObject(Object target, String propertyName, 258 TypeEvaluator evaluator, Object... values) { 259 ObjectAnimator anim = new ObjectAnimator(target, propertyName); 260 anim.setObjectValues(values); 261 anim.setEvaluator(evaluator); 262 return anim; 263 } 264 265 /** 266 * Constructs and returns an ObjectAnimator that animates between Object values. A single 267 * value implies that that value is the one being animated to. Two values imply a starting 268 * and ending values. More than two values imply a starting value, values to animate through 269 * along the way, and an ending value (these values will be distributed evenly across 270 * the duration of the animation). 271 * 272 * @param target The object whose property is to be animated. 273 * @param property The property being animated. 274 * @param evaluator A TypeEvaluator that will be called on each animation frame to 275 * provide the necessary interpolation between the Object values to derive the animated 276 * value. 277 * @param values A set of values that the animation will animate between over time. 278 * @return An ObjectAnimator object that is set up to animate between the given values. 279 */ 280 public static <T, V> ObjectAnimator ofObject(T target, Property<T, V> property, 281 TypeEvaluator<V> evaluator, V... values) { 282 ObjectAnimator anim = new ObjectAnimator(target, property); 283 anim.setObjectValues(values); 284 anim.setEvaluator(evaluator); 285 return anim; 286 } 287 288 /** 289 * Constructs and returns an ObjectAnimator that animates between the sets of values specified 290 * in <code>PropertyValueHolder</code> objects. This variant should be used when animating 291 * several properties at once with the same ObjectAnimator, since PropertyValuesHolder allows 292 * you to associate a set of animation values with a property name. 293 * 294 * @param target The object whose property is to be animated. Depending on how the 295 * PropertyValuesObjects were constructed, the target object should either have the {@link 296 * android.util.Property} objects used to construct the PropertyValuesHolder objects or (if the 297 * PropertyValuesHOlder objects were created with property names) the target object should have 298 * public methods on it called <code>setName()</code>, where <code>name</code> is the name of 299 * the property passed in as the <code>propertyName</code> parameter for each of the 300 * PropertyValuesHolder objects. 301 * @param values A set of PropertyValuesHolder objects whose values will be animated between 302 * over time. 303 * @return An ObjectAnimator object that is set up to animate between the given values. 304 */ 305 public static ObjectAnimator ofPropertyValuesHolder(Object target, 306 PropertyValuesHolder... values) { 307 ObjectAnimator anim = new ObjectAnimator(); 308 anim.mTarget = target; 309 anim.setValues(values); 310 return anim; 311 } 312 313 @Override 314 public void setIntValues(int... values) { 315 if (mValues == null || mValues.length == 0) { 316 // No values yet - this animator is being constructed piecemeal. Init the values with 317 // whatever the current propertyName is 318 if (mProperty != null) { 319 setValues(PropertyValuesHolder.ofInt(mProperty, values)); 320 } else { 321 setValues(PropertyValuesHolder.ofInt(mPropertyName, values)); 322 } 323 } else { 324 super.setIntValues(values); 325 } 326 } 327 328 @Override 329 public void setFloatValues(float... values) { 330 if (mValues == null || mValues.length == 0) { 331 // No values yet - this animator is being constructed piecemeal. Init the values with 332 // whatever the current propertyName is 333 if (mProperty != null) { 334 setValues(PropertyValuesHolder.ofFloat(mProperty, values)); 335 } else { 336 setValues(PropertyValuesHolder.ofFloat(mPropertyName, values)); 337 } 338 } else { 339 super.setFloatValues(values); 340 } 341 } 342 343 @Override 344 public void setObjectValues(Object... values) { 345 if (mValues == null || mValues.length == 0) { 346 // No values yet - this animator is being constructed piecemeal. Init the values with 347 // whatever the current propertyName is 348 if (mProperty != null) { 349 setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator)null, values)); 350 } else { 351 setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator)null, values)); 352 } 353 } else { 354 super.setObjectValues(values); 355 } 356 } 357 358 @Override 359 public void start() { 360 if (DBG) { 361 Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration()); 362 for (int i = 0; i < mValues.length; ++i) { 363 PropertyValuesHolder pvh = mValues[i]; 364 ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes; 365 Log.d("ObjectAnimator", " Values[" + i + "]: " + 366 pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " + 367 keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue()); 368 } 369 } 370 super.start(); 371 } 372 373 /** 374 * This function is called immediately before processing the first animation 375 * frame of an animation. If there is a nonzero <code>startDelay</code>, the 376 * function is called after that delay ends. 377 * It takes care of the final initialization steps for the 378 * animation. This includes setting mEvaluator, if the user has not yet 379 * set it up, and the setter/getter methods, if the user did not supply 380 * them. 381 * 382 * <p>Overriders of this method should call the superclass method to cause 383 * internal mechanisms to be set up correctly.</p> 384 */ 385 @Override 386 void initAnimation() { 387 if (!mInitialized) { 388 // mValueType may change due to setter/getter setup; do this before calling super.init(), 389 // which uses mValueType to set up the default type evaluator. 390 int numValues = mValues.length; 391 for (int i = 0; i < numValues; ++i) { 392 mValues[i].setupSetterAndGetter(mTarget); 393 } 394 super.initAnimation(); 395 } 396 } 397 398 /** 399 * Sets the length of the animation. The default duration is 300 milliseconds. 400 * 401 * @param duration The length of the animation, in milliseconds. 402 * @return ObjectAnimator The object called with setDuration(). This return 403 * value makes it easier to compose statements together that construct and then set the 404 * duration, as in 405 * <code>ObjectAnimator.ofInt(target, propertyName, 0, 10).setDuration(500).start()</code>. 406 */ 407 @Override 408 public ObjectAnimator setDuration(long duration) { 409 super.setDuration(duration); 410 return this; 411 } 412 413 414 /** 415 * The target object whose property will be animated by this animation 416 * 417 * @return The object being animated 418 */ 419 public Object getTarget() { 420 return mTarget; 421 } 422 423 /** 424 * Sets the target object whose property will be animated by this animation 425 * 426 * @param target The object being animated 427 */ 428 @Override 429 public void setTarget(Object target) { 430 if (mTarget != target) { 431 final Object oldTarget = mTarget; 432 mTarget = target; 433 if (oldTarget != null && target != null && oldTarget.getClass() == target.getClass()) { 434 return; 435 } 436 // New target type should cause re-initialization prior to starting 437 mInitialized = false; 438 } 439 } 440 441 @Override 442 public void setupStartValues() { 443 initAnimation(); 444 int numValues = mValues.length; 445 for (int i = 0; i < numValues; ++i) { 446 mValues[i].setupStartValue(mTarget); 447 } 448 } 449 450 @Override 451 public void setupEndValues() { 452 initAnimation(); 453 int numValues = mValues.length; 454 for (int i = 0; i < numValues; ++i) { 455 mValues[i].setupEndValue(mTarget); 456 } 457 } 458 459 /** 460 * This method is called with the elapsed fraction of the animation during every 461 * animation frame. This function turns the elapsed fraction into an interpolated fraction 462 * and then into an animated value (from the evaluator. The function is called mostly during 463 * animation updates, but it is also called when the <code>end()</code> 464 * function is called, to set the final value on the property. 465 * 466 * <p>Overrides of this method must call the superclass to perform the calculation 467 * of the animated value.</p> 468 * 469 * @param fraction The elapsed fraction of the animation. 470 */ 471 @Override 472 void animateValue(float fraction) { 473 super.animateValue(fraction); 474 int numValues = mValues.length; 475 for (int i = 0; i < numValues; ++i) { 476 mValues[i].setAnimatedValue(mTarget); 477 } 478 } 479 480 @Override 481 public ObjectAnimator clone() { 482 final ObjectAnimator anim = (ObjectAnimator) super.clone(); 483 return anim; 484 } 485 486 @Override 487 public String toString() { 488 String returnVal = "ObjectAnimator@" + Integer.toHexString(hashCode()) + ", target " + 489 mTarget; 490 if (mValues != null) { 491 for (int i = 0; i < mValues.length; ++i) { 492 returnVal += "\n " + mValues[i].toString(); 493 } 494 } 495 return returnVal; 496 } 497} 498