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