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 17 package android.view; 18 19 import android.animation.Animator; 20 import android.animation.ValueAnimator; 21 import android.animation.TimeInterpolator; 22 23 import java.util.ArrayList; 24 import java.util.HashMap; 25 import 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 */ 48 public 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 if (mView.isAttachedToWindow()) { 706 mView.buildLayer(); 707 } 708 } 709 }; 710 final int currentLayerType = mView.getLayerType(); 711 mPendingCleanupAction = new Runnable() { 712 @Override 713 public void run() { 714 mView.setLayerType(currentLayerType, null); 715 } 716 }; 717 if (mAnimatorSetupMap == null) { 718 mAnimatorSetupMap = new HashMap<Animator, Runnable>(); 719 } 720 if (mAnimatorCleanupMap == null) { 721 mAnimatorCleanupMap = new HashMap<Animator, Runnable>(); 722 } 723 724 return this; 725 } 726 727 /** 728 * Specifies an action to take place when the next animation runs. If there is a 729 * {@link #setStartDelay(long) startDelay} set on this ViewPropertyAnimator, then the 730 * action will run after that startDelay expires, when the actual animation begins. 731 * This method, along with {@link #withEndAction(Runnable)}, is intended to help facilitate 732 * choreographing ViewPropertyAnimator animations with other animations or actions 733 * in the application. 734 * 735 * @param runnable The action to run when the next animation starts. 736 * @return This object, allowing calls to methods in this class to be chained. 737 */ 738 public ViewPropertyAnimator withStartAction(Runnable runnable) { 739 mPendingOnStartAction = runnable; 740 if (runnable != null && mAnimatorOnStartMap == null) { 741 mAnimatorOnStartMap = new HashMap<Animator, Runnable>(); 742 } 743 return this; 744 } 745 746 /** 747 * Specifies an action to take place when the next animation ends. The action is only 748 * run if the animation ends normally; if the ViewPropertyAnimator is canceled during 749 * that animation, the runnable will not run. 750 * This method, along with {@link #withStartAction(Runnable)}, is intended to help facilitate 751 * choreographing ViewPropertyAnimator animations with other animations or actions 752 * in the application. 753 * 754 * <p>For example, the following code animates a view to x=200 and then back to 0:</p> 755 * <pre> 756 * Runnable endAction = new Runnable() { 757 * public void run() { 758 * view.animate().x(0); 759 * } 760 * }; 761 * view.animate().x(200).withEndAction(endAction); 762 * </pre> 763 * 764 * @param runnable The action to run when the next animation ends. 765 * @return This object, allowing calls to methods in this class to be chained. 766 */ 767 public ViewPropertyAnimator withEndAction(Runnable runnable) { 768 mPendingOnEndAction = runnable; 769 if (runnable != null && mAnimatorOnEndMap == null) { 770 mAnimatorOnEndMap = new HashMap<Animator, Runnable>(); 771 } 772 return this; 773 } 774 775 /** 776 * Starts the underlying Animator for a set of properties. We use a single animator that 777 * simply runs from 0 to 1, and then use that fractional value to set each property 778 * value accordingly. 779 */ 780 private void startAnimation() { 781 mView.setHasTransientState(true); 782 ValueAnimator animator = ValueAnimator.ofFloat(1.0f); 783 ArrayList<NameValuesHolder> nameValueList = 784 (ArrayList<NameValuesHolder>) mPendingAnimations.clone(); 785 mPendingAnimations.clear(); 786 int propertyMask = 0; 787 int propertyCount = nameValueList.size(); 788 for (int i = 0; i < propertyCount; ++i) { 789 NameValuesHolder nameValuesHolder = nameValueList.get(i); 790 propertyMask |= nameValuesHolder.mNameConstant; 791 } 792 mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList)); 793 if (mPendingSetupAction != null) { 794 mAnimatorSetupMap.put(animator, mPendingSetupAction); 795 mPendingSetupAction = null; 796 } 797 if (mPendingCleanupAction != null) { 798 mAnimatorCleanupMap.put(animator, mPendingCleanupAction); 799 mPendingCleanupAction = null; 800 } 801 if (mPendingOnStartAction != null) { 802 mAnimatorOnStartMap.put(animator, mPendingOnStartAction); 803 mPendingOnStartAction = null; 804 } 805 if (mPendingOnEndAction != null) { 806 mAnimatorOnEndMap.put(animator, mPendingOnEndAction); 807 mPendingOnEndAction = null; 808 } 809 animator.addUpdateListener(mAnimatorEventListener); 810 animator.addListener(mAnimatorEventListener); 811 if (mStartDelaySet) { 812 animator.setStartDelay(mStartDelay); 813 } 814 if (mDurationSet) { 815 animator.setDuration(mDuration); 816 } 817 if (mInterpolatorSet) { 818 animator.setInterpolator(mInterpolator); 819 } 820 animator.start(); 821 } 822 823 /** 824 * Utility function, called by the various x(), y(), etc. methods. This stores the 825 * constant name for the property along with the from/delta values that will be used to 826 * calculate and set the property during the animation. This structure is added to the 827 * pending animations, awaiting the eventual start() of the underlying animator. A 828 * Runnable is posted to start the animation, and any pending such Runnable is canceled 829 * (which enables us to end up starting just one animator for all of the properties 830 * specified at one time). 831 * 832 * @param constantName The specifier for the property being animated 833 * @param toValue The value to which the property will animate 834 */ 835 private void animateProperty(int constantName, float toValue) { 836 float fromValue = getValue(constantName); 837 float deltaValue = toValue - fromValue; 838 animatePropertyBy(constantName, fromValue, deltaValue); 839 } 840 841 /** 842 * Utility function, called by the various xBy(), yBy(), etc. methods. This method is 843 * just like animateProperty(), except the value is an offset from the property's 844 * current value, instead of an absolute "to" value. 845 * 846 * @param constantName The specifier for the property being animated 847 * @param byValue The amount by which the property will change 848 */ 849 private void animatePropertyBy(int constantName, float byValue) { 850 float fromValue = getValue(constantName); 851 animatePropertyBy(constantName, fromValue, byValue); 852 } 853 854 /** 855 * Utility function, called by animateProperty() and animatePropertyBy(), which handles the 856 * details of adding a pending animation and posting the request to start the animation. 857 * 858 * @param constantName The specifier for the property being animated 859 * @param startValue The starting value of the property 860 * @param byValue The amount by which the property will change 861 */ 862 private void animatePropertyBy(int constantName, float startValue, float byValue) { 863 // First, cancel any existing animations on this property 864 if (mAnimatorMap.size() > 0) { 865 Animator animatorToCancel = null; 866 Set<Animator> animatorSet = mAnimatorMap.keySet(); 867 for (Animator runningAnim : animatorSet) { 868 PropertyBundle bundle = mAnimatorMap.get(runningAnim); 869 if (bundle.cancel(constantName)) { 870 // property was canceled - cancel the animation if it's now empty 871 // Note that it's safe to break out here because every new animation 872 // on a property will cancel a previous animation on that property, so 873 // there can only ever be one such animation running. 874 if (bundle.mPropertyMask == NONE) { 875 // the animation is no longer changing anything - cancel it 876 animatorToCancel = runningAnim; 877 break; 878 } 879 } 880 } 881 if (animatorToCancel != null) { 882 animatorToCancel.cancel(); 883 } 884 } 885 886 NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue); 887 mPendingAnimations.add(nameValuePair); 888 mView.removeCallbacks(mAnimationStarter); 889 mView.postOnAnimation(mAnimationStarter); 890 } 891 892 /** 893 * This method handles setting the property values directly in the View object's fields. 894 * propertyConstant tells it which property should be set, value is the value to set 895 * the property to. 896 * 897 * @param propertyConstant The property to be set 898 * @param value The value to set the property to 899 */ 900 private void setValue(int propertyConstant, float value) { 901 final View.TransformationInfo info = mView.mTransformationInfo; 902 final DisplayList displayList = mView.mDisplayList; 903 switch (propertyConstant) { 904 case TRANSLATION_X: 905 info.mTranslationX = value; 906 if (displayList != null) displayList.setTranslationX(value); 907 break; 908 case TRANSLATION_Y: 909 info.mTranslationY = value; 910 if (displayList != null) displayList.setTranslationY(value); 911 break; 912 case ROTATION: 913 info.mRotation = value; 914 if (displayList != null) displayList.setRotation(value); 915 break; 916 case ROTATION_X: 917 info.mRotationX = value; 918 if (displayList != null) displayList.setRotationX(value); 919 break; 920 case ROTATION_Y: 921 info.mRotationY = value; 922 if (displayList != null) displayList.setRotationY(value); 923 break; 924 case SCALE_X: 925 info.mScaleX = value; 926 if (displayList != null) displayList.setScaleX(value); 927 break; 928 case SCALE_Y: 929 info.mScaleY = value; 930 if (displayList != null) displayList.setScaleY(value); 931 break; 932 case X: 933 info.mTranslationX = value - mView.mLeft; 934 if (displayList != null) displayList.setTranslationX(value - mView.mLeft); 935 break; 936 case Y: 937 info.mTranslationY = value - mView.mTop; 938 if (displayList != null) displayList.setTranslationY(value - mView.mTop); 939 break; 940 case ALPHA: 941 info.mAlpha = value; 942 if (displayList != null) displayList.setAlpha(value); 943 break; 944 } 945 } 946 947 /** 948 * This method gets the value of the named property from the View object. 949 * 950 * @param propertyConstant The property whose value should be returned 951 * @return float The value of the named property 952 */ 953 private float getValue(int propertyConstant) { 954 final View.TransformationInfo info = mView.mTransformationInfo; 955 switch (propertyConstant) { 956 case TRANSLATION_X: 957 return info.mTranslationX; 958 case TRANSLATION_Y: 959 return info.mTranslationY; 960 case ROTATION: 961 return info.mRotation; 962 case ROTATION_X: 963 return info.mRotationX; 964 case ROTATION_Y: 965 return info.mRotationY; 966 case SCALE_X: 967 return info.mScaleX; 968 case SCALE_Y: 969 return info.mScaleY; 970 case X: 971 return mView.mLeft + info.mTranslationX; 972 case Y: 973 return mView.mTop + info.mTranslationY; 974 case ALPHA: 975 return info.mAlpha; 976 } 977 return 0; 978 } 979 980 /** 981 * Utility class that handles the various Animator events. The only ones we care 982 * about are the end event (which we use to clean up the animator map when an animator 983 * finishes) and the update event (which we use to calculate the current value of each 984 * property and then set it on the view object). 985 */ 986 private class AnimatorEventListener 987 implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener { 988 @Override 989 public void onAnimationStart(Animator animation) { 990 if (mAnimatorSetupMap != null) { 991 Runnable r = mAnimatorSetupMap.get(animation); 992 if (r != null) { 993 r.run(); 994 } 995 mAnimatorSetupMap.remove(animation); 996 } 997 if (mAnimatorOnStartMap != null) { 998 Runnable r = mAnimatorOnStartMap.get(animation); 999 if (r != null) { 1000 r.run(); 1001 } 1002 mAnimatorOnStartMap.remove(animation); 1003 } 1004 if (mListener != null) { 1005 mListener.onAnimationStart(animation); 1006 } 1007 } 1008 1009 @Override 1010 public void onAnimationCancel(Animator animation) { 1011 if (mListener != null) { 1012 mListener.onAnimationCancel(animation); 1013 } 1014 if (mAnimatorOnEndMap != null) { 1015 mAnimatorOnEndMap.remove(animation); 1016 } 1017 } 1018 1019 @Override 1020 public void onAnimationRepeat(Animator animation) { 1021 if (mListener != null) { 1022 mListener.onAnimationRepeat(animation); 1023 } 1024 } 1025 1026 @Override 1027 public void onAnimationEnd(Animator animation) { 1028 mView.setHasTransientState(false); 1029 if (mListener != null) { 1030 mListener.onAnimationEnd(animation); 1031 } 1032 if (mAnimatorOnEndMap != null) { 1033 Runnable r = mAnimatorOnEndMap.get(animation); 1034 if (r != null) { 1035 r.run(); 1036 } 1037 mAnimatorOnEndMap.remove(animation); 1038 } 1039 if (mAnimatorCleanupMap != null) { 1040 Runnable r = mAnimatorCleanupMap.get(animation); 1041 if (r != null) { 1042 r.run(); 1043 } 1044 mAnimatorCleanupMap.remove(animation); 1045 } 1046 mAnimatorMap.remove(animation); 1047 } 1048 1049 /** 1050 * Calculate the current value for each property and set it on the view. Invalidate 1051 * the view object appropriately, depending on which properties are being animated. 1052 * 1053 * @param animation The animator associated with the properties that need to be 1054 * set. This animator holds the animation fraction which we will use to calculate 1055 * the current value of each property. 1056 */ 1057 @Override 1058 public void onAnimationUpdate(ValueAnimator animation) { 1059 PropertyBundle propertyBundle = mAnimatorMap.get(animation); 1060 if (propertyBundle == null) { 1061 // Shouldn't happen, but just to play it safe 1062 return; 1063 } 1064 boolean useDisplayListProperties = mView.mDisplayList != null; 1065 1066 // alpha requires slightly different treatment than the other (transform) properties. 1067 // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation 1068 // logic is dependent on how the view handles an internal call to onSetAlpha(). 1069 // We track what kinds of properties are set, and how alpha is handled when it is 1070 // set, and perform the invalidation steps appropriately. 1071 boolean alphaHandled = false; 1072 if (!useDisplayListProperties) { 1073 mView.invalidateParentCaches(); 1074 } 1075 float fraction = animation.getAnimatedFraction(); 1076 int propertyMask = propertyBundle.mPropertyMask; 1077 if ((propertyMask & TRANSFORM_MASK) != 0) { 1078 mView.invalidateViewProperty(false, false); 1079 } 1080 ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder; 1081 if (valueList != null) { 1082 int count = valueList.size(); 1083 for (int i = 0; i < count; ++i) { 1084 NameValuesHolder values = valueList.get(i); 1085 float value = values.mFromValue + fraction * values.mDeltaValue; 1086 if (values.mNameConstant == ALPHA) { 1087 alphaHandled = mView.setAlphaNoInvalidation(value); 1088 } else { 1089 setValue(values.mNameConstant, value); 1090 } 1091 } 1092 } 1093 if ((propertyMask & TRANSFORM_MASK) != 0) { 1094 mView.mTransformationInfo.mMatrixDirty = true; 1095 if (!useDisplayListProperties) { 1096 mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation 1097 } 1098 } 1099 // invalidate(false) in all cases except if alphaHandled gets set to true 1100 // via the call to setAlphaNoInvalidation(), above 1101 if (alphaHandled) { 1102 mView.invalidate(true); 1103 } else { 1104 mView.invalidateViewProperty(false, false); 1105 } 1106 if (mUpdateListener != null) { 1107 mUpdateListener.onAnimationUpdate(animation); 1108 } 1109 } 1110 } 1111 } 1112