Animation.java revision 80756e38882720860db52f1fcc21fa1505a02abf
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.view.animation; 18 19import android.annotation.ColorInt; 20import android.content.Context; 21import android.content.res.TypedArray; 22import android.graphics.RectF; 23import android.os.Handler; 24import android.os.SystemProperties; 25import android.util.AttributeSet; 26import android.util.TypedValue; 27import dalvik.system.CloseGuard; 28 29/** 30 * Abstraction for an Animation that can be applied to Views, Surfaces, or 31 * other objects. See the {@link android.view.animation animation package 32 * description file}. 33 */ 34public abstract class Animation implements Cloneable { 35 /** 36 * Repeat the animation indefinitely. 37 */ 38 public static final int INFINITE = -1; 39 40 /** 41 * When the animation reaches the end and the repeat count is INFINTE_REPEAT 42 * or a positive value, the animation restarts from the beginning. 43 */ 44 public static final int RESTART = 1; 45 46 /** 47 * When the animation reaches the end and the repeat count is INFINTE_REPEAT 48 * or a positive value, the animation plays backward (and then forward again). 49 */ 50 public static final int REVERSE = 2; 51 52 /** 53 * Can be used as the start time to indicate the start time should be the current 54 * time when {@link #getTransformation(long, Transformation)} is invoked for the 55 * first animation frame. This can is useful for short animations. 56 */ 57 public static final int START_ON_FIRST_FRAME = -1; 58 59 /** 60 * The specified dimension is an absolute number of pixels. 61 */ 62 public static final int ABSOLUTE = 0; 63 64 /** 65 * The specified dimension holds a float and should be multiplied by the 66 * height or width of the object being animated. 67 */ 68 public static final int RELATIVE_TO_SELF = 1; 69 70 /** 71 * The specified dimension holds a float and should be multiplied by the 72 * height or width of the parent of the object being animated. 73 */ 74 public static final int RELATIVE_TO_PARENT = 2; 75 76 /** 77 * Requests that the content being animated be kept in its current Z 78 * order. 79 */ 80 public static final int ZORDER_NORMAL = 0; 81 82 /** 83 * Requests that the content being animated be forced on top of all other 84 * content for the duration of the animation. 85 */ 86 public static final int ZORDER_TOP = 1; 87 88 /** 89 * Requests that the content being animated be forced under all other 90 * content for the duration of the animation. 91 */ 92 public static final int ZORDER_BOTTOM = -1; 93 94 private static final boolean USE_CLOSEGUARD 95 = SystemProperties.getBoolean("log.closeguard.Animation", false); 96 97 /** 98 * Set by {@link #getTransformation(long, Transformation)} when the animation ends. 99 */ 100 boolean mEnded = false; 101 102 /** 103 * Set by {@link #getTransformation(long, Transformation)} when the animation starts. 104 */ 105 boolean mStarted = false; 106 107 /** 108 * Set by {@link #getTransformation(long, Transformation)} when the animation repeats 109 * in REVERSE mode. 110 */ 111 boolean mCycleFlip = false; 112 113 /** 114 * This value must be set to true by {@link #initialize(int, int, int, int)}. It 115 * indicates the animation was successfully initialized and can be played. 116 */ 117 boolean mInitialized = false; 118 119 /** 120 * Indicates whether the animation transformation should be applied before the 121 * animation starts. The value of this variable is only relevant if mFillEnabled is true; 122 * otherwise it is assumed to be true. 123 */ 124 boolean mFillBefore = true; 125 126 /** 127 * Indicates whether the animation transformation should be applied after the 128 * animation ends. 129 */ 130 boolean mFillAfter = false; 131 132 /** 133 * Indicates whether fillBefore should be taken into account. 134 */ 135 boolean mFillEnabled = false; 136 137 /** 138 * The time in milliseconds at which the animation must start; 139 */ 140 long mStartTime = -1; 141 142 /** 143 * The delay in milliseconds after which the animation must start. When the 144 * start offset is > 0, the start time of the animation is startTime + startOffset. 145 */ 146 long mStartOffset; 147 148 /** 149 * The duration of one animation cycle in milliseconds. 150 */ 151 long mDuration; 152 153 /** 154 * The number of times the animation must repeat. By default, an animation repeats 155 * indefinitely. 156 */ 157 int mRepeatCount = 0; 158 159 /** 160 * Indicates how many times the animation was repeated. 161 */ 162 int mRepeated = 0; 163 164 /** 165 * The behavior of the animation when it repeats. The repeat mode is either 166 * {@link #RESTART} or {@link #REVERSE}. 167 * 168 */ 169 int mRepeatMode = RESTART; 170 171 /** 172 * The interpolator used by the animation to smooth the movement. 173 */ 174 Interpolator mInterpolator; 175 176 /** 177 * The animation listener to be notified when the animation starts, ends or repeats. 178 */ 179 AnimationListener mListener; 180 181 /** 182 * Desired Z order mode during animation. 183 */ 184 private int mZAdjustment; 185 186 /** 187 * Desired background color behind animation. 188 */ 189 private int mBackgroundColor; 190 191 /** 192 * scalefactor to apply to pivot points, etc. during animation. Subclasses retrieve the 193 * value via getScaleFactor(). 194 */ 195 private float mScaleFactor = 1f; 196 197 /** 198 * Don't animate the wallpaper. 199 */ 200 private boolean mDetachWallpaper = false; 201 202 private boolean mMore = true; 203 private boolean mOneMoreTime = true; 204 205 RectF mPreviousRegion = new RectF(); 206 RectF mRegion = new RectF(); 207 Transformation mTransformation = new Transformation(); 208 Transformation mPreviousTransformation = new Transformation(); 209 210 private final CloseGuard guard = CloseGuard.get(); 211 212 private Handler mListenerHandler; 213 private Runnable mOnStart; 214 private Runnable mOnRepeat; 215 private Runnable mOnEnd; 216 217 /** 218 * Creates a new animation with a duration of 0ms, the default interpolator, with 219 * fillBefore set to true and fillAfter set to false 220 */ 221 public Animation() { 222 ensureInterpolator(); 223 } 224 225 /** 226 * Creates a new animation whose parameters come from the specified context and 227 * attributes set. 228 * 229 * @param context the application environment 230 * @param attrs the set of attributes holding the animation parameters 231 */ 232 public Animation(Context context, AttributeSet attrs) { 233 TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animation); 234 235 setDuration((long) a.getInt(com.android.internal.R.styleable.Animation_duration, 0)); 236 setStartOffset((long) a.getInt(com.android.internal.R.styleable.Animation_startOffset, 0)); 237 238 setFillEnabled(a.getBoolean(com.android.internal.R.styleable.Animation_fillEnabled, mFillEnabled)); 239 setFillBefore(a.getBoolean(com.android.internal.R.styleable.Animation_fillBefore, mFillBefore)); 240 setFillAfter(a.getBoolean(com.android.internal.R.styleable.Animation_fillAfter, mFillAfter)); 241 242 setRepeatCount(a.getInt(com.android.internal.R.styleable.Animation_repeatCount, mRepeatCount)); 243 setRepeatMode(a.getInt(com.android.internal.R.styleable.Animation_repeatMode, RESTART)); 244 245 setZAdjustment(a.getInt(com.android.internal.R.styleable.Animation_zAdjustment, ZORDER_NORMAL)); 246 247 setBackgroundColor(a.getInt(com.android.internal.R.styleable.Animation_background, 0)); 248 249 setDetachWallpaper(a.getBoolean(com.android.internal.R.styleable.Animation_detachWallpaper, false)); 250 251 final int resID = a.getResourceId(com.android.internal.R.styleable.Animation_interpolator, 0); 252 253 a.recycle(); 254 255 if (resID > 0) { 256 setInterpolator(context, resID); 257 } 258 259 ensureInterpolator(); 260 } 261 262 @Override 263 protected Animation clone() throws CloneNotSupportedException { 264 final Animation animation = (Animation) super.clone(); 265 animation.mPreviousRegion = new RectF(); 266 animation.mRegion = new RectF(); 267 animation.mTransformation = new Transformation(); 268 animation.mPreviousTransformation = new Transformation(); 269 return animation; 270 } 271 272 /** 273 * Reset the initialization state of this animation. 274 * 275 * @see #initialize(int, int, int, int) 276 */ 277 public void reset() { 278 mPreviousRegion.setEmpty(); 279 mPreviousTransformation.clear(); 280 mInitialized = false; 281 mCycleFlip = false; 282 mRepeated = 0; 283 mMore = true; 284 mOneMoreTime = true; 285 mListenerHandler = null; 286 } 287 288 /** 289 * Cancel the animation. Cancelling an animation invokes the animation 290 * listener, if set, to notify the end of the animation. 291 * 292 * If you cancel an animation manually, you must call {@link #reset()} 293 * before starting the animation again. 294 * 295 * @see #reset() 296 * @see #start() 297 * @see #startNow() 298 */ 299 public void cancel() { 300 if (mStarted && !mEnded) { 301 fireAnimationEnd(); 302 mEnded = true; 303 guard.close(); 304 } 305 // Make sure we move the animation to the end 306 mStartTime = Long.MIN_VALUE; 307 mMore = mOneMoreTime = false; 308 } 309 310 /** 311 * @hide 312 */ 313 public void detach() { 314 if (mStarted && !mEnded) { 315 mEnded = true; 316 guard.close(); 317 fireAnimationEnd(); 318 } 319 } 320 321 /** 322 * Whether or not the animation has been initialized. 323 * 324 * @return Has this animation been initialized. 325 * @see #initialize(int, int, int, int) 326 */ 327 public boolean isInitialized() { 328 return mInitialized; 329 } 330 331 /** 332 * Initialize this animation with the dimensions of the object being 333 * animated as well as the objects parents. (This is to support animation 334 * sizes being specified relative to these dimensions.) 335 * 336 * <p>Objects that interpret Animations should call this method when 337 * the sizes of the object being animated and its parent are known, and 338 * before calling {@link #getTransformation}. 339 * 340 * 341 * @param width Width of the object being animated 342 * @param height Height of the object being animated 343 * @param parentWidth Width of the animated object's parent 344 * @param parentHeight Height of the animated object's parent 345 */ 346 public void initialize(int width, int height, int parentWidth, int parentHeight) { 347 reset(); 348 mInitialized = true; 349 } 350 351 /** 352 * Sets the handler used to invoke listeners. 353 * 354 * @hide 355 */ 356 public void setListenerHandler(Handler handler) { 357 if (mListenerHandler == null) { 358 mOnStart = new Runnable() { 359 public void run() { 360 if (mListener != null) { 361 mListener.onAnimationStart(Animation.this); 362 } 363 } 364 }; 365 mOnRepeat = new Runnable() { 366 public void run() { 367 if (mListener != null) { 368 mListener.onAnimationRepeat(Animation.this); 369 } 370 } 371 }; 372 mOnEnd = new Runnable() { 373 public void run() { 374 if (mListener != null) { 375 mListener.onAnimationEnd(Animation.this); 376 } 377 } 378 }; 379 } 380 mListenerHandler = handler; 381 } 382 383 /** 384 * Sets the acceleration curve for this animation. The interpolator is loaded as 385 * a resource from the specified context. 386 * 387 * @param context The application environment 388 * @param resID The resource identifier of the interpolator to load 389 * @attr ref android.R.styleable#Animation_interpolator 390 */ 391 public void setInterpolator(Context context, int resID) { 392 setInterpolator(AnimationUtils.loadInterpolator(context, resID)); 393 } 394 395 /** 396 * Sets the acceleration curve for this animation. Defaults to a linear 397 * interpolation. 398 * 399 * @param i The interpolator which defines the acceleration curve 400 * @attr ref android.R.styleable#Animation_interpolator 401 */ 402 public void setInterpolator(Interpolator i) { 403 mInterpolator = i; 404 } 405 406 /** 407 * When this animation should start relative to the start time. This is most 408 * useful when composing complex animations using an {@link AnimationSet } 409 * where some of the animations components start at different times. 410 * 411 * @param startOffset When this Animation should start, in milliseconds from 412 * the start time of the root AnimationSet. 413 * @attr ref android.R.styleable#Animation_startOffset 414 */ 415 public void setStartOffset(long startOffset) { 416 mStartOffset = startOffset; 417 } 418 419 /** 420 * How long this animation should last. The duration cannot be negative. 421 * 422 * @param durationMillis Duration in milliseconds 423 * 424 * @throws java.lang.IllegalArgumentException if the duration is < 0 425 * 426 * @attr ref android.R.styleable#Animation_duration 427 */ 428 public void setDuration(long durationMillis) { 429 if (durationMillis < 0) { 430 throw new IllegalArgumentException("Animation duration cannot be negative"); 431 } 432 mDuration = durationMillis; 433 } 434 435 /** 436 * Ensure that the duration that this animation will run is not longer 437 * than <var>durationMillis</var>. In addition to adjusting the duration 438 * itself, this ensures that the repeat count also will not make it run 439 * longer than the given time. 440 * 441 * @param durationMillis The maximum duration the animation is allowed 442 * to run. 443 */ 444 public void restrictDuration(long durationMillis) { 445 // If we start after the duration, then we just won't run. 446 if (mStartOffset > durationMillis) { 447 mStartOffset = durationMillis; 448 mDuration = 0; 449 mRepeatCount = 0; 450 return; 451 } 452 453 long dur = mDuration + mStartOffset; 454 if (dur > durationMillis) { 455 mDuration = durationMillis-mStartOffset; 456 dur = durationMillis; 457 } 458 // If the duration is 0 or less, then we won't run. 459 if (mDuration <= 0) { 460 mDuration = 0; 461 mRepeatCount = 0; 462 return; 463 } 464 // Reduce the number of repeats to keep below the maximum duration. 465 // The comparison between mRepeatCount and duration is to catch 466 // overflows after multiplying them. 467 if (mRepeatCount < 0 || mRepeatCount > durationMillis 468 || (dur*mRepeatCount) > durationMillis) { 469 // Figure out how many times to do the animation. Subtract 1 since 470 // repeat count is the number of times to repeat so 0 runs once. 471 mRepeatCount = (int)(durationMillis/dur) - 1; 472 if (mRepeatCount < 0) { 473 mRepeatCount = 0; 474 } 475 } 476 } 477 478 /** 479 * How much to scale the duration by. 480 * 481 * @param scale The amount to scale the duration. 482 */ 483 public void scaleCurrentDuration(float scale) { 484 mDuration = (long) (mDuration * scale); 485 mStartOffset = (long) (mStartOffset * scale); 486 } 487 488 /** 489 * When this animation should start. When the start time is set to 490 * {@link #START_ON_FIRST_FRAME}, the animation will start the first time 491 * {@link #getTransformation(long, Transformation)} is invoked. The time passed 492 * to this method should be obtained by calling 493 * {@link AnimationUtils#currentAnimationTimeMillis()} instead of 494 * {@link System#currentTimeMillis()}. 495 * 496 * @param startTimeMillis the start time in milliseconds 497 */ 498 public void setStartTime(long startTimeMillis) { 499 mStartTime = startTimeMillis; 500 mStarted = mEnded = false; 501 mCycleFlip = false; 502 mRepeated = 0; 503 mMore = true; 504 } 505 506 /** 507 * Convenience method to start the animation the first time 508 * {@link #getTransformation(long, Transformation)} is invoked. 509 */ 510 public void start() { 511 setStartTime(-1); 512 } 513 514 /** 515 * Convenience method to start the animation at the current time in 516 * milliseconds. 517 */ 518 public void startNow() { 519 setStartTime(AnimationUtils.currentAnimationTimeMillis()); 520 } 521 522 /** 523 * Defines what this animation should do when it reaches the end. This 524 * setting is applied only when the repeat count is either greater than 525 * 0 or {@link #INFINITE}. Defaults to {@link #RESTART}. 526 * 527 * @param repeatMode {@link #RESTART} or {@link #REVERSE} 528 * @attr ref android.R.styleable#Animation_repeatMode 529 */ 530 public void setRepeatMode(int repeatMode) { 531 mRepeatMode = repeatMode; 532 } 533 534 /** 535 * Sets how many times the animation should be repeated. If the repeat 536 * count is 0, the animation is never repeated. If the repeat count is 537 * greater than 0 or {@link #INFINITE}, the repeat mode will be taken 538 * into account. The repeat count is 0 by default. 539 * 540 * @param repeatCount the number of times the animation should be repeated 541 * @attr ref android.R.styleable#Animation_repeatCount 542 */ 543 public void setRepeatCount(int repeatCount) { 544 if (repeatCount < 0) { 545 repeatCount = INFINITE; 546 } 547 mRepeatCount = repeatCount; 548 } 549 550 /** 551 * If fillEnabled is true, this animation will apply the value of fillBefore. 552 * 553 * @return true if the animation will take fillBefore into account 554 * @attr ref android.R.styleable#Animation_fillEnabled 555 */ 556 public boolean isFillEnabled() { 557 return mFillEnabled; 558 } 559 560 /** 561 * If fillEnabled is true, the animation will apply the value of fillBefore. 562 * Otherwise, fillBefore is ignored and the animation 563 * transformation is always applied until the animation ends. 564 * 565 * @param fillEnabled true if the animation should take the value of fillBefore into account 566 * @attr ref android.R.styleable#Animation_fillEnabled 567 * 568 * @see #setFillBefore(boolean) 569 * @see #setFillAfter(boolean) 570 */ 571 public void setFillEnabled(boolean fillEnabled) { 572 mFillEnabled = fillEnabled; 573 } 574 575 /** 576 * If fillBefore is true, this animation will apply its transformation 577 * before the start time of the animation. Defaults to true if 578 * {@link #setFillEnabled(boolean)} is not set to true. 579 * Note that this applies when using an {@link 580 * android.view.animation.AnimationSet AnimationSet} to chain 581 * animations. The transformation is not applied before the AnimationSet 582 * itself starts. 583 * 584 * @param fillBefore true if the animation should apply its transformation before it starts 585 * @attr ref android.R.styleable#Animation_fillBefore 586 * 587 * @see #setFillEnabled(boolean) 588 */ 589 public void setFillBefore(boolean fillBefore) { 590 mFillBefore = fillBefore; 591 } 592 593 /** 594 * If fillAfter is true, the transformation that this animation performed 595 * will persist when it is finished. Defaults to false if not set. 596 * Note that this applies to individual animations and when using an {@link 597 * android.view.animation.AnimationSet AnimationSet} to chain 598 * animations. 599 * 600 * @param fillAfter true if the animation should apply its transformation after it ends 601 * @attr ref android.R.styleable#Animation_fillAfter 602 * 603 * @see #setFillEnabled(boolean) 604 */ 605 public void setFillAfter(boolean fillAfter) { 606 mFillAfter = fillAfter; 607 } 608 609 /** 610 * Set the Z ordering mode to use while running the animation. 611 * 612 * @param zAdjustment The desired mode, one of {@link #ZORDER_NORMAL}, 613 * {@link #ZORDER_TOP}, or {@link #ZORDER_BOTTOM}. 614 * @attr ref android.R.styleable#Animation_zAdjustment 615 */ 616 public void setZAdjustment(int zAdjustment) { 617 mZAdjustment = zAdjustment; 618 } 619 620 /** 621 * Set background behind animation. 622 * 623 * @param bg The background color. If 0, no background. Currently must 624 * be black, with any desired alpha level. 625 */ 626 public void setBackgroundColor(@ColorInt int bg) { 627 mBackgroundColor = bg; 628 } 629 630 /** 631 * The scale factor is set by the call to <code>getTransformation</code>. Overrides of 632 * {@link #getTransformation(long, Transformation, float)} will get this value 633 * directly. Overrides of {@link #applyTransformation(float, Transformation)} can 634 * call this method to get the value. 635 * 636 * @return float The scale factor that should be applied to pre-scaled values in 637 * an Animation such as the pivot points in {@link ScaleAnimation} and {@link RotateAnimation}. 638 */ 639 protected float getScaleFactor() { 640 return mScaleFactor; 641 } 642 643 /** 644 * If detachWallpaper is true, and this is a window animation of a window 645 * that has a wallpaper background, then the window will be detached from 646 * the wallpaper while it runs. That is, the animation will only be applied 647 * to the window, and the wallpaper behind it will remain static. 648 * 649 * @param detachWallpaper true if the wallpaper should be detached from the animation 650 * @attr ref android.R.styleable#Animation_detachWallpaper 651 */ 652 public void setDetachWallpaper(boolean detachWallpaper) { 653 mDetachWallpaper = detachWallpaper; 654 } 655 656 /** 657 * Gets the acceleration curve type for this animation. 658 * 659 * @return the {@link Interpolator} associated to this animation 660 * @attr ref android.R.styleable#Animation_interpolator 661 */ 662 public Interpolator getInterpolator() { 663 return mInterpolator; 664 } 665 666 /** 667 * When this animation should start. If the animation has not startet yet, 668 * this method might return {@link #START_ON_FIRST_FRAME}. 669 * 670 * @return the time in milliseconds when the animation should start or 671 * {@link #START_ON_FIRST_FRAME} 672 */ 673 public long getStartTime() { 674 return mStartTime; 675 } 676 677 /** 678 * How long this animation should last 679 * 680 * @return the duration in milliseconds of the animation 681 * @attr ref android.R.styleable#Animation_duration 682 */ 683 public long getDuration() { 684 return mDuration; 685 } 686 687 /** 688 * When this animation should start, relative to StartTime 689 * 690 * @return the start offset in milliseconds 691 * @attr ref android.R.styleable#Animation_startOffset 692 */ 693 public long getStartOffset() { 694 return mStartOffset; 695 } 696 697 /** 698 * Defines what this animation should do when it reaches the end. 699 * 700 * @return either one of {@link #REVERSE} or {@link #RESTART} 701 * @attr ref android.R.styleable#Animation_repeatMode 702 */ 703 public int getRepeatMode() { 704 return mRepeatMode; 705 } 706 707 /** 708 * Defines how many times the animation should repeat. The default value 709 * is 0. 710 * 711 * @return the number of times the animation should repeat, or {@link #INFINITE} 712 * @attr ref android.R.styleable#Animation_repeatCount 713 */ 714 public int getRepeatCount() { 715 return mRepeatCount; 716 } 717 718 /** 719 * If fillBefore is true, this animation will apply its transformation 720 * before the start time of the animation. If fillBefore is false and 721 * {@link #isFillEnabled() fillEnabled} is true, the transformation will not be applied until 722 * the start time of the animation. 723 * 724 * @return true if the animation applies its transformation before it starts 725 * @attr ref android.R.styleable#Animation_fillBefore 726 */ 727 public boolean getFillBefore() { 728 return mFillBefore; 729 } 730 731 /** 732 * If fillAfter is true, this animation will apply its transformation 733 * after the end time of the animation. 734 * 735 * @return true if the animation applies its transformation after it ends 736 * @attr ref android.R.styleable#Animation_fillAfter 737 */ 738 public boolean getFillAfter() { 739 return mFillAfter; 740 } 741 742 /** 743 * Returns the Z ordering mode to use while running the animation as 744 * previously set by {@link #setZAdjustment}. 745 * 746 * @return Returns one of {@link #ZORDER_NORMAL}, 747 * {@link #ZORDER_TOP}, or {@link #ZORDER_BOTTOM}. 748 * @attr ref android.R.styleable#Animation_zAdjustment 749 */ 750 public int getZAdjustment() { 751 return mZAdjustment; 752 } 753 754 /** 755 * Returns the background color behind the animation. 756 */ 757 @ColorInt 758 public int getBackgroundColor() { 759 return mBackgroundColor; 760 } 761 762 /** 763 * Return value of {@link #setDetachWallpaper(boolean)}. 764 * @attr ref android.R.styleable#Animation_detachWallpaper 765 */ 766 public boolean getDetachWallpaper() { 767 return mDetachWallpaper; 768 } 769 770 /** 771 * <p>Indicates whether or not this animation will affect the transformation 772 * matrix. For instance, a fade animation will not affect the matrix whereas 773 * a scale animation will.</p> 774 * 775 * @return true if this animation will change the transformation matrix 776 */ 777 public boolean willChangeTransformationMatrix() { 778 // assume we will change the matrix 779 return true; 780 } 781 782 /** 783 * <p>Indicates whether or not this animation will affect the bounds of the 784 * animated view. For instance, a fade animation will not affect the bounds 785 * whereas a 200% scale animation will.</p> 786 * 787 * @return true if this animation will change the view's bounds 788 */ 789 public boolean willChangeBounds() { 790 // assume we will change the bounds 791 return true; 792 } 793 794 /** 795 * <p>Binds an animation listener to this animation. The animation listener 796 * is notified of animation events such as the end of the animation or the 797 * repetition of the animation.</p> 798 * 799 * @param listener the animation listener to be notified 800 */ 801 public void setAnimationListener(AnimationListener listener) { 802 mListener = listener; 803 } 804 805 /** 806 * Gurantees that this animation has an interpolator. Will use 807 * a AccelerateDecelerateInterpolator is nothing else was specified. 808 */ 809 protected void ensureInterpolator() { 810 if (mInterpolator == null) { 811 mInterpolator = new AccelerateDecelerateInterpolator(); 812 } 813 } 814 815 /** 816 * Compute a hint at how long the entire animation may last, in milliseconds. 817 * Animations can be written to cause themselves to run for a different 818 * duration than what is computed here, but generally this should be 819 * accurate. 820 */ 821 public long computeDurationHint() { 822 return (getStartOffset() + getDuration()) * (getRepeatCount() + 1); 823 } 824 825 /** 826 * Gets the transformation to apply at a specified point in time. Implementations of this 827 * method should always replace the specified Transformation or document they are doing 828 * otherwise. 829 * 830 * @param currentTime Where we are in the animation. This is wall clock time. 831 * @param outTransformation A transformation object that is provided by the 832 * caller and will be filled in by the animation. 833 * @return True if the animation is still running 834 */ 835 public boolean getTransformation(long currentTime, Transformation outTransformation) { 836 if (mStartTime == -1) { 837 mStartTime = currentTime; 838 } 839 840 final long startOffset = getStartOffset(); 841 final long duration = mDuration; 842 float normalizedTime; 843 if (duration != 0) { 844 normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) / 845 (float) duration; 846 } else { 847 // time is a step-change with a zero duration 848 normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f; 849 } 850 851 final boolean expired = normalizedTime >= 1.0f; 852 mMore = !expired; 853 854 if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f); 855 856 if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) { 857 if (!mStarted) { 858 fireAnimationStart(); 859 mStarted = true; 860 if (USE_CLOSEGUARD) { 861 guard.open("cancel or detach or getTransformation"); 862 } 863 } 864 865 if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f); 866 867 if (mCycleFlip) { 868 normalizedTime = 1.0f - normalizedTime; 869 } 870 871 final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime); 872 applyTransformation(interpolatedTime, outTransformation); 873 } 874 875 if (expired) { 876 if (mRepeatCount == mRepeated) { 877 if (!mEnded) { 878 mEnded = true; 879 guard.close(); 880 fireAnimationEnd(); 881 } 882 } else { 883 if (mRepeatCount > 0) { 884 mRepeated++; 885 } 886 887 if (mRepeatMode == REVERSE) { 888 mCycleFlip = !mCycleFlip; 889 } 890 891 mStartTime = -1; 892 mMore = true; 893 894 fireAnimationRepeat(); 895 } 896 } 897 898 if (!mMore && mOneMoreTime) { 899 mOneMoreTime = false; 900 return true; 901 } 902 903 return mMore; 904 } 905 906 private void fireAnimationStart() { 907 if (mListener != null) { 908 if (mListenerHandler == null) mListener.onAnimationStart(this); 909 else mListenerHandler.postAtFrontOfQueue(mOnStart); 910 } 911 } 912 913 private void fireAnimationRepeat() { 914 if (mListener != null) { 915 if (mListenerHandler == null) mListener.onAnimationRepeat(this); 916 else mListenerHandler.postAtFrontOfQueue(mOnRepeat); 917 } 918 } 919 920 private void fireAnimationEnd() { 921 if (mListener != null) { 922 if (mListenerHandler == null) mListener.onAnimationEnd(this); 923 else mListenerHandler.postAtFrontOfQueue(mOnEnd); 924 } 925 } 926 927 /** 928 * Gets the transformation to apply at a specified point in time. Implementations of this 929 * method should always replace the specified Transformation or document they are doing 930 * otherwise. 931 * 932 * @param currentTime Where we are in the animation. This is wall clock time. 933 * @param outTransformation A transformation object that is provided by the 934 * caller and will be filled in by the animation. 935 * @param scale Scaling factor to apply to any inputs to the transform operation, such 936 * pivot points being rotated or scaled around. 937 * @return True if the animation is still running 938 */ 939 public boolean getTransformation(long currentTime, Transformation outTransformation, 940 float scale) { 941 mScaleFactor = scale; 942 return getTransformation(currentTime, outTransformation); 943 } 944 945 /** 946 * <p>Indicates whether this animation has started or not.</p> 947 * 948 * @return true if the animation has started, false otherwise 949 */ 950 public boolean hasStarted() { 951 return mStarted; 952 } 953 954 /** 955 * <p>Indicates whether this animation has ended or not.</p> 956 * 957 * @return true if the animation has ended, false otherwise 958 */ 959 public boolean hasEnded() { 960 return mEnded; 961 } 962 963 /** 964 * Helper for getTransformation. Subclasses should implement this to apply 965 * their transforms given an interpolation value. Implementations of this 966 * method should always replace the specified Transformation or document 967 * they are doing otherwise. 968 * 969 * @param interpolatedTime The value of the normalized time (0.0 to 1.0) 970 * after it has been run through the interpolation function. 971 * @param t The Transformation object to fill in with the current 972 * transforms. 973 */ 974 protected void applyTransformation(float interpolatedTime, Transformation t) { 975 } 976 977 /** 978 * Convert the information in the description of a size to an actual 979 * dimension 980 * 981 * @param type One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or 982 * Animation.RELATIVE_TO_PARENT. 983 * @param value The dimension associated with the type parameter 984 * @param size The size of the object being animated 985 * @param parentSize The size of the parent of the object being animated 986 * @return The dimension to use for the animation 987 */ 988 protected float resolveSize(int type, float value, int size, int parentSize) { 989 switch (type) { 990 case ABSOLUTE: 991 return value; 992 case RELATIVE_TO_SELF: 993 return size * value; 994 case RELATIVE_TO_PARENT: 995 return parentSize * value; 996 default: 997 return value; 998 } 999 } 1000 1001 /** 1002 * @param left 1003 * @param top 1004 * @param right 1005 * @param bottom 1006 * @param invalidate 1007 * @param transformation 1008 * 1009 * @hide 1010 */ 1011 public void getInvalidateRegion(int left, int top, int right, int bottom, 1012 RectF invalidate, Transformation transformation) { 1013 1014 final RectF tempRegion = mRegion; 1015 final RectF previousRegion = mPreviousRegion; 1016 1017 invalidate.set(left, top, right, bottom); 1018 transformation.getMatrix().mapRect(invalidate); 1019 // Enlarge the invalidate region to account for rounding errors 1020 invalidate.inset(-1.0f, -1.0f); 1021 tempRegion.set(invalidate); 1022 invalidate.union(previousRegion); 1023 1024 previousRegion.set(tempRegion); 1025 1026 final Transformation tempTransformation = mTransformation; 1027 final Transformation previousTransformation = mPreviousTransformation; 1028 1029 tempTransformation.set(transformation); 1030 transformation.set(previousTransformation); 1031 previousTransformation.set(tempTransformation); 1032 } 1033 1034 /** 1035 * @param left 1036 * @param top 1037 * @param right 1038 * @param bottom 1039 * 1040 * @hide 1041 */ 1042 public void initializeInvalidateRegion(int left, int top, int right, int bottom) { 1043 final RectF region = mPreviousRegion; 1044 region.set(left, top, right, bottom); 1045 // Enlarge the invalidate region to account for rounding errors 1046 region.inset(-1.0f, -1.0f); 1047 if (mFillBefore) { 1048 final Transformation previousTransformation = mPreviousTransformation; 1049 applyTransformation(mInterpolator.getInterpolation(0.0f), previousTransformation); 1050 } 1051 } 1052 1053 protected void finalize() throws Throwable { 1054 try { 1055 if (guard != null) { 1056 guard.warnIfOpen(); 1057 } 1058 } finally { 1059 super.finalize(); 1060 } 1061 } 1062 1063 /** 1064 * Return true if this animation changes the view's alpha property. 1065 * 1066 * @hide 1067 */ 1068 public boolean hasAlpha() { 1069 return false; 1070 } 1071 1072 /** 1073 * Utility class to parse a string description of a size. 1074 */ 1075 protected static class Description { 1076 /** 1077 * One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or 1078 * Animation.RELATIVE_TO_PARENT. 1079 */ 1080 public int type; 1081 1082 /** 1083 * The absolute or relative dimension for this Description. 1084 */ 1085 public float value; 1086 1087 /** 1088 * Size descriptions can appear inthree forms: 1089 * <ol> 1090 * <li>An absolute size. This is represented by a number.</li> 1091 * <li>A size relative to the size of the object being animated. This 1092 * is represented by a number followed by "%".</li> * 1093 * <li>A size relative to the size of the parent of object being 1094 * animated. This is represented by a number followed by "%p".</li> 1095 * </ol> 1096 * @param value The typed value to parse 1097 * @return The parsed version of the description 1098 */ 1099 static Description parseValue(TypedValue value) { 1100 Description d = new Description(); 1101 if (value == null) { 1102 d.type = ABSOLUTE; 1103 d.value = 0; 1104 } else { 1105 if (value.type == TypedValue.TYPE_FRACTION) { 1106 d.type = (value.data & TypedValue.COMPLEX_UNIT_MASK) == 1107 TypedValue.COMPLEX_UNIT_FRACTION_PARENT ? 1108 RELATIVE_TO_PARENT : RELATIVE_TO_SELF; 1109 d.value = TypedValue.complexToFloat(value.data); 1110 return d; 1111 } else if (value.type == TypedValue.TYPE_FLOAT) { 1112 d.type = ABSOLUTE; 1113 d.value = value.getFloat(); 1114 return d; 1115 } else if (value.type >= TypedValue.TYPE_FIRST_INT && 1116 value.type <= TypedValue.TYPE_LAST_INT) { 1117 d.type = ABSOLUTE; 1118 d.value = value.data; 1119 return d; 1120 } 1121 } 1122 1123 d.type = ABSOLUTE; 1124 d.value = 0.0f; 1125 1126 return d; 1127 } 1128 } 1129 1130 /** 1131 * <p>An animation listener receives notifications from an animation. 1132 * Notifications indicate animation related events, such as the end or the 1133 * repetition of the animation.</p> 1134 */ 1135 public static interface AnimationListener { 1136 /** 1137 * <p>Notifies the start of the animation.</p> 1138 * 1139 * @param animation The started animation. 1140 */ 1141 void onAnimationStart(Animation animation); 1142 1143 /** 1144 * <p>Notifies the end of the animation. This callback is not invoked 1145 * for animations with repeat count set to INFINITE.</p> 1146 * 1147 * @param animation The animation which reached its end. 1148 */ 1149 void onAnimationEnd(Animation animation); 1150 1151 /** 1152 * <p>Notifies the repetition of the animation.</p> 1153 * 1154 * @param animation The animation which was repeated. 1155 */ 1156 void onAnimationRepeat(Animation animation); 1157 } 1158} 1159