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