1 /* 2 * Copyright (C) 2010 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.animation; 18 19 import android.annotation.Nullable; 20 import android.content.pm.ActivityInfo.Config; 21 import android.content.res.ConstantState; 22 23 import java.util.ArrayList; 24 25 /** 26 * This is the superclass for classes which provide basic support for animations which can be 27 * started, ended, and have <code>AnimatorListeners</code> added to them. 28 */ 29 public abstract class Animator implements Cloneable { 30 31 /** 32 * The value used to indicate infinite duration (e.g. when Animators repeat infinitely). 33 */ 34 public static final long DURATION_INFINITE = -1; 35 /** 36 * The set of listeners to be sent events through the life of an animation. 37 */ 38 ArrayList<AnimatorListener> mListeners = null; 39 40 /** 41 * The set of listeners to be sent pause/resume events through the life 42 * of an animation. 43 */ 44 ArrayList<AnimatorPauseListener> mPauseListeners = null; 45 46 /** 47 * Whether this animator is currently in a paused state. 48 */ 49 boolean mPaused = false; 50 51 /** 52 * A set of flags which identify the type of configuration changes that can affect this 53 * Animator. Used by the Animator cache. 54 */ 55 @Config int mChangingConfigurations = 0; 56 57 /** 58 * If this animator is inflated from a constant state, keep a reference to it so that 59 * ConstantState will not be garbage collected until this animator is collected 60 */ 61 private AnimatorConstantState mConstantState; 62 63 /** 64 * Starts this animation. If the animation has a nonzero startDelay, the animation will start 65 * running after that delay elapses. A non-delayed animation will have its initial 66 * value(s) set immediately, followed by calls to 67 * {@link AnimatorListener#onAnimationStart(Animator)} for any listeners of this animator. 68 * 69 * <p>The animation started by calling this method will be run on the thread that called 70 * this method. This thread should have a Looper on it (a runtime exception will be thrown if 71 * this is not the case). Also, if the animation will animate 72 * properties of objects in the view hierarchy, then the calling thread should be the UI 73 * thread for that view hierarchy.</p> 74 * 75 */ start()76 public void start() { 77 } 78 79 /** 80 * Cancels the animation. Unlike {@link #end()}, <code>cancel()</code> causes the animation to 81 * stop in its tracks, sending an 82 * {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to 83 * its listeners, followed by an 84 * {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message. 85 * 86 * <p>This method must be called on the thread that is running the animation.</p> 87 */ cancel()88 public void cancel() { 89 } 90 91 /** 92 * Ends the animation. This causes the animation to assign the end value of the property being 93 * animated, then calling the 94 * {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on 95 * its listeners. 96 * 97 * <p>This method must be called on the thread that is running the animation.</p> 98 */ end()99 public void end() { 100 } 101 102 /** 103 * Pauses a running animation. This method should only be called on the same thread on 104 * which the animation was started. If the animation has not yet been {@link 105 * #isStarted() started} or has since ended, then the call is ignored. Paused 106 * animations can be resumed by calling {@link #resume()}. 107 * 108 * @see #resume() 109 * @see #isPaused() 110 * @see AnimatorPauseListener 111 */ pause()112 public void pause() { 113 if (isStarted() && !mPaused) { 114 mPaused = true; 115 if (mPauseListeners != null) { 116 ArrayList<AnimatorPauseListener> tmpListeners = 117 (ArrayList<AnimatorPauseListener>) mPauseListeners.clone(); 118 int numListeners = tmpListeners.size(); 119 for (int i = 0; i < numListeners; ++i) { 120 tmpListeners.get(i).onAnimationPause(this); 121 } 122 } 123 } 124 } 125 126 /** 127 * Resumes a paused animation, causing the animator to pick up where it left off 128 * when it was paused. This method should only be called on the same thread on 129 * which the animation was started. Calls to resume() on an animator that is 130 * not currently paused will be ignored. 131 * 132 * @see #pause() 133 * @see #isPaused() 134 * @see AnimatorPauseListener 135 */ resume()136 public void resume() { 137 if (mPaused) { 138 mPaused = false; 139 if (mPauseListeners != null) { 140 ArrayList<AnimatorPauseListener> tmpListeners = 141 (ArrayList<AnimatorPauseListener>) mPauseListeners.clone(); 142 int numListeners = tmpListeners.size(); 143 for (int i = 0; i < numListeners; ++i) { 144 tmpListeners.get(i).onAnimationResume(this); 145 } 146 } 147 } 148 } 149 150 /** 151 * Returns whether this animator is currently in a paused state. 152 * 153 * @return True if the animator is currently paused, false otherwise. 154 * 155 * @see #pause() 156 * @see #resume() 157 */ isPaused()158 public boolean isPaused() { 159 return mPaused; 160 } 161 162 /** 163 * The amount of time, in milliseconds, to delay processing the animation 164 * after {@link #start()} is called. 165 * 166 * @return the number of milliseconds to delay running the animation 167 */ getStartDelay()168 public abstract long getStartDelay(); 169 170 /** 171 * The amount of time, in milliseconds, to delay processing the animation 172 * after {@link #start()} is called. 173 174 * @param startDelay The amount of the delay, in milliseconds 175 */ setStartDelay(long startDelay)176 public abstract void setStartDelay(long startDelay); 177 178 /** 179 * Sets the duration of the animation. 180 * 181 * @param duration The length of the animation, in milliseconds. 182 */ setDuration(long duration)183 public abstract Animator setDuration(long duration); 184 185 /** 186 * Gets the duration of the animation. 187 * 188 * @return The length of the animation, in milliseconds. 189 */ getDuration()190 public abstract long getDuration(); 191 192 /** 193 * Gets the total duration of the animation, accounting for animation sequences, start delay, 194 * and repeating. Return {@link #DURATION_INFINITE} if the duration is infinite. 195 * 196 * @return Total time an animation takes to finish, starting from the time {@link #start()} 197 * is called. {@link #DURATION_INFINITE} will be returned if the animation or any 198 * child animation repeats infinite times. 199 */ getTotalDuration()200 public long getTotalDuration() { 201 long duration = getDuration(); 202 if (duration == DURATION_INFINITE) { 203 return DURATION_INFINITE; 204 } else { 205 return getStartDelay() + duration; 206 } 207 } 208 209 /** 210 * The time interpolator used in calculating the elapsed fraction of the 211 * animation. The interpolator determines whether the animation runs with 212 * linear or non-linear motion, such as acceleration and deceleration. The 213 * default value is {@link android.view.animation.AccelerateDecelerateInterpolator}. 214 * 215 * @param value the interpolator to be used by this animation 216 */ setInterpolator(TimeInterpolator value)217 public abstract void setInterpolator(TimeInterpolator value); 218 219 /** 220 * Returns the timing interpolator that this animation uses. 221 * 222 * @return The timing interpolator for this animation. 223 */ getInterpolator()224 public TimeInterpolator getInterpolator() { 225 return null; 226 } 227 228 /** 229 * Returns whether this Animator is currently running (having been started and gone past any 230 * initial startDelay period and not yet ended). 231 * 232 * @return Whether the Animator is running. 233 */ isRunning()234 public abstract boolean isRunning(); 235 236 /** 237 * Returns whether this Animator has been started and not yet ended. For reusable 238 * Animators (which most Animators are, apart from the one-shot animator produced by 239 * {@link android.view.ViewAnimationUtils#createCircularReveal( 240 * android.view.View, int, int, float, float) createCircularReveal()}), 241 * this state is a superset of {@link #isRunning()}, because an Animator with a 242 * nonzero {@link #getStartDelay() startDelay} will return true for {@link #isStarted()} during 243 * the delay phase, whereas {@link #isRunning()} will return true only after the delay phase 244 * is complete. Non-reusable animators will always return true after they have been 245 * started, because they cannot return to a non-started state. 246 * 247 * @return Whether the Animator has been started and not yet ended. 248 */ isStarted()249 public boolean isStarted() { 250 // Default method returns value for isRunning(). Subclasses should override to return a 251 // real value. 252 return isRunning(); 253 } 254 255 /** 256 * Adds a listener to the set of listeners that are sent events through the life of an 257 * animation, such as start, repeat, and end. 258 * 259 * @param listener the listener to be added to the current set of listeners for this animation. 260 */ addListener(AnimatorListener listener)261 public void addListener(AnimatorListener listener) { 262 if (mListeners == null) { 263 mListeners = new ArrayList<AnimatorListener>(); 264 } 265 mListeners.add(listener); 266 } 267 268 /** 269 * Removes a listener from the set listening to this animation. 270 * 271 * @param listener the listener to be removed from the current set of listeners for this 272 * animation. 273 */ removeListener(AnimatorListener listener)274 public void removeListener(AnimatorListener listener) { 275 if (mListeners == null) { 276 return; 277 } 278 mListeners.remove(listener); 279 if (mListeners.size() == 0) { 280 mListeners = null; 281 } 282 } 283 284 /** 285 * Gets the set of {@link android.animation.Animator.AnimatorListener} objects that are currently 286 * listening for events on this <code>Animator</code> object. 287 * 288 * @return ArrayList<AnimatorListener> The set of listeners. 289 */ getListeners()290 public ArrayList<AnimatorListener> getListeners() { 291 return mListeners; 292 } 293 294 /** 295 * Adds a pause listener to this animator. 296 * 297 * @param listener the listener to be added to the current set of pause listeners 298 * for this animation. 299 */ addPauseListener(AnimatorPauseListener listener)300 public void addPauseListener(AnimatorPauseListener listener) { 301 if (mPauseListeners == null) { 302 mPauseListeners = new ArrayList<AnimatorPauseListener>(); 303 } 304 mPauseListeners.add(listener); 305 } 306 307 /** 308 * Removes a pause listener from the set listening to this animation. 309 * 310 * @param listener the listener to be removed from the current set of pause 311 * listeners for this animation. 312 */ removePauseListener(AnimatorPauseListener listener)313 public void removePauseListener(AnimatorPauseListener listener) { 314 if (mPauseListeners == null) { 315 return; 316 } 317 mPauseListeners.remove(listener); 318 if (mPauseListeners.size() == 0) { 319 mPauseListeners = null; 320 } 321 } 322 323 /** 324 * Removes all {@link #addListener(android.animation.Animator.AnimatorListener) listeners} 325 * and {@link #addPauseListener(android.animation.Animator.AnimatorPauseListener) 326 * pauseListeners} from this object. 327 */ removeAllListeners()328 public void removeAllListeners() { 329 if (mListeners != null) { 330 mListeners.clear(); 331 mListeners = null; 332 } 333 if (mPauseListeners != null) { 334 mPauseListeners.clear(); 335 mPauseListeners = null; 336 } 337 } 338 339 /** 340 * Return a mask of the configuration parameters for which this animator may change, requiring 341 * that it should be re-created from Resources. The default implementation returns whatever 342 * value was provided through setChangingConfigurations(int) or 0 by default. 343 * 344 * @return Returns a mask of the changing configuration parameters, as defined by 345 * {@link android.content.pm.ActivityInfo}. 346 * @see android.content.pm.ActivityInfo 347 * @hide 348 */ getChangingConfigurations()349 public @Config int getChangingConfigurations() { 350 return mChangingConfigurations; 351 } 352 353 /** 354 * Set a mask of the configuration parameters for which this animator may change, requiring 355 * that it be re-created from resource. 356 * 357 * @param configs A mask of the changing configuration parameters, as 358 * defined by {@link android.content.pm.ActivityInfo}. 359 * 360 * @see android.content.pm.ActivityInfo 361 * @hide 362 */ setChangingConfigurations(@onfig int configs)363 public void setChangingConfigurations(@Config int configs) { 364 mChangingConfigurations = configs; 365 } 366 367 /** 368 * Sets the changing configurations value to the union of the current changing configurations 369 * and the provided configs. 370 * This method is called while loading the animator. 371 * @hide 372 */ appendChangingConfigurations(@onfig int configs)373 public void appendChangingConfigurations(@Config int configs) { 374 mChangingConfigurations |= configs; 375 } 376 377 /** 378 * Return a {@link android.content.res.ConstantState} instance that holds the shared state of 379 * this Animator. 380 * <p> 381 * This constant state is used to create new instances of this animator when needed, instead 382 * of re-loading it from resources. Default implementation creates a new 383 * {@link AnimatorConstantState}. You can override this method to provide your custom logic or 384 * return null if you don't want this animator to be cached. 385 * 386 * @return The ConfigurationBoundResourceCache.BaseConstantState associated to this Animator. 387 * @see android.content.res.ConstantState 388 * @see #clone() 389 * @hide 390 */ createConstantState()391 public ConstantState<Animator> createConstantState() { 392 return new AnimatorConstantState(this); 393 } 394 395 @Override clone()396 public Animator clone() { 397 try { 398 final Animator anim = (Animator) super.clone(); 399 if (mListeners != null) { 400 anim.mListeners = new ArrayList<AnimatorListener>(mListeners); 401 } 402 if (mPauseListeners != null) { 403 anim.mPauseListeners = new ArrayList<AnimatorPauseListener>(mPauseListeners); 404 } 405 return anim; 406 } catch (CloneNotSupportedException e) { 407 throw new AssertionError(); 408 } 409 } 410 411 /** 412 * This method tells the object to use appropriate information to extract 413 * starting values for the animation. For example, a AnimatorSet object will pass 414 * this call to its child objects to tell them to set up the values. A 415 * ObjectAnimator object will use the information it has about its target object 416 * and PropertyValuesHolder objects to get the start values for its properties. 417 * A ValueAnimator object will ignore the request since it does not have enough 418 * information (such as a target object) to gather these values. 419 */ setupStartValues()420 public void setupStartValues() { 421 } 422 423 /** 424 * This method tells the object to use appropriate information to extract 425 * ending values for the animation. For example, a AnimatorSet object will pass 426 * this call to its child objects to tell them to set up the values. A 427 * ObjectAnimator object will use the information it has about its target object 428 * and PropertyValuesHolder objects to get the start values for its properties. 429 * A ValueAnimator object will ignore the request since it does not have enough 430 * information (such as a target object) to gather these values. 431 */ setupEndValues()432 public void setupEndValues() { 433 } 434 435 /** 436 * Sets the target object whose property will be animated by this animation. Not all subclasses 437 * operate on target objects (for example, {@link ValueAnimator}, but this method 438 * is on the superclass for the convenience of dealing generically with those subclasses 439 * that do handle targets. 440 * <p> 441 * <strong>Note:</strong> The target is stored as a weak reference internally to avoid leaking 442 * resources by having animators directly reference old targets. Therefore, you should 443 * ensure that animator targets always have a hard reference elsewhere. 444 * 445 * @param target The object being animated 446 */ setTarget(@ullable Object target)447 public void setTarget(@Nullable Object target) { 448 } 449 450 // Hide reverse() and canReverse() for now since reverse() only work for simple 451 // cases, like we don't support sequential, neither startDelay. 452 // TODO: make reverse() works for all the Animators. 453 /** 454 * @hide 455 */ canReverse()456 public boolean canReverse() { 457 return false; 458 } 459 460 /** 461 * @hide 462 */ reverse()463 public void reverse() { 464 throw new IllegalStateException("Reverse is not supported"); 465 } 466 467 /** 468 * <p>An animation listener receives notifications from an animation. 469 * Notifications indicate animation related events, such as the end or the 470 * repetition of the animation.</p> 471 */ 472 public static interface AnimatorListener { 473 /** 474 * <p>Notifies the start of the animation.</p> 475 * 476 * @param animation The started animation. 477 */ onAnimationStart(Animator animation)478 void onAnimationStart(Animator animation); 479 480 /** 481 * <p>Notifies the end of the animation. This callback is not invoked 482 * for animations with repeat count set to INFINITE.</p> 483 * 484 * @param animation The animation which reached its end. 485 */ onAnimationEnd(Animator animation)486 void onAnimationEnd(Animator animation); 487 488 /** 489 * <p>Notifies the cancellation of the animation. This callback is not invoked 490 * for animations with repeat count set to INFINITE.</p> 491 * 492 * @param animation The animation which was canceled. 493 */ onAnimationCancel(Animator animation)494 void onAnimationCancel(Animator animation); 495 496 /** 497 * <p>Notifies the repetition of the animation.</p> 498 * 499 * @param animation The animation which was repeated. 500 */ onAnimationRepeat(Animator animation)501 void onAnimationRepeat(Animator animation); 502 } 503 504 /** 505 * A pause listener receives notifications from an animation when the 506 * animation is {@link #pause() paused} or {@link #resume() resumed}. 507 * 508 * @see #addPauseListener(AnimatorPauseListener) 509 */ 510 public static interface AnimatorPauseListener { 511 /** 512 * <p>Notifies that the animation was paused.</p> 513 * 514 * @param animation The animaton being paused. 515 * @see #pause() 516 */ onAnimationPause(Animator animation)517 void onAnimationPause(Animator animation); 518 519 /** 520 * <p>Notifies that the animation was resumed, after being 521 * previously paused.</p> 522 * 523 * @param animation The animation being resumed. 524 * @see #resume() 525 */ onAnimationResume(Animator animation)526 void onAnimationResume(Animator animation); 527 } 528 529 /** 530 * <p>Whether or not the Animator is allowed to run asynchronously off of 531 * the UI thread. This is a hint that informs the Animator that it is 532 * OK to run the animation off-thread, however the Animator may decide 533 * that it must run the animation on the UI thread anyway. 534 * 535 * <p>Regardless of whether or not the animation runs asynchronously, all 536 * listener callbacks will be called on the UI thread.</p> 537 * 538 * <p>To be able to use this hint the following must be true:</p> 539 * <ol> 540 * <li>The animator is immutable while {@link #isStarted()} is true. Requests 541 * to change duration, delay, etc... may be ignored.</li> 542 * <li>Lifecycle callback events may be asynchronous. Events such as 543 * {@link Animator.AnimatorListener#onAnimationEnd(Animator)} or 544 * {@link Animator.AnimatorListener#onAnimationRepeat(Animator)} may end up delayed 545 * as they must be posted back to the UI thread, and any actions performed 546 * by those callbacks (such as starting new animations) will not happen 547 * in the same frame.</li> 548 * <li>State change requests ({@link #cancel()}, {@link #end()}, {@link #reverse()}, etc...) 549 * may be asynchronous. It is guaranteed that all state changes that are 550 * performed on the UI thread in the same frame will be applied as a single 551 * atomic update, however that frame may be the current frame, 552 * the next frame, or some future frame. This will also impact the observed 553 * state of the Animator. For example, {@link #isStarted()} may still return true 554 * after a call to {@link #end()}. Using the lifecycle callbacks is preferred over 555 * queries to {@link #isStarted()}, {@link #isRunning()}, and {@link #isPaused()} 556 * for this reason.</li> 557 * </ol> 558 * @hide 559 */ setAllowRunningAsynchronously(boolean mayRunAsync)560 public void setAllowRunningAsynchronously(boolean mayRunAsync) { 561 // It is up to subclasses to support this, if they can. 562 } 563 564 /** 565 * Creates a {@link ConstantState} which holds changing configurations information associated 566 * with the given Animator. 567 * <p> 568 * When {@link #newInstance()} is called, default implementation clones the Animator. 569 */ 570 private static class AnimatorConstantState extends ConstantState<Animator> { 571 572 final Animator mAnimator; 573 @Config int mChangingConf; 574 AnimatorConstantState(Animator animator)575 public AnimatorConstantState(Animator animator) { 576 mAnimator = animator; 577 // ensure a reference back to here so that constante state is not gc'ed. 578 mAnimator.mConstantState = this; 579 mChangingConf = mAnimator.getChangingConfigurations(); 580 } 581 582 @Override getChangingConfigurations()583 public @Config int getChangingConfigurations() { 584 return mChangingConf; 585 } 586 587 @Override newInstance()588 public Animator newInstance() { 589 final Animator clone = mAnimator.clone(); 590 clone.mConstantState = this; 591 return clone; 592 } 593 } 594 } 595