1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.animation; 18 19 import android.util.Log; 20 import android.util.Property; 21 22 import java.util.ArrayList; 23 24 /** 25 * This subclass of {@link ValueAnimator} provides support for animating properties on target objects. 26 * The constructors of this class take parameters to define the target object that will be animated 27 * as well as the name of the property that will be animated. Appropriate set/get functions 28 * are then determined internally and the animation will call these functions as necessary to 29 * animate the property. 30 * 31 * <div class="special reference"> 32 * <h3>Developer Guides</h3> 33 * <p>For more information about animating with {@code ObjectAnimator}, read the 34 * <a href="{@docRoot}guide/topics/graphics/prop-animation.html#object-animator">Property 35 * Animation</a> developer guide.</p> 36 * </div> 37 * 38 * @see #setPropertyName(String) 39 * 40 */ 41 public final class ObjectAnimator extends ValueAnimator { 42 private static final boolean DBG = false; 43 44 // The target object on which the property exists, set in the constructor 45 private Object mTarget; 46 47 private String mPropertyName; 48 49 private Property mProperty; 50 51 private boolean mAutoCancel = false; 52 53 /** 54 * Sets the name of the property that will be animated. This name is used to derive 55 * a setter function that will be called to set animated values. 56 * For example, a property name of <code>foo</code> will result 57 * in a call to the function <code>setFoo()</code> on the target object. If either 58 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 59 * also be derived and called. 60 * 61 * <p>For best performance of the mechanism that calls the setter function determined by the 62 * name of the property being animated, use <code>float</code> or <code>int</code> typed values, 63 * and make the setter function for those properties have a <code>void</code> return value. This 64 * will cause the code to take an optimized path for these constrained circumstances. Other 65 * property types and return types will work, but will have more overhead in processing 66 * the requests due to normal reflection mechanisms.</p> 67 * 68 * <p>Note that the setter function derived from this property name 69 * must take the same parameter type as the 70 * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to 71 * the setter function will fail.</p> 72 * 73 * <p>If this ObjectAnimator has been set up to animate several properties together, 74 * using more than one PropertyValuesHolder objects, then setting the propertyName simply 75 * sets the propertyName in the first of those PropertyValuesHolder objects.</p> 76 * 77 * @param propertyName The name of the property being animated. Should not be null. 78 */ 79 public void setPropertyName(String propertyName) { 80 // mValues could be null if this is being constructed piecemeal. Just record the 81 // propertyName to be used later when setValues() is called if so. 82 if (mValues != null) { 83 PropertyValuesHolder valuesHolder = mValues[0]; 84 String oldName = valuesHolder.getPropertyName(); 85 valuesHolder.setPropertyName(propertyName); 86 mValuesMap.remove(oldName); 87 mValuesMap.put(propertyName, valuesHolder); 88 } 89 mPropertyName = propertyName; 90 // New property/values/target should cause re-initialization prior to starting 91 mInitialized = false; 92 } 93 94 /** 95 * Sets the property that will be animated. Property objects will take precedence over 96 * properties specified by the {@link #setPropertyName(String)} method. Animations should 97 * be set up to use one or the other, not both. 98 * 99 * @param property The property being animated. Should not be null. 100 */ 101 public void setProperty(Property property) { 102 // mValues could be null if this is being constructed piecemeal. Just record the 103 // propertyName to be used later when setValues() is called if so. 104 if (mValues != null) { 105 PropertyValuesHolder valuesHolder = mValues[0]; 106 String oldName = valuesHolder.getPropertyName(); 107 valuesHolder.setProperty(property); 108 mValuesMap.remove(oldName); 109 mValuesMap.put(mPropertyName, valuesHolder); 110 } 111 if (mProperty != null) { 112 mPropertyName = property.getName(); 113 } 114 mProperty = property; 115 // New property/values/target should cause re-initialization prior to starting 116 mInitialized = false; 117 } 118 119 /** 120 * Gets the name of the property that will be animated. This name will be used to derive 121 * a setter function that will be called to set animated values. 122 * For example, a property name of <code>foo</code> will result 123 * in a call to the function <code>setFoo()</code> on the target object. If either 124 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 125 * also be derived and called. 126 * 127 * <p>If this animator was created with a {@link Property} object instead of the 128 * string name of a property, then this method will return the {@link 129 * Property#getName() name} of that Property object instead. If this animator was 130 * created with one or more {@link PropertyValuesHolder} objects, then this method 131 * will return the {@link PropertyValuesHolder#getPropertyName() name} of that 132 * object (if there was just one) or a comma-separated list of all of the 133 * names (if there are more than one).</p> 134 */ 135 public String getPropertyName() { 136 String propertyName = null; 137 if (mPropertyName != null) { 138 propertyName = mPropertyName; 139 } else if (mProperty != null) { 140 propertyName = mProperty.getName(); 141 } else if (mValues != null && mValues.length > 0) { 142 for (int i = 0; i < mValues.length; ++i) { 143 if (i == 0) { 144 propertyName = ""; 145 } else { 146 propertyName += ","; 147 } 148 propertyName += mValues[i].getPropertyName(); 149 } 150 } 151 return propertyName; 152 } 153 154 @Override 155 String getNameForTrace() { 156 return "animator:" + getPropertyName(); 157 } 158 159 /** 160 * Creates a new ObjectAnimator object. This default constructor is primarily for 161 * use internally; the other constructors which take parameters are more generally 162 * useful. 163 */ 164 public ObjectAnimator() { 165 } 166 167 /** 168 * Private utility constructor that initializes the target object and name of the 169 * property being animated. 170 * 171 * @param target The object whose property is to be animated. This object should 172 * have a public method on it called <code>setName()</code>, where <code>name</code> is 173 * the value of the <code>propertyName</code> parameter. 174 * @param propertyName The name of the property being animated. 175 */ 176 private ObjectAnimator(Object target, String propertyName) { 177 mTarget = target; 178 setPropertyName(propertyName); 179 } 180 181 /** 182 * Private utility constructor that initializes the target object and property being animated. 183 * 184 * @param target The object whose property is to be animated. 185 * @param property The property being animated. 186 */ 187 private <T> ObjectAnimator(T target, Property<T, ?> property) { 188 mTarget = target; 189 setProperty(property); 190 } 191 192 /** 193 * Constructs and returns an ObjectAnimator that animates between int values. A single 194 * value implies that that value is the one being animated to. Two values imply a starting 195 * and ending values. More than two values imply a starting value, values to animate through 196 * along the way, and an ending value (these values will be distributed evenly across 197 * the duration of the animation). 198 * 199 * @param target The object whose property is to be animated. This object should 200 * have a public method on it called <code>setName()</code>, where <code>name</code> is 201 * the value of the <code>propertyName</code> parameter. 202 * @param propertyName The name of the property being animated. 203 * @param values A set of values that the animation will animate between over time. 204 * @return An ObjectAnimator object that is set up to animate between the given values. 205 */ 206 public static ObjectAnimator ofInt(Object target, String propertyName, int... values) { 207 ObjectAnimator anim = new ObjectAnimator(target, propertyName); 208 anim.setIntValues(values); 209 return anim; 210 } 211 212 /** 213 * Constructs and returns an ObjectAnimator that animates between int values. A single 214 * value implies that that value is the one being animated to. Two values imply a starting 215 * and ending values. More than two values imply a starting value, values to animate through 216 * along the way, and an ending value (these values will be distributed evenly across 217 * the duration of the animation). 218 * 219 * @param target The object whose property is to be animated. 220 * @param property The property being animated. 221 * @param values A set of values that the animation will animate between over time. 222 * @return An ObjectAnimator object that is set up to animate between the given values. 223 */ 224 public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values) { 225 ObjectAnimator anim = new ObjectAnimator(target, property); 226 anim.setIntValues(values); 227 return anim; 228 } 229 230 /** 231 * Constructs and returns an ObjectAnimator that animates between float values. A single 232 * value implies that that value is the one being animated to. Two values imply a starting 233 * and ending values. More than two values imply a starting value, values to animate through 234 * along the way, and an ending value (these values will be distributed evenly across 235 * the duration of the animation). 236 * 237 * @param target The object whose property is to be animated. This object should 238 * have a public method on it called <code>setName()</code>, where <code>name</code> is 239 * the value of the <code>propertyName</code> parameter. 240 * @param propertyName The name of the property being animated. 241 * @param values A set of values that the animation will animate between over time. 242 * @return An ObjectAnimator object that is set up to animate between the given values. 243 */ 244 public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) { 245 ObjectAnimator anim = new ObjectAnimator(target, propertyName); 246 anim.setFloatValues(values); 247 return anim; 248 } 249 250 /** 251 * Constructs and returns an ObjectAnimator that animates between float values. A single 252 * value implies that that value is the one being animated to. Two values imply a starting 253 * and ending values. More than two values imply a starting value, values to animate through 254 * along the way, and an ending value (these values will be distributed evenly across 255 * the duration of the animation). 256 * 257 * @param target The object whose property is to be animated. 258 * @param property The property being animated. 259 * @param values A set of values that the animation will animate between over time. 260 * @return An ObjectAnimator object that is set up to animate between the given values. 261 */ 262 public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property, 263 float... values) { 264 ObjectAnimator anim = new ObjectAnimator(target, property); 265 anim.setFloatValues(values); 266 return anim; 267 } 268 269 /** 270 * Constructs and returns an ObjectAnimator that animates between Object values. A single 271 * value implies that that value is the one being animated to. Two values imply a starting 272 * and ending values. More than two values imply a starting value, values to animate through 273 * along the way, and an ending value (these values will be distributed evenly across 274 * the duration of the animation). 275 * 276 * @param target The object whose property is to be animated. This object should 277 * have a public method on it called <code>setName()</code>, where <code>name</code> is 278 * the value of the <code>propertyName</code> parameter. 279 * @param propertyName The name of the property being animated. 280 * @param evaluator A TypeEvaluator that will be called on each animation frame to 281 * provide the necessary interpolation between the Object values to derive the animated 282 * value. 283 * @param values A set of values that the animation will animate between over time. 284 * @return An ObjectAnimator object that is set up to animate between the given values. 285 */ 286 public static ObjectAnimator ofObject(Object target, String propertyName, 287 TypeEvaluator evaluator, Object... values) { 288 ObjectAnimator anim = new ObjectAnimator(target, propertyName); 289 anim.setObjectValues(values); 290 anim.setEvaluator(evaluator); 291 return anim; 292 } 293 294 /** 295 * Constructs and returns an ObjectAnimator that animates between Object values. A single 296 * value implies that that value is the one being animated to. Two values imply a starting 297 * and ending values. More than two values imply a starting value, values to animate through 298 * along the way, and an ending value (these values will be distributed evenly across 299 * the duration of the animation). 300 * 301 * @param target The object whose property is to be animated. 302 * @param property The property being animated. 303 * @param evaluator A TypeEvaluator that will be called on each animation frame to 304 * provide the necessary interpolation between the Object values to derive the animated 305 * value. 306 * @param values A set of values that the animation will animate between over time. 307 * @return An ObjectAnimator object that is set up to animate between the given values. 308 */ 309 public static <T, V> ObjectAnimator ofObject(T target, Property<T, V> property, 310 TypeEvaluator<V> evaluator, V... values) { 311 ObjectAnimator anim = new ObjectAnimator(target, property); 312 anim.setObjectValues(values); 313 anim.setEvaluator(evaluator); 314 return anim; 315 } 316 317 /** 318 * Constructs and returns an ObjectAnimator that animates between the sets of values specified 319 * in <code>PropertyValueHolder</code> objects. This variant should be used when animating 320 * several properties at once with the same ObjectAnimator, since PropertyValuesHolder allows 321 * you to associate a set of animation values with a property name. 322 * 323 * @param target The object whose property is to be animated. Depending on how the 324 * PropertyValuesObjects were constructed, the target object should either have the {@link 325 * android.util.Property} objects used to construct the PropertyValuesHolder objects or (if the 326 * PropertyValuesHOlder objects were created with property names) the target object should have 327 * public methods on it called <code>setName()</code>, where <code>name</code> is the name of 328 * the property passed in as the <code>propertyName</code> parameter for each of the 329 * PropertyValuesHolder objects. 330 * @param values A set of PropertyValuesHolder objects whose values will be animated between 331 * over time. 332 * @return An ObjectAnimator object that is set up to animate between the given values. 333 */ 334 public static ObjectAnimator ofPropertyValuesHolder(Object target, 335 PropertyValuesHolder... values) { 336 ObjectAnimator anim = new ObjectAnimator(); 337 anim.mTarget = target; 338 anim.setValues(values); 339 return anim; 340 } 341 342 @Override 343 public void setIntValues(int... values) { 344 if (mValues == null || mValues.length == 0) { 345 // No values yet - this animator is being constructed piecemeal. Init the values with 346 // whatever the current propertyName is 347 if (mProperty != null) { 348 setValues(PropertyValuesHolder.ofInt(mProperty, values)); 349 } else { 350 setValues(PropertyValuesHolder.ofInt(mPropertyName, values)); 351 } 352 } else { 353 super.setIntValues(values); 354 } 355 } 356 357 @Override 358 public void setFloatValues(float... values) { 359 if (mValues == null || mValues.length == 0) { 360 // No values yet - this animator is being constructed piecemeal. Init the values with 361 // whatever the current propertyName is 362 if (mProperty != null) { 363 setValues(PropertyValuesHolder.ofFloat(mProperty, values)); 364 } else { 365 setValues(PropertyValuesHolder.ofFloat(mPropertyName, values)); 366 } 367 } else { 368 super.setFloatValues(values); 369 } 370 } 371 372 @Override 373 public void setObjectValues(Object... values) { 374 if (mValues == null || mValues.length == 0) { 375 // No values yet - this animator is being constructed piecemeal. Init the values with 376 // whatever the current propertyName is 377 if (mProperty != null) { 378 setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator) null, values)); 379 } else { 380 setValues(PropertyValuesHolder.ofObject(mPropertyName, 381 (TypeEvaluator) null, values)); 382 } 383 } else { 384 super.setObjectValues(values); 385 } 386 } 387 388 /** 389 * autoCancel controls whether an ObjectAnimator will be canceled automatically 390 * when any other ObjectAnimator with the same target and properties is started. 391 * Setting this flag may make it easier to run different animators on the same target 392 * object without having to keep track of whether there are conflicting animators that 393 * need to be manually canceled. Canceling animators must have the same exact set of 394 * target properties, in the same order. 395 * 396 * @param cancel Whether future ObjectAnimators with the same target and properties 397 * as this ObjectAnimator will cause this ObjectAnimator to be canceled. 398 */ 399 public void setAutoCancel(boolean cancel) { 400 mAutoCancel = cancel; 401 } 402 403 private boolean hasSameTargetAndProperties(Animator anim) { 404 if (anim instanceof ObjectAnimator) { 405 PropertyValuesHolder[] theirValues = ((ObjectAnimator) anim).getValues(); 406 if (((ObjectAnimator) anim).getTarget() == mTarget && 407 mValues.length == theirValues.length) { 408 for (int i = 0; i < mValues.length; ++i) { 409 PropertyValuesHolder pvhMine = mValues[i]; 410 PropertyValuesHolder pvhTheirs = theirValues[i]; 411 if (pvhMine.getPropertyName() == null || 412 !pvhMine.getPropertyName().equals(pvhTheirs.getPropertyName())) { 413 return false; 414 } 415 } 416 return true; 417 } 418 } 419 return false; 420 } 421 422 @Override 423 public void start() { 424 // See if any of the current active/pending animators need to be canceled 425 AnimationHandler handler = sAnimationHandler.get(); 426 if (handler != null) { 427 int numAnims = handler.mAnimations.size(); 428 for (int i = numAnims - 1; i >= 0; i--) { 429 if (handler.mAnimations.get(i) instanceof ObjectAnimator) { 430 ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i); 431 if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { 432 anim.cancel(); 433 } 434 } 435 } 436 numAnims = handler.mPendingAnimations.size(); 437 for (int i = numAnims - 1; i >= 0; i--) { 438 if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) { 439 ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i); 440 if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { 441 anim.cancel(); 442 } 443 } 444 } 445 numAnims = handler.mDelayedAnims.size(); 446 for (int i = numAnims - 1; i >= 0; i--) { 447 if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) { 448 ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i); 449 if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { 450 anim.cancel(); 451 } 452 } 453 } 454 } 455 if (DBG) { 456 Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration()); 457 for (int i = 0; i < mValues.length; ++i) { 458 PropertyValuesHolder pvh = mValues[i]; 459 ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes; 460 Log.d("ObjectAnimator", " Values[" + i + "]: " + 461 pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " + 462 keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue()); 463 } 464 } 465 super.start(); 466 } 467 468 /** 469 * This function is called immediately before processing the first animation 470 * frame of an animation. If there is a nonzero <code>startDelay</code>, the 471 * function is called after that delay ends. 472 * It takes care of the final initialization steps for the 473 * animation. This includes setting mEvaluator, if the user has not yet 474 * set it up, and the setter/getter methods, if the user did not supply 475 * them. 476 * 477 * <p>Overriders of this method should call the superclass method to cause 478 * internal mechanisms to be set up correctly.</p> 479 */ 480 @Override 481 void initAnimation() { 482 if (!mInitialized) { 483 // mValueType may change due to setter/getter setup; do this before calling super.init(), 484 // which uses mValueType to set up the default type evaluator. 485 int numValues = mValues.length; 486 for (int i = 0; i < numValues; ++i) { 487 mValues[i].setupSetterAndGetter(mTarget); 488 } 489 super.initAnimation(); 490 } 491 } 492 493 /** 494 * Sets the length of the animation. The default duration is 300 milliseconds. 495 * 496 * @param duration The length of the animation, in milliseconds. 497 * @return ObjectAnimator The object called with setDuration(). This return 498 * value makes it easier to compose statements together that construct and then set the 499 * duration, as in 500 * <code>ObjectAnimator.ofInt(target, propertyName, 0, 10).setDuration(500).start()</code>. 501 */ 502 @Override 503 public ObjectAnimator setDuration(long duration) { 504 super.setDuration(duration); 505 return this; 506 } 507 508 509 /** 510 * The target object whose property will be animated by this animation 511 * 512 * @return The object being animated 513 */ 514 public Object getTarget() { 515 return mTarget; 516 } 517 518 /** 519 * Sets the target object whose property will be animated by this animation 520 * 521 * @param target The object being animated 522 */ 523 @Override 524 public void setTarget(Object target) { 525 if (mTarget != target) { 526 final Object oldTarget = mTarget; 527 mTarget = target; 528 if (oldTarget != null && target != null && oldTarget.getClass() == target.getClass()) { 529 return; 530 } 531 // New target type should cause re-initialization prior to starting 532 mInitialized = false; 533 } 534 } 535 536 @Override 537 public void setupStartValues() { 538 initAnimation(); 539 int numValues = mValues.length; 540 for (int i = 0; i < numValues; ++i) { 541 mValues[i].setupStartValue(mTarget); 542 } 543 } 544 545 @Override 546 public void setupEndValues() { 547 initAnimation(); 548 int numValues = mValues.length; 549 for (int i = 0; i < numValues; ++i) { 550 mValues[i].setupEndValue(mTarget); 551 } 552 } 553 554 /** 555 * This method is called with the elapsed fraction of the animation during every 556 * animation frame. This function turns the elapsed fraction into an interpolated fraction 557 * and then into an animated value (from the evaluator. The function is called mostly during 558 * animation updates, but it is also called when the <code>end()</code> 559 * function is called, to set the final value on the property. 560 * 561 * <p>Overrides of this method must call the superclass to perform the calculation 562 * of the animated value.</p> 563 * 564 * @param fraction The elapsed fraction of the animation. 565 */ 566 @Override 567 void animateValue(float fraction) { 568 super.animateValue(fraction); 569 int numValues = mValues.length; 570 for (int i = 0; i < numValues; ++i) { 571 mValues[i].setAnimatedValue(mTarget); 572 } 573 } 574 575 @Override 576 public ObjectAnimator clone() { 577 final ObjectAnimator anim = (ObjectAnimator) super.clone(); 578 return anim; 579 } 580 581 @Override 582 public String toString() { 583 String returnVal = "ObjectAnimator@" + Integer.toHexString(hashCode()) + ", target " + 584 mTarget; 585 if (mValues != null) { 586 for (int i = 0; i < mValues.length; ++i) { 587 returnVal += "\n " + mValues[i].toString(); 588 } 589 } 590 return returnVal; 591 } 592 } 593