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