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