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