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