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