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