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