ViewPropertyAnimator.java revision 87f4ae67c86c7044253b3e1bcec6956a8c8bf017
1/* 2 * Copyright (C) 2011 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.view; 18 19import android.animation.Animator; 20import android.animation.ValueAnimator; 21import android.animation.TimeInterpolator; 22 23import java.util.ArrayList; 24import java.util.HashMap; 25import java.util.Set; 26 27/** 28 * This class enables automatic and optimized animation of select properties on View objects. 29 * If only one or two properties on a View object are being animated, then using an 30 * {@link android.animation.ObjectAnimator} is fine; the property setters called by ObjectAnimator 31 * are well equipped to do the right thing to set the property and invalidate the view 32 * appropriately. But if several properties are animated simultaneously, or if you just want a 33 * more convenient syntax to animate a specific property, then ViewPropertyAnimator might be 34 * more well-suited to the task. 35 * 36 * <p>This class may provide better performance for several simultaneous animations, because 37 * it will optimize invalidate calls to take place only once for several properties instead of each 38 * animated property independently causing its own invalidation. Also, the syntax of using this 39 * class could be easier to use because the caller need only tell the View object which 40 * property to animate, and the value to animate either to or by, and this class handles the 41 * details of configuring the underlying Animator class and starting it.</p> 42 * 43 * <p>This class is not constructed by the caller, but rather by the View whose properties 44 * it will animate. Calls to {@link android.view.View#animate()} will return a reference 45 * to the appropriate ViewPropertyAnimator object for that View.</p> 46 * 47 */ 48public class ViewPropertyAnimator { 49 50 /** 51 * The View whose properties are being animated by this class. This is set at 52 * construction time. 53 */ 54 private final View mView; 55 56 /** 57 * The duration of the underlying Animator object. By default, we don't set the duration 58 * on the Animator and just use its default duration. If the duration is ever set on this 59 * Animator, then we use the duration that it was set to. 60 */ 61 private long mDuration; 62 63 /** 64 * A flag indicating whether the duration has been set on this object. If not, we don't set 65 * the duration on the underlying Animator, but instead just use its default duration. 66 */ 67 private boolean mDurationSet = false; 68 69 /** 70 * The startDelay of the underlying Animator object. By default, we don't set the startDelay 71 * on the Animator and just use its default startDelay. If the startDelay is ever set on this 72 * Animator, then we use the startDelay that it was set to. 73 */ 74 private long mStartDelay = 0; 75 76 /** 77 * A flag indicating whether the startDelay has been set on this object. If not, we don't set 78 * the startDelay on the underlying Animator, but instead just use its default startDelay. 79 */ 80 private boolean mStartDelaySet = false; 81 82 /** 83 * The interpolator of the underlying Animator object. By default, we don't set the interpolator 84 * on the Animator and just use its default interpolator. If the interpolator is ever set on 85 * this Animator, then we use the interpolator that it was set to. 86 */ 87 private TimeInterpolator mInterpolator; 88 89 /** 90 * A flag indicating whether the interpolator has been set on this object. If not, we don't set 91 * the interpolator on the underlying Animator, but instead just use its default interpolator. 92 */ 93 private boolean mInterpolatorSet = false; 94 95 /** 96 * Listener for the lifecycle events of the underlying ValueAnimator object. 97 */ 98 private Animator.AnimatorListener mListener = null; 99 100 /** 101 * Listener for the update events of the underlying ValueAnimator object. 102 */ 103 private ValueAnimator.AnimatorUpdateListener mUpdateListener = null; 104 105 /** 106 * A lazily-created ValueAnimator used in order to get some default animator properties 107 * (duration, start delay, interpolator, etc.). 108 */ 109 private ValueAnimator mTempValueAnimator; 110 111 /** 112 * This listener is the mechanism by which the underlying Animator causes changes to the 113 * properties currently being animated, as well as the cleanup after an animation is 114 * complete. 115 */ 116 private AnimatorEventListener mAnimatorEventListener = new AnimatorEventListener(); 117 118 /** 119 * This list holds the properties that have been asked to animate. We allow the caller to 120 * request several animations prior to actually starting the underlying animator. This 121 * enables us to run one single animator to handle several properties in parallel. Each 122 * property is tossed onto the pending list until the animation actually starts (which is 123 * done by posting it onto mView), at which time the pending list is cleared and the properties 124 * on that list are added to the list of properties associated with that animator. 125 */ 126 ArrayList<NameValuesHolder> mPendingAnimations = new ArrayList<NameValuesHolder>(); 127 private Runnable mPendingSetupAction; 128 private Runnable mPendingCleanupAction; 129 private Runnable mPendingOnStartAction; 130 private Runnable mPendingOnEndAction; 131 132 /** 133 * Constants used to associate a property being requested and the mechanism used to set 134 * the property (this class calls directly into View to set the properties in question). 135 */ 136 private static final int NONE = 0x0000; 137 private static final int TRANSLATION_X = 0x0001; 138 private static final int TRANSLATION_Y = 0x0002; 139 private static final int SCALE_X = 0x0004; 140 private static final int SCALE_Y = 0x0008; 141 private static final int ROTATION = 0x0010; 142 private static final int ROTATION_X = 0x0020; 143 private static final int ROTATION_Y = 0x0040; 144 private static final int X = 0x0080; 145 private static final int Y = 0x0100; 146 private static final int ALPHA = 0x0200; 147 148 private static final int TRANSFORM_MASK = TRANSLATION_X | TRANSLATION_Y | SCALE_X | SCALE_Y | 149 ROTATION | ROTATION_X | ROTATION_Y | X | Y; 150 151 /** 152 * The mechanism by which the user can request several properties that are then animated 153 * together works by posting this Runnable to start the underlying Animator. Every time 154 * a property animation is requested, we cancel any previous postings of the Runnable 155 * and re-post it. This means that we will only ever run the Runnable (and thus start the 156 * underlying animator) after the caller is done setting the properties that should be 157 * animated together. 158 */ 159 private Runnable mAnimationStarter = new Runnable() { 160 @Override 161 public void run() { 162 startAnimation(); 163 } 164 }; 165 166 /** 167 * This class holds information about the overall animation being run on the set of 168 * properties. The mask describes which properties are being animated and the 169 * values holder is the list of all property/value objects. 170 */ 171 private static class PropertyBundle { 172 int mPropertyMask; 173 ArrayList<NameValuesHolder> mNameValuesHolder; 174 175 PropertyBundle(int propertyMask, ArrayList<NameValuesHolder> nameValuesHolder) { 176 mPropertyMask = propertyMask; 177 mNameValuesHolder = nameValuesHolder; 178 } 179 180 /** 181 * Removes the given property from being animated as a part of this 182 * PropertyBundle. If the property was a part of this bundle, it returns 183 * true to indicate that it was, in fact, canceled. This is an indication 184 * to the caller that a cancellation actually occurred. 185 * 186 * @param propertyConstant The property whose cancellation is requested. 187 * @return true if the given property is a part of this bundle and if it 188 * has therefore been canceled. 189 */ 190 boolean cancel(int propertyConstant) { 191 if ((mPropertyMask & propertyConstant) != 0 && mNameValuesHolder != null) { 192 int count = mNameValuesHolder.size(); 193 for (int i = 0; i < count; ++i) { 194 NameValuesHolder nameValuesHolder = mNameValuesHolder.get(i); 195 if (nameValuesHolder.mNameConstant == propertyConstant) { 196 mNameValuesHolder.remove(i); 197 mPropertyMask &= ~propertyConstant; 198 return true; 199 } 200 } 201 } 202 return false; 203 } 204 } 205 206 /** 207 * This list tracks the list of properties being animated by any particular animator. 208 * In most situations, there would only ever be one animator running at a time. But it is 209 * possible to request some properties to animate together, then while those properties 210 * are animating, to request some other properties to animate together. The way that 211 * works is by having this map associate the group of properties being animated with the 212 * animator handling the animation. On every update event for an Animator, we ask the 213 * map for the associated properties and set them accordingly. 214 */ 215 private HashMap<Animator, PropertyBundle> mAnimatorMap = 216 new HashMap<Animator, PropertyBundle>(); 217 private HashMap<Animator, Runnable> mAnimatorSetupMap; 218 private HashMap<Animator, Runnable> mAnimatorCleanupMap; 219 private HashMap<Animator, Runnable> mAnimatorOnStartMap; 220 private HashMap<Animator, Runnable> mAnimatorOnEndMap; 221 222 /** 223 * This is the information we need to set each property during the animation. 224 * mNameConstant is used to set the appropriate field in View, and the from/delta 225 * values are used to calculate the animated value for a given animation fraction 226 * during the animation. 227 */ 228 private static class NameValuesHolder { 229 int mNameConstant; 230 float mFromValue; 231 float mDeltaValue; 232 NameValuesHolder(int nameConstant, float fromValue, float deltaValue) { 233 mNameConstant = nameConstant; 234 mFromValue = fromValue; 235 mDeltaValue = deltaValue; 236 } 237 } 238 239 /** 240 * Constructor, called by View. This is private by design, as the user should only 241 * get a ViewPropertyAnimator by calling View.animate(). 242 * 243 * @param view The View associated with this ViewPropertyAnimator 244 */ 245 ViewPropertyAnimator(View view) { 246 mView = view; 247 view.ensureTransformationInfo(); 248 } 249 250 /** 251 * Sets the duration for the underlying animator that animates the requested properties. 252 * By default, the animator uses the default value for ValueAnimator. Calling this method 253 * will cause the declared value to be used instead. 254 * @param duration The length of ensuing property animations, in milliseconds. The value 255 * cannot be negative. 256 * @return This object, allowing calls to methods in this class to be chained. 257 */ 258 public ViewPropertyAnimator setDuration(long duration) { 259 if (duration < 0) { 260 throw new IllegalArgumentException("Animators cannot have negative duration: " + 261 duration); 262 } 263 mDurationSet = true; 264 mDuration = duration; 265 return this; 266 } 267 268 /** 269 * Returns the current duration of property animations. If the duration was set on this 270 * object, that value is returned. Otherwise, the default value of the underlying Animator 271 * is returned. 272 * 273 * @see #setDuration(long) 274 * @return The duration of animations, in milliseconds. 275 */ 276 public long getDuration() { 277 if (mDurationSet) { 278 return mDuration; 279 } else { 280 // Just return the default from ValueAnimator, since that's what we'd get if 281 // the value has not been set otherwise 282 if (mTempValueAnimator == null) { 283 mTempValueAnimator = new ValueAnimator(); 284 } 285 return mTempValueAnimator.getDuration(); 286 } 287 } 288 289 /** 290 * Returns the current startDelay of property animations. If the startDelay was set on this 291 * object, that value is returned. Otherwise, the default value of the underlying Animator 292 * is returned. 293 * 294 * @see #setStartDelay(long) 295 * @return The startDelay of animations, in milliseconds. 296 */ 297 public long getStartDelay() { 298 if (mStartDelaySet) { 299 return mStartDelay; 300 } else { 301 // Just return the default from ValueAnimator (0), since that's what we'd get if 302 // the value has not been set otherwise 303 return 0; 304 } 305 } 306 307 /** 308 * Sets the startDelay for the underlying animator that animates the requested properties. 309 * By default, the animator uses the default value for ValueAnimator. Calling this method 310 * will cause the declared value to be used instead. 311 * @param startDelay The delay of ensuing property animations, in milliseconds. The value 312 * cannot be negative. 313 * @return This object, allowing calls to methods in this class to be chained. 314 */ 315 public ViewPropertyAnimator setStartDelay(long startDelay) { 316 if (startDelay < 0) { 317 throw new IllegalArgumentException("Animators cannot have negative duration: " + 318 startDelay); 319 } 320 mStartDelaySet = true; 321 mStartDelay = startDelay; 322 return this; 323 } 324 325 /** 326 * Sets the interpolator for the underlying animator that animates the requested properties. 327 * By default, the animator uses the default interpolator for ValueAnimator. Calling this method 328 * will cause the declared object to be used instead. 329 * 330 * @param interpolator The TimeInterpolator to be used for ensuing property animations. 331 * @return This object, allowing calls to methods in this class to be chained. 332 */ 333 public ViewPropertyAnimator setInterpolator(TimeInterpolator interpolator) { 334 mInterpolatorSet = true; 335 mInterpolator = interpolator; 336 return this; 337 } 338 339 /** 340 * Returns the timing interpolator that this animation uses. 341 * 342 * @return The timing interpolator for this animation. 343 */ 344 public TimeInterpolator getInterpolator() { 345 if (mInterpolatorSet) { 346 return mInterpolator; 347 } else { 348 // Just return the default from ValueAnimator, since that's what we'd get if 349 // the value has not been set otherwise 350 if (mTempValueAnimator == null) { 351 mTempValueAnimator = new ValueAnimator(); 352 } 353 return mTempValueAnimator.getInterpolator(); 354 } 355 } 356 357 /** 358 * Sets a listener for events in the underlying Animators that run the property 359 * animations. 360 * 361 * @see Animator.AnimatorListener 362 * 363 * @param listener The listener to be called with AnimatorListener events. A value of 364 * <code>null</code> removes any existing listener. 365 * @return This object, allowing calls to methods in this class to be chained. 366 */ 367 public ViewPropertyAnimator setListener(Animator.AnimatorListener listener) { 368 mListener = listener; 369 return this; 370 } 371 372 /** 373 * Sets a listener for update events in the underlying ValueAnimator that runs 374 * the property animations. Note that the underlying animator is animating between 375 * 0 and 1 (these values are then turned into the actual property values internally 376 * by ViewPropertyAnimator). So the animator cannot give information on the current 377 * values of the properties being animated by this ViewPropertyAnimator, although 378 * the view object itself can be queried to get the current values. 379 * 380 * @see android.animation.ValueAnimator.AnimatorUpdateListener 381 * 382 * @param listener The listener to be called with update events. A value of 383 * <code>null</code> removes any existing listener. 384 * @return This object, allowing calls to methods in this class to be chained. 385 */ 386 public ViewPropertyAnimator setUpdateListener(ValueAnimator.AnimatorUpdateListener listener) { 387 mUpdateListener = listener; 388 return this; 389 } 390 391 /** 392 * Starts the currently pending property animations immediately. Calling <code>start()</code> 393 * is optional because all animations start automatically at the next opportunity. However, 394 * if the animations are needed to start immediately and synchronously (not at the time when 395 * the next event is processed by the hierarchy, which is when the animations would begin 396 * otherwise), then this method can be used. 397 */ 398 public void start() { 399 mView.removeCallbacks(mAnimationStarter); 400 startAnimation(); 401 } 402 403 /** 404 * Cancels all property animations that are currently running or pending. 405 */ 406 public void cancel() { 407 if (mAnimatorMap.size() > 0) { 408 HashMap<Animator, PropertyBundle> mAnimatorMapCopy = 409 (HashMap<Animator, PropertyBundle>)mAnimatorMap.clone(); 410 Set<Animator> animatorSet = mAnimatorMapCopy.keySet(); 411 for (Animator runningAnim : animatorSet) { 412 runningAnim.cancel(); 413 } 414 } 415 mPendingAnimations.clear(); 416 mView.removeCallbacks(mAnimationStarter); 417 } 418 419 /** 420 * This method will cause the View's <code>x</code> property to be animated to the 421 * specified value. Animations already running on the property will be canceled. 422 * 423 * @param value The value to be animated to. 424 * @see View#setX(float) 425 * @return This object, allowing calls to methods in this class to be chained. 426 */ 427 public ViewPropertyAnimator x(float value) { 428 animateProperty(X, value); 429 return this; 430 } 431 432 /** 433 * This method will cause the View's <code>x</code> property to be animated by the 434 * specified value. Animations already running on the property will be canceled. 435 * 436 * @param value The amount to be animated by, as an offset from the current value. 437 * @see View#setX(float) 438 * @return This object, allowing calls to methods in this class to be chained. 439 */ 440 public ViewPropertyAnimator xBy(float value) { 441 animatePropertyBy(X, value); 442 return this; 443 } 444 445 /** 446 * This method will cause the View's <code>y</code> property to be animated to the 447 * specified value. Animations already running on the property will be canceled. 448 * 449 * @param value The value to be animated to. 450 * @see View#setY(float) 451 * @return This object, allowing calls to methods in this class to be chained. 452 */ 453 public ViewPropertyAnimator y(float value) { 454 animateProperty(Y, value); 455 return this; 456 } 457 458 /** 459 * This method will cause the View's <code>y</code> property to be animated by the 460 * specified value. Animations already running on the property will be canceled. 461 * 462 * @param value The amount to be animated by, as an offset from the current value. 463 * @see View#setY(float) 464 * @return This object, allowing calls to methods in this class to be chained. 465 */ 466 public ViewPropertyAnimator yBy(float value) { 467 animatePropertyBy(Y, value); 468 return this; 469 } 470 471 /** 472 * This method will cause the View's <code>rotation</code> property to be animated to the 473 * specified value. Animations already running on the property will be canceled. 474 * 475 * @param value The value to be animated to. 476 * @see View#setRotation(float) 477 * @return This object, allowing calls to methods in this class to be chained. 478 */ 479 public ViewPropertyAnimator rotation(float value) { 480 animateProperty(ROTATION, value); 481 return this; 482 } 483 484 /** 485 * This method will cause the View's <code>rotation</code> property to be animated by the 486 * specified value. Animations already running on the property will be canceled. 487 * 488 * @param value The amount to be animated by, as an offset from the current value. 489 * @see View#setRotation(float) 490 * @return This object, allowing calls to methods in this class to be chained. 491 */ 492 public ViewPropertyAnimator rotationBy(float value) { 493 animatePropertyBy(ROTATION, value); 494 return this; 495 } 496 497 /** 498 * This method will cause the View's <code>rotationX</code> property to be animated to the 499 * specified value. Animations already running on the property will be canceled. 500 * 501 * @param value The value to be animated to. 502 * @see View#setRotationX(float) 503 * @return This object, allowing calls to methods in this class to be chained. 504 */ 505 public ViewPropertyAnimator rotationX(float value) { 506 animateProperty(ROTATION_X, value); 507 return this; 508 } 509 510 /** 511 * This method will cause the View's <code>rotationX</code> property to be animated by the 512 * specified value. Animations already running on the property will be canceled. 513 * 514 * @param value The amount to be animated by, as an offset from the current value. 515 * @see View#setRotationX(float) 516 * @return This object, allowing calls to methods in this class to be chained. 517 */ 518 public ViewPropertyAnimator rotationXBy(float value) { 519 animatePropertyBy(ROTATION_X, value); 520 return this; 521 } 522 523 /** 524 * This method will cause the View's <code>rotationY</code> property to be animated to the 525 * specified value. Animations already running on the property will be canceled. 526 * 527 * @param value The value to be animated to. 528 * @see View#setRotationY(float) 529 * @return This object, allowing calls to methods in this class to be chained. 530 */ 531 public ViewPropertyAnimator rotationY(float value) { 532 animateProperty(ROTATION_Y, value); 533 return this; 534 } 535 536 /** 537 * This method will cause the View's <code>rotationY</code> property to be animated by the 538 * specified value. Animations already running on the property will be canceled. 539 * 540 * @param value The amount to be animated by, as an offset from the current value. 541 * @see View#setRotationY(float) 542 * @return This object, allowing calls to methods in this class to be chained. 543 */ 544 public ViewPropertyAnimator rotationYBy(float value) { 545 animatePropertyBy(ROTATION_Y, value); 546 return this; 547 } 548 549 /** 550 * This method will cause the View's <code>translationX</code> property to be animated to the 551 * specified value. Animations already running on the property will be canceled. 552 * 553 * @param value The value to be animated to. 554 * @see View#setTranslationX(float) 555 * @return This object, allowing calls to methods in this class to be chained. 556 */ 557 public ViewPropertyAnimator translationX(float value) { 558 animateProperty(TRANSLATION_X, value); 559 return this; 560 } 561 562 /** 563 * This method will cause the View's <code>translationX</code> property to be animated by the 564 * specified value. Animations already running on the property will be canceled. 565 * 566 * @param value The amount to be animated by, as an offset from the current value. 567 * @see View#setTranslationX(float) 568 * @return This object, allowing calls to methods in this class to be chained. 569 */ 570 public ViewPropertyAnimator translationXBy(float value) { 571 animatePropertyBy(TRANSLATION_X, value); 572 return this; 573 } 574 575 /** 576 * This method will cause the View's <code>translationY</code> property to be animated to the 577 * specified value. Animations already running on the property will be canceled. 578 * 579 * @param value The value to be animated to. 580 * @see View#setTranslationY(float) 581 * @return This object, allowing calls to methods in this class to be chained. 582 */ 583 public ViewPropertyAnimator translationY(float value) { 584 animateProperty(TRANSLATION_Y, value); 585 return this; 586 } 587 588 /** 589 * This method will cause the View's <code>translationY</code> property to be animated by the 590 * specified value. Animations already running on the property will be canceled. 591 * 592 * @param value The amount to be animated by, as an offset from the current value. 593 * @see View#setTranslationY(float) 594 * @return This object, allowing calls to methods in this class to be chained. 595 */ 596 public ViewPropertyAnimator translationYBy(float value) { 597 animatePropertyBy(TRANSLATION_Y, value); 598 return this; 599 } 600 601 /** 602 * This method will cause the View's <code>scaleX</code> property to be animated to the 603 * specified value. Animations already running on the property will be canceled. 604 * 605 * @param value The value to be animated to. 606 * @see View#setScaleX(float) 607 * @return This object, allowing calls to methods in this class to be chained. 608 */ 609 public ViewPropertyAnimator scaleX(float value) { 610 animateProperty(SCALE_X, value); 611 return this; 612 } 613 614 /** 615 * This method will cause the View's <code>scaleX</code> property to be animated by the 616 * specified value. Animations already running on the property will be canceled. 617 * 618 * @param value The amount to be animated by, as an offset from the current value. 619 * @see View#setScaleX(float) 620 * @return This object, allowing calls to methods in this class to be chained. 621 */ 622 public ViewPropertyAnimator scaleXBy(float value) { 623 animatePropertyBy(SCALE_X, value); 624 return this; 625 } 626 627 /** 628 * This method will cause the View's <code>scaleY</code> property to be animated to the 629 * specified value. Animations already running on the property will be canceled. 630 * 631 * @param value The value to be animated to. 632 * @see View#setScaleY(float) 633 * @return This object, allowing calls to methods in this class to be chained. 634 */ 635 public ViewPropertyAnimator scaleY(float value) { 636 animateProperty(SCALE_Y, value); 637 return this; 638 } 639 640 /** 641 * This method will cause the View's <code>scaleY</code> property to be animated by the 642 * specified value. Animations already running on the property will be canceled. 643 * 644 * @param value The amount to be animated by, as an offset from the current value. 645 * @see View#setScaleY(float) 646 * @return This object, allowing calls to methods in this class to be chained. 647 */ 648 public ViewPropertyAnimator scaleYBy(float value) { 649 animatePropertyBy(SCALE_Y, value); 650 return this; 651 } 652 653 /** 654 * This method will cause the View's <code>alpha</code> property to be animated to the 655 * specified value. Animations already running on the property will be canceled. 656 * 657 * @param value The value to be animated to. 658 * @see View#setAlpha(float) 659 * @return This object, allowing calls to methods in this class to be chained. 660 */ 661 public ViewPropertyAnimator alpha(float value) { 662 animateProperty(ALPHA, value); 663 return this; 664 } 665 666 /** 667 * This method will cause the View's <code>alpha</code> property to be animated by the 668 * specified value. Animations already running on the property will be canceled. 669 * 670 * @param value The amount to be animated by, as an offset from the current value. 671 * @see View#setAlpha(float) 672 * @return This object, allowing calls to methods in this class to be chained. 673 */ 674 public ViewPropertyAnimator alphaBy(float value) { 675 animatePropertyBy(ALPHA, value); 676 return this; 677 } 678 679 /** 680 * The View associated with this ViewPropertyAnimator will have its 681 * {@link View#setLayerType(int, android.graphics.Paint) layer type} set to 682 * {@link View#LAYER_TYPE_HARDWARE} for the duration of the next animation. 683 * As stated in the documentation for {@link View#LAYER_TYPE_HARDWARE}, 684 * the actual type of layer used internally depends on the runtime situation of the 685 * view. If the activity and this view are hardware-accelerated, then the layer will be 686 * accelerated as well. If the activity or the view is not accelerated, then the layer will 687 * effectively be the same as {@link View#LAYER_TYPE_SOFTWARE}. 688 * 689 * <p>This state is not persistent, either on the View or on this ViewPropertyAnimator: the 690 * layer type of the View will be restored when the animation ends to what it was when this 691 * method was called, and this setting on ViewPropertyAnimator is only valid for the next 692 * animation. Note that calling this method and then independently setting the layer type of 693 * the View (by a direct call to {@link View#setLayerType(int, android.graphics.Paint)}) will 694 * result in some inconsistency, including having the layer type restored to its pre-withLayer() 695 * value when the animation ends.</p> 696 * 697 * @see View#setLayerType(int, android.graphics.Paint) 698 * @return This object, allowing calls to methods in this class to be chained. 699 */ 700 public ViewPropertyAnimator withLayer() { 701 mPendingSetupAction= new Runnable() { 702 @Override 703 public void run() { 704 mView.setLayerType(View.LAYER_TYPE_HARDWARE, null); 705 } 706 }; 707 final int currentLayerType = mView.getLayerType(); 708 mPendingCleanupAction = new Runnable() { 709 @Override 710 public void run() { 711 mView.setLayerType(currentLayerType, null); 712 } 713 }; 714 if (mAnimatorSetupMap == null) { 715 mAnimatorSetupMap = new HashMap<Animator, Runnable>(); 716 } 717 if (mAnimatorCleanupMap == null) { 718 mAnimatorCleanupMap = new HashMap<Animator, Runnable>(); 719 } 720 721 return this; 722 } 723 724 /** 725 * Specifies an action to take place when the next animation runs. If there is a 726 * {@link #setStartDelay(long) startDelay} set on this ViewPropertyAnimator, then the 727 * action will run after that startDelay expires, when the actual animation begins. 728 * This method, along with {@link #withEndAction(Runnable)}, is intended to help facilitate 729 * choreographing ViewPropertyAnimator animations with other animations or actions 730 * in the application. 731 * 732 * @param runnable The action to run when the next animation starts. 733 * @return This object, allowing calls to methods in this class to be chained. 734 */ 735 public ViewPropertyAnimator withStartAction(Runnable runnable) { 736 mPendingOnStartAction = runnable; 737 if (runnable != null && mAnimatorOnStartMap == null) { 738 mAnimatorOnStartMap = new HashMap<Animator, Runnable>(); 739 } 740 return this; 741 } 742 743 /** 744 * Specifies an action to take place when the next animation ends. The action is only 745 * run if the animation ends normally; if the ViewPropertyAnimator is canceled during 746 * that animation, the runnable will not run. 747 * This method, along with {@link #withStartAction(Runnable)}, is intended to help facilitate 748 * choreographing ViewPropertyAnimator animations with other animations or actions 749 * in the application. 750 * 751 * <p>For example, the following code animates a view to x=200 and then back to 0:</p> 752 * <pre> 753 * Runnable endAction = new Runnable() { 754 * public void run() { 755 * view.animate().x(0); 756 * } 757 * }; 758 * view.animate().x(200).withEndAction(endAction); 759 * </pre> 760 * 761 * @param runnable The action to run when the next animation ends. 762 * @return This object, allowing calls to methods in this class to be chained. 763 */ 764 public ViewPropertyAnimator withEndAction(Runnable runnable) { 765 mPendingOnEndAction = runnable; 766 if (runnable != null && mAnimatorOnEndMap == null) { 767 mAnimatorOnEndMap = new HashMap<Animator, Runnable>(); 768 } 769 return this; 770 } 771 772 /** 773 * Starts the underlying Animator for a set of properties. We use a single animator that 774 * simply runs from 0 to 1, and then use that fractional value to set each property 775 * value accordingly. 776 */ 777 private void startAnimation() { 778 mView.setHasTransientState(true); 779 ValueAnimator animator = ValueAnimator.ofFloat(1.0f); 780 ArrayList<NameValuesHolder> nameValueList = 781 (ArrayList<NameValuesHolder>) mPendingAnimations.clone(); 782 mPendingAnimations.clear(); 783 int propertyMask = 0; 784 int propertyCount = nameValueList.size(); 785 for (int i = 0; i < propertyCount; ++i) { 786 NameValuesHolder nameValuesHolder = nameValueList.get(i); 787 propertyMask |= nameValuesHolder.mNameConstant; 788 } 789 mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList)); 790 if (mPendingSetupAction != null) { 791 mAnimatorSetupMap.put(animator, mPendingSetupAction); 792 mPendingSetupAction = null; 793 } 794 if (mPendingCleanupAction != null) { 795 mAnimatorCleanupMap.put(animator, mPendingCleanupAction); 796 mPendingCleanupAction = null; 797 } 798 if (mPendingOnStartAction != null) { 799 mAnimatorOnStartMap.put(animator, mPendingOnStartAction); 800 mPendingOnStartAction = null; 801 } 802 if (mPendingOnEndAction != null) { 803 mAnimatorOnEndMap.put(animator, mPendingOnEndAction); 804 mPendingOnEndAction = null; 805 } 806 animator.addUpdateListener(mAnimatorEventListener); 807 animator.addListener(mAnimatorEventListener); 808 if (mStartDelaySet) { 809 animator.setStartDelay(mStartDelay); 810 } 811 if (mDurationSet) { 812 animator.setDuration(mDuration); 813 } 814 if (mInterpolatorSet) { 815 animator.setInterpolator(mInterpolator); 816 } 817 animator.start(); 818 } 819 820 /** 821 * Utility function, called by the various x(), y(), etc. methods. This stores the 822 * constant name for the property along with the from/delta values that will be used to 823 * calculate and set the property during the animation. This structure is added to the 824 * pending animations, awaiting the eventual start() of the underlying animator. A 825 * Runnable is posted to start the animation, and any pending such Runnable is canceled 826 * (which enables us to end up starting just one animator for all of the properties 827 * specified at one time). 828 * 829 * @param constantName The specifier for the property being animated 830 * @param toValue The value to which the property will animate 831 */ 832 private void animateProperty(int constantName, float toValue) { 833 float fromValue = getValue(constantName); 834 float deltaValue = toValue - fromValue; 835 animatePropertyBy(constantName, fromValue, deltaValue); 836 } 837 838 /** 839 * Utility function, called by the various xBy(), yBy(), etc. methods. This method is 840 * just like animateProperty(), except the value is an offset from the property's 841 * current value, instead of an absolute "to" value. 842 * 843 * @param constantName The specifier for the property being animated 844 * @param byValue The amount by which the property will change 845 */ 846 private void animatePropertyBy(int constantName, float byValue) { 847 float fromValue = getValue(constantName); 848 animatePropertyBy(constantName, fromValue, byValue); 849 } 850 851 /** 852 * Utility function, called by animateProperty() and animatePropertyBy(), which handles the 853 * details of adding a pending animation and posting the request to start the animation. 854 * 855 * @param constantName The specifier for the property being animated 856 * @param startValue The starting value of the property 857 * @param byValue The amount by which the property will change 858 */ 859 private void animatePropertyBy(int constantName, float startValue, float byValue) { 860 // First, cancel any existing animations on this property 861 if (mAnimatorMap.size() > 0) { 862 Animator animatorToCancel = null; 863 Set<Animator> animatorSet = mAnimatorMap.keySet(); 864 for (Animator runningAnim : animatorSet) { 865 PropertyBundle bundle = mAnimatorMap.get(runningAnim); 866 if (bundle.cancel(constantName)) { 867 // property was canceled - cancel the animation if it's now empty 868 // Note that it's safe to break out here because every new animation 869 // on a property will cancel a previous animation on that property, so 870 // there can only ever be one such animation running. 871 if (bundle.mPropertyMask == NONE) { 872 // the animation is no longer changing anything - cancel it 873 animatorToCancel = runningAnim; 874 break; 875 } 876 } 877 } 878 if (animatorToCancel != null) { 879 animatorToCancel.cancel(); 880 } 881 } 882 883 NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue); 884 mPendingAnimations.add(nameValuePair); 885 mView.removeCallbacks(mAnimationStarter); 886 mView.postOnAnimation(mAnimationStarter); 887 } 888 889 /** 890 * This method handles setting the property values directly in the View object's fields. 891 * propertyConstant tells it which property should be set, value is the value to set 892 * the property to. 893 * 894 * @param propertyConstant The property to be set 895 * @param value The value to set the property to 896 */ 897 private void setValue(int propertyConstant, float value) { 898 final View.TransformationInfo info = mView.mTransformationInfo; 899 final DisplayList displayList = mView.mDisplayList; 900 switch (propertyConstant) { 901 case TRANSLATION_X: 902 info.mTranslationX = value; 903 if (displayList != null) displayList.setTranslationX(value); 904 break; 905 case TRANSLATION_Y: 906 info.mTranslationY = value; 907 if (displayList != null) displayList.setTranslationY(value); 908 break; 909 case ROTATION: 910 info.mRotation = value; 911 if (displayList != null) displayList.setRotation(value); 912 break; 913 case ROTATION_X: 914 info.mRotationX = value; 915 if (displayList != null) displayList.setRotationX(value); 916 break; 917 case ROTATION_Y: 918 info.mRotationY = value; 919 if (displayList != null) displayList.setRotationY(value); 920 break; 921 case SCALE_X: 922 info.mScaleX = value; 923 if (displayList != null) displayList.setScaleX(value); 924 break; 925 case SCALE_Y: 926 info.mScaleY = value; 927 if (displayList != null) displayList.setScaleY(value); 928 break; 929 case X: 930 info.mTranslationX = value - mView.mLeft; 931 if (displayList != null) displayList.setTranslationX(value - mView.mLeft); 932 break; 933 case Y: 934 info.mTranslationY = value - mView.mTop; 935 if (displayList != null) displayList.setTranslationY(value - mView.mTop); 936 break; 937 case ALPHA: 938 info.mAlpha = value; 939 if (displayList != null) displayList.setAlpha(value); 940 break; 941 } 942 } 943 944 /** 945 * This method gets the value of the named property from the View object. 946 * 947 * @param propertyConstant The property whose value should be returned 948 * @return float The value of the named property 949 */ 950 private float getValue(int propertyConstant) { 951 final View.TransformationInfo info = mView.mTransformationInfo; 952 switch (propertyConstant) { 953 case TRANSLATION_X: 954 return info.mTranslationX; 955 case TRANSLATION_Y: 956 return info.mTranslationY; 957 case ROTATION: 958 return info.mRotation; 959 case ROTATION_X: 960 return info.mRotationX; 961 case ROTATION_Y: 962 return info.mRotationY; 963 case SCALE_X: 964 return info.mScaleX; 965 case SCALE_Y: 966 return info.mScaleY; 967 case X: 968 return mView.mLeft + info.mTranslationX; 969 case Y: 970 return mView.mTop + info.mTranslationY; 971 case ALPHA: 972 return info.mAlpha; 973 } 974 return 0; 975 } 976 977 /** 978 * Utility class that handles the various Animator events. The only ones we care 979 * about are the end event (which we use to clean up the animator map when an animator 980 * finishes) and the update event (which we use to calculate the current value of each 981 * property and then set it on the view object). 982 */ 983 private class AnimatorEventListener 984 implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener { 985 @Override 986 public void onAnimationStart(Animator animation) { 987 if (mAnimatorSetupMap != null) { 988 Runnable r = mAnimatorSetupMap.get(animation); 989 if (r != null) { 990 r.run(); 991 } 992 mAnimatorSetupMap.remove(animation); 993 } 994 if (mAnimatorOnStartMap != null) { 995 Runnable r = mAnimatorOnStartMap.get(animation); 996 if (r != null) { 997 r.run(); 998 } 999 mAnimatorOnStartMap.remove(animation); 1000 } 1001 if (mListener != null) { 1002 mListener.onAnimationStart(animation); 1003 } 1004 } 1005 1006 @Override 1007 public void onAnimationCancel(Animator animation) { 1008 if (mListener != null) { 1009 mListener.onAnimationCancel(animation); 1010 } 1011 if (mAnimatorOnEndMap != null) { 1012 mAnimatorOnEndMap.remove(animation); 1013 } 1014 } 1015 1016 @Override 1017 public void onAnimationRepeat(Animator animation) { 1018 if (mListener != null) { 1019 mListener.onAnimationRepeat(animation); 1020 } 1021 } 1022 1023 @Override 1024 public void onAnimationEnd(Animator animation) { 1025 mView.setHasTransientState(false); 1026 if (mListener != null) { 1027 mListener.onAnimationEnd(animation); 1028 } 1029 if (mAnimatorOnEndMap != null) { 1030 Runnable r = mAnimatorOnEndMap.get(animation); 1031 if (r != null) { 1032 r.run(); 1033 } 1034 mAnimatorOnEndMap.remove(animation); 1035 } 1036 if (mAnimatorCleanupMap != null) { 1037 Runnable r = mAnimatorCleanupMap.get(animation); 1038 if (r != null) { 1039 r.run(); 1040 } 1041 mAnimatorCleanupMap.remove(animation); 1042 } 1043 mAnimatorMap.remove(animation); 1044 } 1045 1046 /** 1047 * Calculate the current value for each property and set it on the view. Invalidate 1048 * the view object appropriately, depending on which properties are being animated. 1049 * 1050 * @param animation The animator associated with the properties that need to be 1051 * set. This animator holds the animation fraction which we will use to calculate 1052 * the current value of each property. 1053 */ 1054 @Override 1055 public void onAnimationUpdate(ValueAnimator animation) { 1056 PropertyBundle propertyBundle = mAnimatorMap.get(animation); 1057 if (propertyBundle == null) { 1058 // Shouldn't happen, but just to play it safe 1059 return; 1060 } 1061 boolean useDisplayListProperties = mView.mDisplayList != null; 1062 1063 // alpha requires slightly different treatment than the other (transform) properties. 1064 // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation 1065 // logic is dependent on how the view handles an internal call to onSetAlpha(). 1066 // We track what kinds of properties are set, and how alpha is handled when it is 1067 // set, and perform the invalidation steps appropriately. 1068 boolean alphaHandled = false; 1069 if (!useDisplayListProperties) { 1070 mView.invalidateParentCaches(); 1071 } 1072 float fraction = animation.getAnimatedFraction(); 1073 int propertyMask = propertyBundle.mPropertyMask; 1074 if ((propertyMask & TRANSFORM_MASK) != 0) { 1075 mView.invalidateViewProperty(false, false); 1076 } 1077 ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder; 1078 if (valueList != null) { 1079 int count = valueList.size(); 1080 for (int i = 0; i < count; ++i) { 1081 NameValuesHolder values = valueList.get(i); 1082 float value = values.mFromValue + fraction * values.mDeltaValue; 1083 if (values.mNameConstant == ALPHA) { 1084 alphaHandled = mView.setAlphaNoInvalidation(value); 1085 } else { 1086 setValue(values.mNameConstant, value); 1087 } 1088 } 1089 } 1090 if ((propertyMask & TRANSFORM_MASK) != 0) { 1091 mView.mTransformationInfo.mMatrixDirty = true; 1092 if (!useDisplayListProperties) { 1093 mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation 1094 } 1095 } 1096 // invalidate(false) in all cases except if alphaHandled gets set to true 1097 // via the call to setAlphaNoInvalidation(), above 1098 if (alphaHandled) { 1099 mView.invalidate(true); 1100 } else { 1101 mView.invalidateViewProperty(false, false); 1102 } 1103 if (mUpdateListener != null) { 1104 mUpdateListener.onAnimationUpdate(animation); 1105 } 1106 } 1107 } 1108} 1109