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 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 private 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 97 */ 98 private Animator.AnimatorListener mListener = null; 99 100 /** 101 * This listener is the mechanism by which the underlying Animator causes changes to the 102 * properties currently being animated, as well as the cleanup after an animation is 103 * complete. 104 */ 105 private AnimatorEventListener mAnimatorEventListener = new AnimatorEventListener(); 106 107 /** 108 * This list holds the properties that have been asked to animate. We allow the caller to 109 * request several animations prior to actually starting the underlying animator. This 110 * enables us to run one single animator to handle several properties in parallel. Each 111 * property is tossed onto the pending list until the animation actually starts (which is 112 * done by posting it onto mView), at which time the pending list is cleared and the properties 113 * on that list are added to the list of properties associated with that animator. 114 */ 115 ArrayList<NameValuesHolder> mPendingAnimations = new ArrayList<NameValuesHolder>(); 116 117 /** 118 * Constants used to associate a property being requested and the mechanism used to set 119 * the property (this class calls directly into View to set the properties in question). 120 */ 121 private static final int NONE = 0x0000; 122 private static final int TRANSLATION_X = 0x0001; 123 private static final int TRANSLATION_Y = 0x0002; 124 private static final int SCALE_X = 0x0004; 125 private static final int SCALE_Y = 0x0008; 126 private static final int ROTATION = 0x0010; 127 private static final int ROTATION_X = 0x0020; 128 private static final int ROTATION_Y = 0x0040; 129 private static final int X = 0x0080; 130 private static final int Y = 0x0100; 131 private static final int ALPHA = 0x0200; 132 133 private static final int TRANSFORM_MASK = TRANSLATION_X | TRANSLATION_Y | SCALE_X | SCALE_Y | 134 ROTATION | ROTATION_X | ROTATION_Y | X | Y; 135 136 /** 137 * The mechanism by which the user can request several properties that are then animated 138 * together works by posting this Runnable to start the underlying Animator. Every time 139 * a property animation is requested, we cancel any previous postings of the Runnable 140 * and re-post it. This means that we will only ever run the Runnable (and thus start the 141 * underlying animator) after the caller is done setting the properties that should be 142 * animated together. 143 */ 144 private Runnable mAnimationStarter = new Runnable() { 145 @Override 146 public void run() { 147 startAnimation(); 148 } 149 }; 150 151 /** 152 * This class holds information about the overall animation being run on the set of 153 * properties. The mask describes which properties are being animated and the 154 * values holder is the list of all property/value objects. 155 */ 156 private static class PropertyBundle { 157 int mPropertyMask; 158 ArrayList<NameValuesHolder> mNameValuesHolder; 159 PropertyBundle(int propertyMask, ArrayList<NameValuesHolder> nameValuesHolder)160 PropertyBundle(int propertyMask, ArrayList<NameValuesHolder> nameValuesHolder) { 161 mPropertyMask = propertyMask; 162 mNameValuesHolder = nameValuesHolder; 163 } 164 165 /** 166 * Removes the given property from being animated as a part of this 167 * PropertyBundle. If the property was a part of this bundle, it returns 168 * true to indicate that it was, in fact, canceled. This is an indication 169 * to the caller that a cancellation actually occurred. 170 * 171 * @param propertyConstant The property whose cancellation is requested. 172 * @return true if the given property is a part of this bundle and if it 173 * has therefore been canceled. 174 */ cancel(int propertyConstant)175 boolean cancel(int propertyConstant) { 176 if ((mPropertyMask & propertyConstant) != 0 && mNameValuesHolder != null) { 177 int count = mNameValuesHolder.size(); 178 for (int i = 0; i < count; ++i) { 179 NameValuesHolder nameValuesHolder = mNameValuesHolder.get(i); 180 if (nameValuesHolder.mNameConstant == propertyConstant) { 181 mNameValuesHolder.remove(i); 182 mPropertyMask &= ~propertyConstant; 183 return true; 184 } 185 } 186 } 187 return false; 188 } 189 } 190 191 /** 192 * This list tracks the list of properties being animated by any particular animator. 193 * In most situations, there would only ever be one animator running at a time. But it is 194 * possible to request some properties to animate together, then while those properties 195 * are animating, to request some other properties to animate together. The way that 196 * works is by having this map associate the group of properties being animated with the 197 * animator handling the animation. On every update event for an Animator, we ask the 198 * map for the associated properties and set them accordingly. 199 */ 200 private HashMap<Animator, PropertyBundle> mAnimatorMap = 201 new HashMap<Animator, PropertyBundle>(); 202 203 /** 204 * This is the information we need to set each property during the animation. 205 * mNameConstant is used to set the appropriate field in View, and the from/delta 206 * values are used to calculate the animated value for a given animation fraction 207 * during the animation. 208 */ 209 private static class NameValuesHolder { 210 int mNameConstant; 211 float mFromValue; 212 float mDeltaValue; NameValuesHolder(int nameConstant, float fromValue, float deltaValue)213 NameValuesHolder(int nameConstant, float fromValue, float deltaValue) { 214 mNameConstant = nameConstant; 215 mFromValue = fromValue; 216 mDeltaValue = deltaValue; 217 } 218 } 219 220 /** 221 * Constructor, called by View. This is private by design, as the user should only 222 * get a ViewPropertyAnimator by calling View.animate(). 223 * 224 * @param view The View associated with this ViewPropertyAnimator 225 */ ViewPropertyAnimator(View view)226 ViewPropertyAnimator(View view) { 227 mView = view; 228 view.ensureTransformationInfo(); 229 } 230 231 /** 232 * Sets the duration for the underlying animator that animates the requested properties. 233 * By default, the animator uses the default value for ValueAnimator. Calling this method 234 * will cause the declared value to be used instead. 235 * @param duration The length of ensuing property animations, in milliseconds. The value 236 * cannot be negative. 237 * @return This object, allowing calls to methods in this class to be chained. 238 */ setDuration(long duration)239 public ViewPropertyAnimator setDuration(long duration) { 240 if (duration < 0) { 241 throw new IllegalArgumentException("Animators cannot have negative duration: " + 242 duration); 243 } 244 mDurationSet = true; 245 mDuration = duration; 246 return this; 247 } 248 249 /** 250 * Returns the current duration of property animations. If the duration was set on this 251 * object, that value is returned. Otherwise, the default value of the underlying Animator 252 * is returned. 253 * 254 * @see #setDuration(long) 255 * @return The duration of animations, in milliseconds. 256 */ getDuration()257 public long getDuration() { 258 if (mDurationSet) { 259 return mDuration; 260 } else { 261 // Just return the default from ValueAnimator, since that's what we'd get if 262 // the value has not been set otherwise 263 return new ValueAnimator().getDuration(); 264 } 265 } 266 267 /** 268 * Returns the current startDelay of property animations. If the startDelay was set on this 269 * object, that value is returned. Otherwise, the default value of the underlying Animator 270 * is returned. 271 * 272 * @see #setStartDelay(long) 273 * @return The startDelay of animations, in milliseconds. 274 */ getStartDelay()275 public long getStartDelay() { 276 if (mStartDelaySet) { 277 return mStartDelay; 278 } else { 279 // Just return the default from ValueAnimator (0), since that's what we'd get if 280 // the value has not been set otherwise 281 return 0; 282 } 283 } 284 285 /** 286 * Sets the startDelay for the underlying animator that animates the requested properties. 287 * By default, the animator uses the default value for ValueAnimator. Calling this method 288 * will cause the declared value to be used instead. 289 * @param startDelay The delay of ensuing property animations, in milliseconds. The value 290 * cannot be negative. 291 * @return This object, allowing calls to methods in this class to be chained. 292 */ setStartDelay(long startDelay)293 public ViewPropertyAnimator setStartDelay(long startDelay) { 294 if (startDelay < 0) { 295 throw new IllegalArgumentException("Animators cannot have negative duration: " + 296 startDelay); 297 } 298 mStartDelaySet = true; 299 mStartDelay = startDelay; 300 return this; 301 } 302 303 /** 304 * Sets the interpolator for the underlying animator that animates the requested properties. 305 * By default, the animator uses the default interpolator for ValueAnimator. Calling this method 306 * will cause the declared object to be used instead. 307 * 308 * @param interpolator The TimeInterpolator to be used for ensuing property animations. 309 * @return This object, allowing calls to methods in this class to be chained. 310 */ setInterpolator(TimeInterpolator interpolator)311 public ViewPropertyAnimator setInterpolator(TimeInterpolator interpolator) { 312 mInterpolatorSet = true; 313 mInterpolator = interpolator; 314 return this; 315 } 316 317 /** 318 * Sets a listener for events in the underlying Animators that run the property 319 * animations. 320 * 321 * @param listener The listener to be called with AnimatorListener events. 322 * @return This object, allowing calls to methods in this class to be chained. 323 */ setListener(Animator.AnimatorListener listener)324 public ViewPropertyAnimator setListener(Animator.AnimatorListener listener) { 325 mListener = listener; 326 return this; 327 } 328 329 /** 330 * Starts the currently pending property animations immediately. Calling <code>start()</code> 331 * is optional because all animations start automatically at the next opportunity. However, 332 * if the animations are needed to start immediately and synchronously (not at the time when 333 * the next event is processed by the hierarchy, which is when the animations would begin 334 * otherwise), then this method can be used. 335 */ start()336 public void start() { 337 startAnimation(); 338 } 339 340 /** 341 * Cancels all property animations that are currently running or pending. 342 */ cancel()343 public void cancel() { 344 if (mAnimatorMap.size() > 0) { 345 HashMap<Animator, PropertyBundle> mAnimatorMapCopy = 346 (HashMap<Animator, PropertyBundle>)mAnimatorMap.clone(); 347 Set<Animator> animatorSet = mAnimatorMapCopy.keySet(); 348 for (Animator runningAnim : animatorSet) { 349 runningAnim.cancel(); 350 } 351 } 352 mPendingAnimations.clear(); 353 mView.removeCallbacks(mAnimationStarter); 354 } 355 356 /** 357 * This method will cause the View's <code>x</code> property to be animated to the 358 * specified value. Animations already running on the property will be canceled. 359 * 360 * @param value The value to be animated to. 361 * @see View#setX(float) 362 * @return This object, allowing calls to methods in this class to be chained. 363 */ x(float value)364 public ViewPropertyAnimator x(float value) { 365 animateProperty(X, value); 366 return this; 367 } 368 369 /** 370 * This method will cause the View's <code>x</code> property to be animated by the 371 * specified value. Animations already running on the property will be canceled. 372 * 373 * @param value The amount to be animated by, as an offset from the current value. 374 * @see View#setX(float) 375 * @return This object, allowing calls to methods in this class to be chained. 376 */ xBy(float value)377 public ViewPropertyAnimator xBy(float value) { 378 animatePropertyBy(X, value); 379 return this; 380 } 381 382 /** 383 * This method will cause the View's <code>y</code> property to be animated to the 384 * specified value. Animations already running on the property will be canceled. 385 * 386 * @param value The value to be animated to. 387 * @see View#setY(float) 388 * @return This object, allowing calls to methods in this class to be chained. 389 */ y(float value)390 public ViewPropertyAnimator y(float value) { 391 animateProperty(Y, value); 392 return this; 393 } 394 395 /** 396 * This method will cause the View's <code>y</code> property to be animated by the 397 * specified value. Animations already running on the property will be canceled. 398 * 399 * @param value The amount to be animated by, as an offset from the current value. 400 * @see View#setY(float) 401 * @return This object, allowing calls to methods in this class to be chained. 402 */ yBy(float value)403 public ViewPropertyAnimator yBy(float value) { 404 animatePropertyBy(Y, value); 405 return this; 406 } 407 408 /** 409 * This method will cause the View's <code>rotation</code> property to be animated to the 410 * specified value. Animations already running on the property will be canceled. 411 * 412 * @param value The value to be animated to. 413 * @see View#setRotation(float) 414 * @return This object, allowing calls to methods in this class to be chained. 415 */ rotation(float value)416 public ViewPropertyAnimator rotation(float value) { 417 animateProperty(ROTATION, value); 418 return this; 419 } 420 421 /** 422 * This method will cause the View's <code>rotation</code> property to be animated by the 423 * specified value. Animations already running on the property will be canceled. 424 * 425 * @param value The amount to be animated by, as an offset from the current value. 426 * @see View#setRotation(float) 427 * @return This object, allowing calls to methods in this class to be chained. 428 */ rotationBy(float value)429 public ViewPropertyAnimator rotationBy(float value) { 430 animatePropertyBy(ROTATION, value); 431 return this; 432 } 433 434 /** 435 * This method will cause the View's <code>rotationX</code> property to be animated to the 436 * specified value. Animations already running on the property will be canceled. 437 * 438 * @param value The value to be animated to. 439 * @see View#setRotationX(float) 440 * @return This object, allowing calls to methods in this class to be chained. 441 */ rotationX(float value)442 public ViewPropertyAnimator rotationX(float value) { 443 animateProperty(ROTATION_X, value); 444 return this; 445 } 446 447 /** 448 * This method will cause the View's <code>rotationX</code> property to be animated by the 449 * specified value. Animations already running on the property will be canceled. 450 * 451 * @param value The amount to be animated by, as an offset from the current value. 452 * @see View#setRotationX(float) 453 * @return This object, allowing calls to methods in this class to be chained. 454 */ rotationXBy(float value)455 public ViewPropertyAnimator rotationXBy(float value) { 456 animatePropertyBy(ROTATION_X, value); 457 return this; 458 } 459 460 /** 461 * This method will cause the View's <code>rotationY</code> property to be animated to the 462 * specified value. Animations already running on the property will be canceled. 463 * 464 * @param value The value to be animated to. 465 * @see View#setRotationY(float) 466 * @return This object, allowing calls to methods in this class to be chained. 467 */ rotationY(float value)468 public ViewPropertyAnimator rotationY(float value) { 469 animateProperty(ROTATION_Y, value); 470 return this; 471 } 472 473 /** 474 * This method will cause the View's <code>rotationY</code> property to be animated by the 475 * specified value. Animations already running on the property will be canceled. 476 * 477 * @param value The amount to be animated by, as an offset from the current value. 478 * @see View#setRotationY(float) 479 * @return This object, allowing calls to methods in this class to be chained. 480 */ rotationYBy(float value)481 public ViewPropertyAnimator rotationYBy(float value) { 482 animatePropertyBy(ROTATION_Y, value); 483 return this; 484 } 485 486 /** 487 * This method will cause the View's <code>translationX</code> property to be animated to the 488 * specified value. Animations already running on the property will be canceled. 489 * 490 * @param value The value to be animated to. 491 * @see View#setTranslationX(float) 492 * @return This object, allowing calls to methods in this class to be chained. 493 */ translationX(float value)494 public ViewPropertyAnimator translationX(float value) { 495 animateProperty(TRANSLATION_X, value); 496 return this; 497 } 498 499 /** 500 * This method will cause the View's <code>translationX</code> property to be animated by the 501 * specified value. Animations already running on the property will be canceled. 502 * 503 * @param value The amount to be animated by, as an offset from the current value. 504 * @see View#setTranslationX(float) 505 * @return This object, allowing calls to methods in this class to be chained. 506 */ translationXBy(float value)507 public ViewPropertyAnimator translationXBy(float value) { 508 animatePropertyBy(TRANSLATION_X, value); 509 return this; 510 } 511 512 /** 513 * This method will cause the View's <code>translationY</code> property to be animated to the 514 * specified value. Animations already running on the property will be canceled. 515 * 516 * @param value The value to be animated to. 517 * @see View#setTranslationY(float) 518 * @return This object, allowing calls to methods in this class to be chained. 519 */ translationY(float value)520 public ViewPropertyAnimator translationY(float value) { 521 animateProperty(TRANSLATION_Y, value); 522 return this; 523 } 524 525 /** 526 * This method will cause the View's <code>translationY</code> property to be animated by the 527 * specified value. Animations already running on the property will be canceled. 528 * 529 * @param value The amount to be animated by, as an offset from the current value. 530 * @see View#setTranslationY(float) 531 * @return This object, allowing calls to methods in this class to be chained. 532 */ translationYBy(float value)533 public ViewPropertyAnimator translationYBy(float value) { 534 animatePropertyBy(TRANSLATION_Y, value); 535 return this; 536 } 537 538 /** 539 * This method will cause the View's <code>scaleX</code> property to be animated to the 540 * specified value. Animations already running on the property will be canceled. 541 * 542 * @param value The value to be animated to. 543 * @see View#setScaleX(float) 544 * @return This object, allowing calls to methods in this class to be chained. 545 */ scaleX(float value)546 public ViewPropertyAnimator scaleX(float value) { 547 animateProperty(SCALE_X, value); 548 return this; 549 } 550 551 /** 552 * This method will cause the View's <code>scaleX</code> property to be animated by the 553 * specified value. Animations already running on the property will be canceled. 554 * 555 * @param value The amount to be animated by, as an offset from the current value. 556 * @see View#setScaleX(float) 557 * @return This object, allowing calls to methods in this class to be chained. 558 */ scaleXBy(float value)559 public ViewPropertyAnimator scaleXBy(float value) { 560 animatePropertyBy(SCALE_X, value); 561 return this; 562 } 563 564 /** 565 * This method will cause the View's <code>scaleY</code> property to be animated to the 566 * specified value. Animations already running on the property will be canceled. 567 * 568 * @param value The value to be animated to. 569 * @see View#setScaleY(float) 570 * @return This object, allowing calls to methods in this class to be chained. 571 */ scaleY(float value)572 public ViewPropertyAnimator scaleY(float value) { 573 animateProperty(SCALE_Y, value); 574 return this; 575 } 576 577 /** 578 * This method will cause the View's <code>scaleY</code> property to be animated by the 579 * specified value. Animations already running on the property will be canceled. 580 * 581 * @param value The amount to be animated by, as an offset from the current value. 582 * @see View#setScaleY(float) 583 * @return This object, allowing calls to methods in this class to be chained. 584 */ scaleYBy(float value)585 public ViewPropertyAnimator scaleYBy(float value) { 586 animatePropertyBy(SCALE_Y, value); 587 return this; 588 } 589 590 /** 591 * This method will cause the View's <code>alpha</code> property to be animated to the 592 * specified value. Animations already running on the property will be canceled. 593 * 594 * @param value The value to be animated to. 595 * @see View#setAlpha(float) 596 * @return This object, allowing calls to methods in this class to be chained. 597 */ alpha(float value)598 public ViewPropertyAnimator alpha(float value) { 599 animateProperty(ALPHA, value); 600 return this; 601 } 602 603 /** 604 * This method will cause the View's <code>alpha</code> property to be animated by the 605 * specified value. Animations already running on the property will be canceled. 606 * 607 * @param value The amount to be animated by, as an offset from the current value. 608 * @see View#setAlpha(float) 609 * @return This object, allowing calls to methods in this class to be chained. 610 */ alphaBy(float value)611 public ViewPropertyAnimator alphaBy(float value) { 612 animatePropertyBy(ALPHA, value); 613 return this; 614 } 615 616 /** 617 * Starts the underlying Animator for a set of properties. We use a single animator that 618 * simply runs from 0 to 1, and then use that fractional value to set each property 619 * value accordingly. 620 */ startAnimation()621 private void startAnimation() { 622 ValueAnimator animator = ValueAnimator.ofFloat(1.0f); 623 ArrayList<NameValuesHolder> nameValueList = 624 (ArrayList<NameValuesHolder>) mPendingAnimations.clone(); 625 mPendingAnimations.clear(); 626 int propertyMask = 0; 627 int propertyCount = nameValueList.size(); 628 for (int i = 0; i < propertyCount; ++i) { 629 NameValuesHolder nameValuesHolder = nameValueList.get(i); 630 propertyMask |= nameValuesHolder.mNameConstant; 631 } 632 mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList)); 633 animator.addUpdateListener(mAnimatorEventListener); 634 animator.addListener(mAnimatorEventListener); 635 if (mStartDelaySet) { 636 animator.setStartDelay(mStartDelay); 637 } 638 if (mDurationSet) { 639 animator.setDuration(mDuration); 640 } 641 if (mInterpolatorSet) { 642 animator.setInterpolator(mInterpolator); 643 } 644 animator.start(); 645 } 646 647 /** 648 * Utility function, called by the various x(), y(), etc. methods. This stores the 649 * constant name for the property along with the from/delta values that will be used to 650 * calculate and set the property during the animation. This structure is added to the 651 * pending animations, awaiting the eventual start() of the underlying animator. A 652 * Runnable is posted to start the animation, and any pending such Runnable is canceled 653 * (which enables us to end up starting just one animator for all of the properties 654 * specified at one time). 655 * 656 * @param constantName The specifier for the property being animated 657 * @param toValue The value to which the property will animate 658 */ animateProperty(int constantName, float toValue)659 private void animateProperty(int constantName, float toValue) { 660 float fromValue = getValue(constantName); 661 float deltaValue = toValue - fromValue; 662 animatePropertyBy(constantName, fromValue, deltaValue); 663 } 664 665 /** 666 * Utility function, called by the various xBy(), yBy(), etc. methods. This method is 667 * just like animateProperty(), except the value is an offset from the property's 668 * current value, instead of an absolute "to" value. 669 * 670 * @param constantName The specifier for the property being animated 671 * @param byValue The amount by which the property will change 672 */ animatePropertyBy(int constantName, float byValue)673 private void animatePropertyBy(int constantName, float byValue) { 674 float fromValue = getValue(constantName); 675 animatePropertyBy(constantName, fromValue, byValue); 676 } 677 678 /** 679 * Utility function, called by animateProperty() and animatePropertyBy(), which handles the 680 * details of adding a pending animation and posting the request to start the animation. 681 * 682 * @param constantName The specifier for the property being animated 683 * @param startValue The starting value of the property 684 * @param byValue The amount by which the property will change 685 */ animatePropertyBy(int constantName, float startValue, float byValue)686 private void animatePropertyBy(int constantName, float startValue, float byValue) { 687 // First, cancel any existing animations on this property 688 if (mAnimatorMap.size() > 0) { 689 Animator animatorToCancel = null; 690 Set<Animator> animatorSet = mAnimatorMap.keySet(); 691 for (Animator runningAnim : animatorSet) { 692 PropertyBundle bundle = mAnimatorMap.get(runningAnim); 693 if (bundle.cancel(constantName)) { 694 // property was canceled - cancel the animation if it's now empty 695 // Note that it's safe to break out here because every new animation 696 // on a property will cancel a previous animation on that property, so 697 // there can only ever be one such animation running. 698 if (bundle.mPropertyMask == NONE) { 699 // the animation is no longer changing anything - cancel it 700 animatorToCancel = runningAnim; 701 break; 702 } 703 } 704 } 705 if (animatorToCancel != null) { 706 animatorToCancel.cancel(); 707 } 708 } 709 710 NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue); 711 mPendingAnimations.add(nameValuePair); 712 mView.removeCallbacks(mAnimationStarter); 713 mView.post(mAnimationStarter); 714 } 715 716 /** 717 * This method handles setting the property values directly in the View object's fields. 718 * propertyConstant tells it which property should be set, value is the value to set 719 * the property to. 720 * 721 * @param propertyConstant The property to be set 722 * @param value The value to set the property to 723 */ setValue(int propertyConstant, float value)724 private void setValue(int propertyConstant, float value) { 725 final View.TransformationInfo info = mView.mTransformationInfo; 726 switch (propertyConstant) { 727 case TRANSLATION_X: 728 info.mTranslationX = value; 729 break; 730 case TRANSLATION_Y: 731 info.mTranslationY = value; 732 break; 733 case ROTATION: 734 info.mRotation = value; 735 break; 736 case ROTATION_X: 737 info.mRotationX = value; 738 break; 739 case ROTATION_Y: 740 info.mRotationY = value; 741 break; 742 case SCALE_X: 743 info.mScaleX = value; 744 break; 745 case SCALE_Y: 746 info.mScaleY = value; 747 break; 748 case X: 749 info.mTranslationX = value - mView.mLeft; 750 break; 751 case Y: 752 info.mTranslationY = value - mView.mTop; 753 break; 754 case ALPHA: 755 info.mAlpha = value; 756 break; 757 } 758 } 759 760 /** 761 * This method gets the value of the named property from the View object. 762 * 763 * @param propertyConstant The property whose value should be returned 764 * @return float The value of the named property 765 */ getValue(int propertyConstant)766 private float getValue(int propertyConstant) { 767 final View.TransformationInfo info = mView.mTransformationInfo; 768 switch (propertyConstant) { 769 case TRANSLATION_X: 770 return info.mTranslationX; 771 case TRANSLATION_Y: 772 return info.mTranslationY; 773 case ROTATION: 774 return info.mRotation; 775 case ROTATION_X: 776 return info.mRotationX; 777 case ROTATION_Y: 778 return info.mRotationY; 779 case SCALE_X: 780 return info.mScaleX; 781 case SCALE_Y: 782 return info.mScaleY; 783 case X: 784 return mView.mLeft + info.mTranslationX; 785 case Y: 786 return mView.mTop + info.mTranslationY; 787 case ALPHA: 788 return info.mAlpha; 789 } 790 return 0; 791 } 792 793 /** 794 * Utility class that handles the various Animator events. The only ones we care 795 * about are the end event (which we use to clean up the animator map when an animator 796 * finishes) and the update event (which we use to calculate the current value of each 797 * property and then set it on the view object). 798 */ 799 private class AnimatorEventListener 800 implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener { 801 @Override onAnimationStart(Animator animation)802 public void onAnimationStart(Animator animation) { 803 if (mListener != null) { 804 mListener.onAnimationStart(animation); 805 } 806 } 807 808 @Override onAnimationCancel(Animator animation)809 public void onAnimationCancel(Animator animation) { 810 if (mListener != null) { 811 mListener.onAnimationCancel(animation); 812 } 813 } 814 815 @Override onAnimationRepeat(Animator animation)816 public void onAnimationRepeat(Animator animation) { 817 if (mListener != null) { 818 mListener.onAnimationRepeat(animation); 819 } 820 } 821 822 @Override onAnimationEnd(Animator animation)823 public void onAnimationEnd(Animator animation) { 824 if (mListener != null) { 825 mListener.onAnimationEnd(animation); 826 } 827 mAnimatorMap.remove(animation); 828 } 829 830 /** 831 * Calculate the current value for each property and set it on the view. Invalidate 832 * the view object appropriately, depending on which properties are being animated. 833 * 834 * @param animation The animator associated with the properties that need to be 835 * set. This animator holds the animation fraction which we will use to calculate 836 * the current value of each property. 837 */ 838 @Override onAnimationUpdate(ValueAnimator animation)839 public void onAnimationUpdate(ValueAnimator animation) { 840 // alpha requires slightly different treatment than the other (transform) properties. 841 // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation 842 // logic is dependent on how the view handles an internal call to onSetAlpha(). 843 // We track what kinds of properties are set, and how alpha is handled when it is 844 // set, and perform the invalidation steps appropriately. 845 boolean alphaHandled = false; 846 mView.invalidateParentCaches(); 847 float fraction = animation.getAnimatedFraction(); 848 PropertyBundle propertyBundle = mAnimatorMap.get(animation); 849 int propertyMask = propertyBundle.mPropertyMask; 850 if ((propertyMask & TRANSFORM_MASK) != 0) { 851 mView.invalidate(false); 852 } 853 ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder; 854 if (valueList != null) { 855 int count = valueList.size(); 856 for (int i = 0; i < count; ++i) { 857 NameValuesHolder values = valueList.get(i); 858 float value = values.mFromValue + fraction * values.mDeltaValue; 859 if (values.mNameConstant == ALPHA) { 860 alphaHandled = mView.setAlphaNoInvalidation(value); 861 } else { 862 setValue(values.mNameConstant, value); 863 } 864 } 865 } 866 if ((propertyMask & TRANSFORM_MASK) != 0) { 867 mView.mTransformationInfo.mMatrixDirty = true; 868 mView.mPrivateFlags |= View.DRAWN; // force another invalidation 869 } 870 // invalidate(false) in all cases except if alphaHandled gets set to true 871 // via the call to setAlphaNoInvalidation(), above 872 mView.invalidate(alphaHandled); 873 } 874 } 875 } 876