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