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 java.util.ArrayList; 20 21 /** 22 * This is the superclass for classes which provide basic support for animations which can be 23 * started, ended, and have <code>AnimatorListeners</code> added to them. 24 */ 25 public abstract class Animator implements Cloneable { 26 27 /** 28 * The set of listeners to be sent events through the life of an animation. 29 */ 30 ArrayList<AnimatorListener> mListeners = null; 31 32 /** 33 * The set of listeners to be sent pause/resume events through the life 34 * of an animation. 35 */ 36 ArrayList<AnimatorPauseListener> mPauseListeners = null; 37 38 /** 39 * Whether this animator is currently in a paused state. 40 */ 41 boolean mPaused = false; 42 43 /** 44 * Starts this animation. If the animation has a nonzero startDelay, the animation will start 45 * running after that delay elapses. A non-delayed animation will have its initial 46 * value(s) set immediately, followed by calls to 47 * {@link AnimatorListener#onAnimationStart(Animator)} for any listeners of this animator. 48 * 49 * <p>The animation started by calling this method will be run on the thread that called 50 * this method. This thread should have a Looper on it (a runtime exception will be thrown if 51 * this is not the case). Also, if the animation will animate 52 * properties of objects in the view hierarchy, then the calling thread should be the UI 53 * thread for that view hierarchy.</p> 54 * 55 */ 56 public void start() { 57 } 58 59 /** 60 * Cancels the animation. Unlike {@link #end()}, <code>cancel()</code> causes the animation to 61 * stop in its tracks, sending an 62 * {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to 63 * its listeners, followed by an 64 * {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message. 65 * 66 * <p>This method must be called on the thread that is running the animation.</p> 67 */ 68 public void cancel() { 69 } 70 71 /** 72 * Ends the animation. This causes the animation to assign the end value of the property being 73 * animated, then calling the 74 * {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on 75 * its listeners. 76 * 77 * <p>This method must be called on the thread that is running the animation.</p> 78 */ 79 public void end() { 80 } 81 82 /** 83 * Pauses a running animation. This method should only be called on the same thread on 84 * which the animation was started. If the animation has not yet been {@link 85 * #isStarted() started} or has since ended, then the call is ignored. Paused 86 * animations can be resumed by calling {@link #resume()}. 87 * 88 * @see #resume() 89 * @see #isPaused() 90 * @see AnimatorPauseListener 91 */ 92 public void pause() { 93 if (isStarted() && !mPaused) { 94 mPaused = true; 95 if (mPauseListeners != null) { 96 ArrayList<AnimatorPauseListener> tmpListeners = 97 (ArrayList<AnimatorPauseListener>) mPauseListeners.clone(); 98 int numListeners = tmpListeners.size(); 99 for (int i = 0; i < numListeners; ++i) { 100 tmpListeners.get(i).onAnimationPause(this); 101 } 102 } 103 } 104 } 105 106 /** 107 * Resumes a paused animation, causing the animator to pick up where it left off 108 * when it was paused. This method should only be called on the same thread on 109 * which the animation was started. Calls to resume() on an animator that is 110 * not currently paused will be ignored. 111 * 112 * @see #pause() 113 * @see #isPaused() 114 * @see AnimatorPauseListener 115 */ 116 public void resume() { 117 if (mPaused) { 118 mPaused = false; 119 if (mPauseListeners != null) { 120 ArrayList<AnimatorPauseListener> tmpListeners = 121 (ArrayList<AnimatorPauseListener>) mPauseListeners.clone(); 122 int numListeners = tmpListeners.size(); 123 for (int i = 0; i < numListeners; ++i) { 124 tmpListeners.get(i).onAnimationResume(this); 125 } 126 } 127 } 128 } 129 130 /** 131 * Returns whether this animator is currently in a paused state. 132 * 133 * @return True if the animator is currently paused, false otherwise. 134 * 135 * @see #pause() 136 * @see #resume() 137 */ 138 public boolean isPaused() { 139 return mPaused; 140 } 141 142 /** 143 * The amount of time, in milliseconds, to delay processing the animation 144 * after {@link #start()} is called. 145 * 146 * @return the number of milliseconds to delay running the animation 147 */ 148 public abstract long getStartDelay(); 149 150 /** 151 * The amount of time, in milliseconds, to delay processing the animation 152 * after {@link #start()} is called. 153 154 * @param startDelay The amount of the delay, in milliseconds 155 */ 156 public abstract void setStartDelay(long startDelay); 157 158 /** 159 * Sets the duration of the animation. 160 * 161 * @param duration The length of the animation, in milliseconds. 162 */ 163 public abstract Animator setDuration(long duration); 164 165 /** 166 * Gets the duration of the animation. 167 * 168 * @return The length of the animation, in milliseconds. 169 */ 170 public abstract long getDuration(); 171 172 /** 173 * The time interpolator used in calculating the elapsed fraction of the 174 * animation. The interpolator determines whether the animation runs with 175 * linear or non-linear motion, such as acceleration and deceleration. The 176 * default value is {@link android.view.animation.AccelerateDecelerateInterpolator}. 177 * 178 * @param value the interpolator to be used by this animation 179 */ 180 public abstract void setInterpolator(TimeInterpolator value); 181 182 /** 183 * Returns the timing interpolator that this animation uses. 184 * 185 * @return The timing interpolator for this animation. 186 */ 187 public TimeInterpolator getInterpolator() { 188 return null; 189 } 190 191 /** 192 * Returns whether this Animator is currently running (having been started and gone past any 193 * initial startDelay period and not yet ended). 194 * 195 * @return Whether the Animator is running. 196 */ 197 public abstract boolean isRunning(); 198 199 /** 200 * Returns whether this Animator has been started and not yet ended. This state is a superset 201 * of the state of {@link #isRunning()}, because an Animator with a nonzero 202 * {@link #getStartDelay() startDelay} will return true for {@link #isStarted()} during the 203 * delay phase, whereas {@link #isRunning()} will return true only after the delay phase 204 * is complete. 205 * 206 * @return Whether the Animator has been started and not yet ended. 207 */ 208 public boolean isStarted() { 209 // Default method returns value for isRunning(). Subclasses should override to return a 210 // real value. 211 return isRunning(); 212 } 213 214 /** 215 * Adds a listener to the set of listeners that are sent events through the life of an 216 * animation, such as start, repeat, and end. 217 * 218 * @param listener the listener to be added to the current set of listeners for this animation. 219 */ 220 public void addListener(AnimatorListener listener) { 221 if (mListeners == null) { 222 mListeners = new ArrayList<AnimatorListener>(); 223 } 224 mListeners.add(listener); 225 } 226 227 /** 228 * Removes a listener from the set listening to this animation. 229 * 230 * @param listener the listener to be removed from the current set of listeners for this 231 * animation. 232 */ 233 public void removeListener(AnimatorListener listener) { 234 if (mListeners == null) { 235 return; 236 } 237 mListeners.remove(listener); 238 if (mListeners.size() == 0) { 239 mListeners = null; 240 } 241 } 242 243 /** 244 * Gets the set of {@link android.animation.Animator.AnimatorListener} objects that are currently 245 * listening for events on this <code>Animator</code> object. 246 * 247 * @return ArrayList<AnimatorListener> The set of listeners. 248 */ 249 public ArrayList<AnimatorListener> getListeners() { 250 return mListeners; 251 } 252 253 /** 254 * Adds a pause listener to this animator. 255 * 256 * @param listener the listener to be added to the current set of pause listeners 257 * for this animation. 258 */ 259 public void addPauseListener(AnimatorPauseListener listener) { 260 if (mPauseListeners == null) { 261 mPauseListeners = new ArrayList<AnimatorPauseListener>(); 262 } 263 mPauseListeners.add(listener); 264 } 265 266 /** 267 * Removes a pause listener from the set listening to this animation. 268 * 269 * @param listener the listener to be removed from the current set of pause 270 * listeners for this animation. 271 */ 272 public void removePauseListener(AnimatorPauseListener listener) { 273 if (mPauseListeners == null) { 274 return; 275 } 276 mPauseListeners.remove(listener); 277 if (mPauseListeners.size() == 0) { 278 mPauseListeners = null; 279 } 280 } 281 282 /** 283 * Removes all {@link #addListener(android.animation.Animator.AnimatorListener) listeners} 284 * and {@link #addPauseListener(android.animation.Animator.AnimatorPauseListener) 285 * pauseListeners} from this object. 286 */ 287 public void removeAllListeners() { 288 if (mListeners != null) { 289 mListeners.clear(); 290 mListeners = null; 291 } 292 if (mPauseListeners != null) { 293 mPauseListeners.clear(); 294 mPauseListeners = null; 295 } 296 } 297 298 @Override 299 public Animator clone() { 300 try { 301 final Animator anim = (Animator) super.clone(); 302 if (mListeners != null) { 303 ArrayList<AnimatorListener> oldListeners = mListeners; 304 anim.mListeners = new ArrayList<AnimatorListener>(); 305 int numListeners = oldListeners.size(); 306 for (int i = 0; i < numListeners; ++i) { 307 anim.mListeners.add(oldListeners.get(i)); 308 } 309 } 310 if (mPauseListeners != null) { 311 ArrayList<AnimatorPauseListener> oldListeners = mPauseListeners; 312 anim.mPauseListeners = new ArrayList<AnimatorPauseListener>(); 313 int numListeners = oldListeners.size(); 314 for (int i = 0; i < numListeners; ++i) { 315 anim.mPauseListeners.add(oldListeners.get(i)); 316 } 317 } 318 return anim; 319 } catch (CloneNotSupportedException e) { 320 throw new AssertionError(); 321 } 322 } 323 324 /** 325 * This method tells the object to use appropriate information to extract 326 * starting values for the animation. For example, a AnimatorSet object will pass 327 * this call to its child objects to tell them to set up the values. A 328 * ObjectAnimator object will use the information it has about its target object 329 * and PropertyValuesHolder objects to get the start values for its properties. 330 * A ValueAnimator object will ignore the request since it does not have enough 331 * information (such as a target object) to gather these values. 332 */ 333 public void setupStartValues() { 334 } 335 336 /** 337 * This method tells the object to use appropriate information to extract 338 * ending values for the animation. For example, a AnimatorSet object will pass 339 * this call to its child objects to tell them to set up the values. A 340 * ObjectAnimator object will use the information it has about its target object 341 * and PropertyValuesHolder objects to get the start values for its properties. 342 * A ValueAnimator object will ignore the request since it does not have enough 343 * information (such as a target object) to gather these values. 344 */ 345 public void setupEndValues() { 346 } 347 348 /** 349 * Sets the target object whose property will be animated by this animation. Not all subclasses 350 * operate on target objects (for example, {@link ValueAnimator}, but this method 351 * is on the superclass for the convenience of dealing generically with those subclasses 352 * that do handle targets. 353 * 354 * @param target The object being animated 355 */ 356 public void setTarget(Object target) { 357 } 358 359 // Hide reverse() and canReverse() for now since reverse() only work for simple 360 // cases, like we don't support sequential, neither startDelay. 361 // TODO: make reverse() works for all the Animators. 362 /** 363 * @hide 364 */ 365 public boolean canReverse() { 366 return false; 367 } 368 369 /** 370 * @hide 371 */ 372 public void reverse() { 373 throw new IllegalStateException("Reverse is not supported"); 374 } 375 376 /** 377 * <p>An animation listener receives notifications from an animation. 378 * Notifications indicate animation related events, such as the end or the 379 * repetition of the animation.</p> 380 */ 381 public static interface AnimatorListener { 382 /** 383 * <p>Notifies the start of the animation.</p> 384 * 385 * @param animation The started animation. 386 */ 387 void onAnimationStart(Animator animation); 388 389 /** 390 * <p>Notifies the end of the animation. This callback is not invoked 391 * for animations with repeat count set to INFINITE.</p> 392 * 393 * @param animation The animation which reached its end. 394 */ 395 void onAnimationEnd(Animator animation); 396 397 /** 398 * <p>Notifies the cancellation of the animation. This callback is not invoked 399 * for animations with repeat count set to INFINITE.</p> 400 * 401 * @param animation The animation which was canceled. 402 */ 403 void onAnimationCancel(Animator animation); 404 405 /** 406 * <p>Notifies the repetition of the animation.</p> 407 * 408 * @param animation The animation which was repeated. 409 */ 410 void onAnimationRepeat(Animator animation); 411 } 412 413 /** 414 * A pause listener receives notifications from an animation when the 415 * animation is {@link #pause() paused} or {@link #resume() resumed}. 416 * 417 * @see #addPauseListener(AnimatorPauseListener) 418 */ 419 public static interface AnimatorPauseListener { 420 /** 421 * <p>Notifies that the animation was paused.</p> 422 * 423 * @param animation The animaton being paused. 424 * @see #pause() 425 */ 426 void onAnimationPause(Animator animation); 427 428 /** 429 * <p>Notifies that the animation was resumed, after being 430 * previously paused.</p> 431 * 432 * @param animation The animation being resumed. 433 * @see #resume() 434 */ 435 void onAnimationResume(Animator animation); 436 } 437 438 /** 439 * <p>Whether or not the Animator is allowed to run asynchronously off of 440 * the UI thread. This is a hint that informs the Animator that it is 441 * OK to run the animation off-thread, however the Animator may decide 442 * that it must run the animation on the UI thread anyway. 443 * 444 * <p>Regardless of whether or not the animation runs asynchronously, all 445 * listener callbacks will be called on the UI thread.</p> 446 * 447 * <p>To be able to use this hint the following must be true:</p> 448 * <ol> 449 * <li>The animator is immutable while {@link #isStarted()} is true. Requests 450 * to change duration, delay, etc... may be ignored.</li> 451 * <li>Lifecycle callback events may be asynchronous. Events such as 452 * {@link Animator.AnimatorListener#onAnimationEnd(Animator)} or 453 * {@link Animator.AnimatorListener#onAnimationRepeat(Animator)} may end up delayed 454 * as they must be posted back to the UI thread, and any actions performed 455 * by those callbacks (such as starting new animations) will not happen 456 * in the same frame.</li> 457 * <li>State change requests ({@link #cancel()}, {@link #end()}, {@link #reverse()}, etc...) 458 * may be asynchronous. It is guaranteed that all state changes that are 459 * performed on the UI thread in the same frame will be applied as a single 460 * atomic update, however that frame may be the current frame, 461 * the next frame, or some future frame. This will also impact the observed 462 * state of the Animator. For example, {@link #isStarted()} may still return true 463 * after a call to {@link #end()}. Using the lifecycle callbacks is preferred over 464 * queries to {@link #isStarted()}, {@link #isRunning()}, and {@link #isPaused()} 465 * for this reason.</li> 466 * </ol> 467 * @hide 468 */ 469 public void setAllowRunningAsynchronously(boolean mayRunAsync) { 470 // It is up to subclasses to support this, if they can. 471 } 472 } 473