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