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