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 import java.util.Collection; 21 import java.util.HashMap; 22 import java.util.List; 23 24 /** 25 * This class plays a set of {@link Animator} objects in the specified order. Animations 26 * can be set up to play together, in sequence, or after a specified delay. 27 * 28 * <p>There are two different approaches to adding animations to a <code>AnimatorSet</code>: 29 * either the {@link AnimatorSet#playTogether(Animator[]) playTogether()} or 30 * {@link AnimatorSet#playSequentially(Animator[]) playSequentially()} methods can be called to add 31 * a set of animations all at once, or the {@link AnimatorSet#play(Animator)} can be 32 * used in conjunction with methods in the {@link AnimatorSet.Builder Builder} 33 * class to add animations 34 * one by one.</p> 35 * 36 * <p>It is possible to set up a <code>AnimatorSet</code> with circular dependencies between 37 * its animations. For example, an animation a1 could be set up to start before animation a2, a2 38 * before a3, and a3 before a1. The results of this configuration are undefined, but will typically 39 * result in none of the affected animations being played. Because of this (and because 40 * circular dependencies do not make logical sense anyway), circular dependencies 41 * should be avoided, and the dependency flow of animations should only be in one direction. 42 * 43 * <div class="special reference"> 44 * <h3>Developer Guides</h3> 45 * <p>For more information about animating with {@code AnimatorSet}, read the 46 * <a href="{@docRoot}guide/topics/graphics/prop-animation.html#choreography">Property 47 * Animation</a> developer guide.</p> 48 * </div> 49 */ 50 public final class AnimatorSet extends Animator { 51 52 /** 53 * Internal variables 54 * NOTE: This object implements the clone() method, making a deep copy of any referenced 55 * objects. As other non-trivial fields are added to this class, make sure to add logic 56 * to clone() to make deep copies of them. 57 */ 58 59 /** 60 * Tracks animations currently being played, so that we know what to 61 * cancel or end when cancel() or end() is called on this AnimatorSet 62 */ 63 private ArrayList<Animator> mPlayingSet = new ArrayList<Animator>(); 64 65 /** 66 * Contains all nodes, mapped to their respective Animators. When new 67 * dependency information is added for an Animator, we want to add it 68 * to a single node representing that Animator, not create a new Node 69 * if one already exists. 70 */ 71 private HashMap<Animator, Node> mNodeMap = new HashMap<Animator, Node>(); 72 73 /** 74 * Set of all nodes created for this AnimatorSet. This list is used upon 75 * starting the set, and the nodes are placed in sorted order into the 76 * sortedNodes collection. 77 */ 78 private ArrayList<Node> mNodes = new ArrayList<Node>(); 79 80 /** 81 * The sorted list of nodes. This is the order in which the animations will 82 * be played. The details about when exactly they will be played depend 83 * on the dependency relationships of the nodes. 84 */ 85 private ArrayList<Node> mSortedNodes = new ArrayList<Node>(); 86 87 /** 88 * Flag indicating whether the nodes should be sorted prior to playing. This 89 * flag allows us to cache the previous sorted nodes so that if the sequence 90 * is replayed with no changes, it does not have to re-sort the nodes again. 91 */ 92 private boolean mNeedsSort = true; 93 94 private AnimatorSetListener mSetListener = null; 95 96 /** 97 * Flag indicating that the AnimatorSet has been manually 98 * terminated (by calling cancel() or end()). 99 * This flag is used to avoid starting other animations when currently-playing 100 * child animations of this AnimatorSet end. It also determines whether cancel/end 101 * notifications are sent out via the normal AnimatorSetListener mechanism. 102 */ 103 boolean mTerminated = false; 104 105 /** 106 * Indicates whether an AnimatorSet has been start()'d, whether or 107 * not there is a nonzero startDelay. 108 */ 109 private boolean mStarted = false; 110 111 // The amount of time in ms to delay starting the animation after start() is called 112 private long mStartDelay = 0; 113 114 // Animator used for a nonzero startDelay 115 private ValueAnimator mDelayAnim = null; 116 117 118 // How long the child animations should last in ms. The default value is negative, which 119 // simply means that there is no duration set on the AnimatorSet. When a real duration is 120 // set, it is passed along to the child animations. 121 private long mDuration = -1; 122 123 // Records the interpolator for the set. Null value indicates that no interpolator 124 // was set on this AnimatorSet, so it should not be passed down to the children. 125 private TimeInterpolator mInterpolator = null; 126 127 private boolean mReversible = true; 128 /** 129 * Sets up this AnimatorSet to play all of the supplied animations at the same time. 130 * This is equivalent to calling {@link #play(Animator)} with the first animator in the 131 * set and then {@link Builder#with(Animator)} with each of the other animators. Note that 132 * an Animator with a {@link Animator#setStartDelay(long) startDelay} will not actually 133 * start until that delay elapses, which means that if the first animator in the list 134 * supplied to this constructor has a startDelay, none of the other animators will start 135 * until that first animator's startDelay has elapsed. 136 * 137 * @param items The animations that will be started simultaneously. 138 */ playTogether(Animator... items)139 public void playTogether(Animator... items) { 140 if (items != null) { 141 mNeedsSort = true; 142 Builder builder = play(items[0]); 143 for (int i = 1; i < items.length; ++i) { 144 builder.with(items[i]); 145 } 146 } 147 } 148 149 /** 150 * Sets up this AnimatorSet to play all of the supplied animations at the same time. 151 * 152 * @param items The animations that will be started simultaneously. 153 */ playTogether(Collection<Animator> items)154 public void playTogether(Collection<Animator> items) { 155 if (items != null && items.size() > 0) { 156 mNeedsSort = true; 157 Builder builder = null; 158 for (Animator anim : items) { 159 if (builder == null) { 160 builder = play(anim); 161 } else { 162 builder.with(anim); 163 } 164 } 165 } 166 } 167 168 /** 169 * Sets up this AnimatorSet to play each of the supplied animations when the 170 * previous animation ends. 171 * 172 * @param items The animations that will be started one after another. 173 */ playSequentially(Animator... items)174 public void playSequentially(Animator... items) { 175 if (items != null) { 176 mNeedsSort = true; 177 if (items.length == 1) { 178 play(items[0]); 179 } else { 180 mReversible = false; 181 for (int i = 0; i < items.length - 1; ++i) { 182 play(items[i]).before(items[i+1]); 183 } 184 } 185 } 186 } 187 188 /** 189 * Sets up this AnimatorSet to play each of the supplied animations when the 190 * previous animation ends. 191 * 192 * @param items The animations that will be started one after another. 193 */ playSequentially(List<Animator> items)194 public void playSequentially(List<Animator> items) { 195 if (items != null && items.size() > 0) { 196 mNeedsSort = true; 197 if (items.size() == 1) { 198 play(items.get(0)); 199 } else { 200 mReversible = false; 201 for (int i = 0; i < items.size() - 1; ++i) { 202 play(items.get(i)).before(items.get(i+1)); 203 } 204 } 205 } 206 } 207 208 /** 209 * Returns the current list of child Animator objects controlled by this 210 * AnimatorSet. This is a copy of the internal list; modifications to the returned list 211 * will not affect the AnimatorSet, although changes to the underlying Animator objects 212 * will affect those objects being managed by the AnimatorSet. 213 * 214 * @return ArrayList<Animator> The list of child animations of this AnimatorSet. 215 */ getChildAnimations()216 public ArrayList<Animator> getChildAnimations() { 217 ArrayList<Animator> childList = new ArrayList<Animator>(); 218 for (Node node : mNodes) { 219 childList.add(node.animation); 220 } 221 return childList; 222 } 223 224 /** 225 * Sets the target object for all current {@link #getChildAnimations() child animations} 226 * of this AnimatorSet that take targets ({@link ObjectAnimator} and 227 * AnimatorSet). 228 * 229 * @param target The object being animated 230 */ 231 @Override setTarget(Object target)232 public void setTarget(Object target) { 233 for (Node node : mNodes) { 234 Animator animation = node.animation; 235 if (animation instanceof AnimatorSet) { 236 ((AnimatorSet)animation).setTarget(target); 237 } else if (animation instanceof ObjectAnimator) { 238 ((ObjectAnimator)animation).setTarget(target); 239 } 240 } 241 } 242 243 /** 244 * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations} 245 * of this AnimatorSet. The default value is null, which means that no interpolator 246 * is set on this AnimatorSet. Setting the interpolator to any non-null value 247 * will cause that interpolator to be set on the child animations 248 * when the set is started. 249 * 250 * @param interpolator the interpolator to be used by each child animation of this AnimatorSet 251 */ 252 @Override setInterpolator(TimeInterpolator interpolator)253 public void setInterpolator(TimeInterpolator interpolator) { 254 mInterpolator = interpolator; 255 } 256 257 @Override getInterpolator()258 public TimeInterpolator getInterpolator() { 259 return mInterpolator; 260 } 261 262 /** 263 * This method creates a <code>Builder</code> object, which is used to 264 * set up playing constraints. This initial <code>play()</code> method 265 * tells the <code>Builder</code> the animation that is the dependency for 266 * the succeeding commands to the <code>Builder</code>. For example, 267 * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play 268 * <code>a1</code> and <code>a2</code> at the same time, 269 * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play 270 * <code>a1</code> first, followed by <code>a2</code>, and 271 * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play 272 * <code>a2</code> first, followed by <code>a1</code>. 273 * 274 * <p>Note that <code>play()</code> is the only way to tell the 275 * <code>Builder</code> the animation upon which the dependency is created, 276 * so successive calls to the various functions in <code>Builder</code> 277 * will all refer to the initial parameter supplied in <code>play()</code> 278 * as the dependency of the other animations. For example, calling 279 * <code>play(a1).before(a2).before(a3)</code> will play both <code>a2</code> 280 * and <code>a3</code> when a1 ends; it does not set up a dependency between 281 * <code>a2</code> and <code>a3</code>.</p> 282 * 283 * @param anim The animation that is the dependency used in later calls to the 284 * methods in the returned <code>Builder</code> object. A null parameter will result 285 * in a null <code>Builder</code> return value. 286 * @return Builder The object that constructs the AnimatorSet based on the dependencies 287 * outlined in the calls to <code>play</code> and the other methods in the 288 * <code>Builder</code object. 289 */ play(Animator anim)290 public Builder play(Animator anim) { 291 if (anim != null) { 292 mNeedsSort = true; 293 return new Builder(anim); 294 } 295 return null; 296 } 297 298 /** 299 * {@inheritDoc} 300 * 301 * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it 302 * is responsible for.</p> 303 */ 304 @SuppressWarnings("unchecked") 305 @Override cancel()306 public void cancel() { 307 mTerminated = true; 308 if (isStarted()) { 309 ArrayList<AnimatorListener> tmpListeners = null; 310 if (mListeners != null) { 311 tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone(); 312 for (AnimatorListener listener : tmpListeners) { 313 listener.onAnimationCancel(this); 314 } 315 } 316 if (mDelayAnim != null && mDelayAnim.isRunning()) { 317 // If we're currently in the startDelay period, just cancel that animator and 318 // send out the end event to all listeners 319 mDelayAnim.cancel(); 320 } else if (mSortedNodes.size() > 0) { 321 for (Node node : mSortedNodes) { 322 node.animation.cancel(); 323 } 324 } 325 if (tmpListeners != null) { 326 for (AnimatorListener listener : tmpListeners) { 327 listener.onAnimationEnd(this); 328 } 329 } 330 mStarted = false; 331 } 332 } 333 334 /** 335 * {@inheritDoc} 336 * 337 * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is 338 * responsible for.</p> 339 */ 340 @Override end()341 public void end() { 342 mTerminated = true; 343 if (isStarted()) { 344 if (mSortedNodes.size() != mNodes.size()) { 345 // hasn't been started yet - sort the nodes now, then end them 346 sortNodes(); 347 for (Node node : mSortedNodes) { 348 if (mSetListener == null) { 349 mSetListener = new AnimatorSetListener(this); 350 } 351 node.animation.addListener(mSetListener); 352 } 353 } 354 if (mDelayAnim != null) { 355 mDelayAnim.cancel(); 356 } 357 if (mSortedNodes.size() > 0) { 358 for (Node node : mSortedNodes) { 359 node.animation.end(); 360 } 361 } 362 if (mListeners != null) { 363 ArrayList<AnimatorListener> tmpListeners = 364 (ArrayList<AnimatorListener>) mListeners.clone(); 365 for (AnimatorListener listener : tmpListeners) { 366 listener.onAnimationEnd(this); 367 } 368 } 369 mStarted = false; 370 } 371 } 372 373 /** 374 * Returns true if any of the child animations of this AnimatorSet have been started and have 375 * not yet ended. 376 * @return Whether this AnimatorSet has been started and has not yet ended. 377 */ 378 @Override isRunning()379 public boolean isRunning() { 380 for (Node node : mNodes) { 381 if (node.animation.isRunning()) { 382 return true; 383 } 384 } 385 return false; 386 } 387 388 @Override isStarted()389 public boolean isStarted() { 390 return mStarted; 391 } 392 393 /** 394 * The amount of time, in milliseconds, to delay starting the animation after 395 * {@link #start()} is called. 396 * 397 * @return the number of milliseconds to delay running the animation 398 */ 399 @Override getStartDelay()400 public long getStartDelay() { 401 return mStartDelay; 402 } 403 404 /** 405 * The amount of time, in milliseconds, to delay starting the animation after 406 * {@link #start()} is called. 407 408 * @param startDelay The amount of the delay, in milliseconds 409 */ 410 @Override setStartDelay(long startDelay)411 public void setStartDelay(long startDelay) { 412 if (mStartDelay > 0) { 413 mReversible = false; 414 } 415 mStartDelay = startDelay; 416 } 417 418 /** 419 * Gets the length of each of the child animations of this AnimatorSet. This value may 420 * be less than 0, which indicates that no duration has been set on this AnimatorSet 421 * and each of the child animations will use their own duration. 422 * 423 * @return The length of the animation, in milliseconds, of each of the child 424 * animations of this AnimatorSet. 425 */ 426 @Override getDuration()427 public long getDuration() { 428 return mDuration; 429 } 430 431 /** 432 * Sets the length of each of the current child animations of this AnimatorSet. By default, 433 * each child animation will use its own duration. If the duration is set on the AnimatorSet, 434 * then each child animation inherits this duration. 435 * 436 * @param duration The length of the animation, in milliseconds, of each of the child 437 * animations of this AnimatorSet. 438 */ 439 @Override setDuration(long duration)440 public AnimatorSet setDuration(long duration) { 441 if (duration < 0) { 442 throw new IllegalArgumentException("duration must be a value of zero or greater"); 443 } 444 // Just record the value for now - it will be used later when the AnimatorSet starts 445 mDuration = duration; 446 return this; 447 } 448 449 @Override setupStartValues()450 public void setupStartValues() { 451 for (Node node : mNodes) { 452 node.animation.setupStartValues(); 453 } 454 } 455 456 @Override setupEndValues()457 public void setupEndValues() { 458 for (Node node : mNodes) { 459 node.animation.setupEndValues(); 460 } 461 } 462 463 @Override pause()464 public void pause() { 465 boolean previouslyPaused = mPaused; 466 super.pause(); 467 if (!previouslyPaused && mPaused) { 468 if (mDelayAnim != null) { 469 mDelayAnim.pause(); 470 } else { 471 for (Node node : mNodes) { 472 node.animation.pause(); 473 } 474 } 475 } 476 } 477 478 @Override resume()479 public void resume() { 480 boolean previouslyPaused = mPaused; 481 super.resume(); 482 if (previouslyPaused && !mPaused) { 483 if (mDelayAnim != null) { 484 mDelayAnim.resume(); 485 } else { 486 for (Node node : mNodes) { 487 node.animation.resume(); 488 } 489 } 490 } 491 } 492 493 /** 494 * {@inheritDoc} 495 * 496 * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which 497 * it is responsible. The details of when exactly those animations are started depends on 498 * the dependency relationships that have been set up between the animations. 499 */ 500 @SuppressWarnings("unchecked") 501 @Override start()502 public void start() { 503 mTerminated = false; 504 mStarted = true; 505 mPaused = false; 506 507 for (Node node : mNodes) { 508 node.animation.setAllowRunningAsynchronously(false); 509 } 510 511 if (mDuration >= 0) { 512 // If the duration was set on this AnimatorSet, pass it along to all child animations 513 for (Node node : mNodes) { 514 // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to 515 // insert "play-after" delays 516 node.animation.setDuration(mDuration); 517 } 518 } 519 if (mInterpolator != null) { 520 for (Node node : mNodes) { 521 node.animation.setInterpolator(mInterpolator); 522 } 523 } 524 // First, sort the nodes (if necessary). This will ensure that sortedNodes 525 // contains the animation nodes in the correct order. 526 sortNodes(); 527 528 int numSortedNodes = mSortedNodes.size(); 529 for (int i = 0; i < numSortedNodes; ++i) { 530 Node node = mSortedNodes.get(i); 531 // First, clear out the old listeners 532 ArrayList<AnimatorListener> oldListeners = node.animation.getListeners(); 533 if (oldListeners != null && oldListeners.size() > 0) { 534 final ArrayList<AnimatorListener> clonedListeners = new 535 ArrayList<AnimatorListener>(oldListeners); 536 537 for (AnimatorListener listener : clonedListeners) { 538 if (listener instanceof DependencyListener || 539 listener instanceof AnimatorSetListener) { 540 node.animation.removeListener(listener); 541 } 542 } 543 } 544 } 545 546 // nodesToStart holds the list of nodes to be started immediately. We don't want to 547 // start the animations in the loop directly because we first need to set up 548 // dependencies on all of the nodes. For example, we don't want to start an animation 549 // when some other animation also wants to start when the first animation begins. 550 final ArrayList<Node> nodesToStart = new ArrayList<Node>(); 551 for (int i = 0; i < numSortedNodes; ++i) { 552 Node node = mSortedNodes.get(i); 553 if (mSetListener == null) { 554 mSetListener = new AnimatorSetListener(this); 555 } 556 if (node.dependencies == null || node.dependencies.size() == 0) { 557 nodesToStart.add(node); 558 } else { 559 int numDependencies = node.dependencies.size(); 560 for (int j = 0; j < numDependencies; ++j) { 561 Dependency dependency = node.dependencies.get(j); 562 dependency.node.animation.addListener( 563 new DependencyListener(this, node, dependency.rule)); 564 } 565 node.tmpDependencies = (ArrayList<Dependency>) node.dependencies.clone(); 566 } 567 node.animation.addListener(mSetListener); 568 } 569 // Now that all dependencies are set up, start the animations that should be started. 570 if (mStartDelay <= 0) { 571 for (Node node : nodesToStart) { 572 node.animation.start(); 573 mPlayingSet.add(node.animation); 574 } 575 } else { 576 mDelayAnim = ValueAnimator.ofFloat(0f, 1f); 577 mDelayAnim.setDuration(mStartDelay); 578 mDelayAnim.addListener(new AnimatorListenerAdapter() { 579 boolean canceled = false; 580 public void onAnimationCancel(Animator anim) { 581 canceled = true; 582 } 583 public void onAnimationEnd(Animator anim) { 584 if (!canceled) { 585 int numNodes = nodesToStart.size(); 586 for (int i = 0; i < numNodes; ++i) { 587 Node node = nodesToStart.get(i); 588 node.animation.start(); 589 mPlayingSet.add(node.animation); 590 } 591 } 592 mDelayAnim = null; 593 } 594 }); 595 mDelayAnim.start(); 596 } 597 if (mListeners != null) { 598 ArrayList<AnimatorListener> tmpListeners = 599 (ArrayList<AnimatorListener>) mListeners.clone(); 600 int numListeners = tmpListeners.size(); 601 for (int i = 0; i < numListeners; ++i) { 602 tmpListeners.get(i).onAnimationStart(this); 603 } 604 } 605 if (mNodes.size() == 0 && mStartDelay == 0) { 606 // Handle unusual case where empty AnimatorSet is started - should send out 607 // end event immediately since the event will not be sent out at all otherwise 608 mStarted = false; 609 if (mListeners != null) { 610 ArrayList<AnimatorListener> tmpListeners = 611 (ArrayList<AnimatorListener>) mListeners.clone(); 612 int numListeners = tmpListeners.size(); 613 for (int i = 0; i < numListeners; ++i) { 614 tmpListeners.get(i).onAnimationEnd(this); 615 } 616 } 617 } 618 } 619 620 @Override clone()621 public AnimatorSet clone() { 622 final AnimatorSet anim = (AnimatorSet) super.clone(); 623 /* 624 * The basic clone() operation copies all items. This doesn't work very well for 625 * AnimatorSet, because it will copy references that need to be recreated and state 626 * that may not apply. What we need to do now is put the clone in an uninitialized 627 * state, with fresh, empty data structures. Then we will build up the nodes list 628 * manually, as we clone each Node (and its animation). The clone will then be sorted, 629 * and will populate any appropriate lists, when it is started. 630 */ 631 anim.mNeedsSort = true; 632 anim.mTerminated = false; 633 anim.mStarted = false; 634 anim.mPlayingSet = new ArrayList<Animator>(); 635 anim.mNodeMap = new HashMap<Animator, Node>(); 636 anim.mNodes = new ArrayList<Node>(); 637 anim.mSortedNodes = new ArrayList<Node>(); 638 anim.mReversible = mReversible; 639 anim.mSetListener = null; 640 641 // Walk through the old nodes list, cloning each node and adding it to the new nodemap. 642 // One problem is that the old node dependencies point to nodes in the old AnimatorSet. 643 // We need to track the old/new nodes in order to reconstruct the dependencies in the clone. 644 HashMap<Node, Node> nodeCloneMap = new HashMap<Node, Node>(); // <old, new> 645 for (Node node : mNodes) { 646 Node nodeClone = node.clone(); 647 nodeCloneMap.put(node, nodeClone); 648 anim.mNodes.add(nodeClone); 649 anim.mNodeMap.put(nodeClone.animation, nodeClone); 650 // Clear out the dependencies in the clone; we'll set these up manually later 651 nodeClone.dependencies = null; 652 nodeClone.tmpDependencies = null; 653 nodeClone.nodeDependents = null; 654 nodeClone.nodeDependencies = null; 655 // clear out any listeners that were set up by the AnimatorSet; these will 656 // be set up when the clone's nodes are sorted 657 ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners(); 658 if (cloneListeners != null) { 659 ArrayList<AnimatorListener> listenersToRemove = null; 660 for (AnimatorListener listener : cloneListeners) { 661 if (listener instanceof AnimatorSetListener) { 662 if (listenersToRemove == null) { 663 listenersToRemove = new ArrayList<AnimatorListener>(); 664 } 665 listenersToRemove.add(listener); 666 } 667 } 668 if (listenersToRemove != null) { 669 for (AnimatorListener listener : listenersToRemove) { 670 cloneListeners.remove(listener); 671 } 672 } 673 } 674 } 675 // Now that we've cloned all of the nodes, we're ready to walk through their 676 // dependencies, mapping the old dependencies to the new nodes 677 for (Node node : mNodes) { 678 Node nodeClone = nodeCloneMap.get(node); 679 if (node.dependencies != null) { 680 for (Dependency dependency : node.dependencies) { 681 Node clonedDependencyNode = nodeCloneMap.get(dependency.node); 682 Dependency cloneDependency = new Dependency(clonedDependencyNode, 683 dependency.rule); 684 nodeClone.addDependency(cloneDependency); 685 } 686 } 687 } 688 689 return anim; 690 } 691 692 /** 693 * This class is the mechanism by which animations are started based on events in other 694 * animations. If an animation has multiple dependencies on other animations, then 695 * all dependencies must be satisfied before the animation is started. 696 */ 697 private static class DependencyListener implements AnimatorListener { 698 699 private AnimatorSet mAnimatorSet; 700 701 // The node upon which the dependency is based. 702 private Node mNode; 703 704 // The Dependency rule (WITH or AFTER) that the listener should wait for on 705 // the node 706 private int mRule; 707 DependencyListener(AnimatorSet animatorSet, Node node, int rule)708 public DependencyListener(AnimatorSet animatorSet, Node node, int rule) { 709 this.mAnimatorSet = animatorSet; 710 this.mNode = node; 711 this.mRule = rule; 712 } 713 714 /** 715 * Ignore cancel events for now. We may want to handle this eventually, 716 * to prevent follow-on animations from running when some dependency 717 * animation is canceled. 718 */ onAnimationCancel(Animator animation)719 public void onAnimationCancel(Animator animation) { 720 } 721 722 /** 723 * An end event is received - see if this is an event we are listening for 724 */ onAnimationEnd(Animator animation)725 public void onAnimationEnd(Animator animation) { 726 if (mRule == Dependency.AFTER) { 727 startIfReady(animation); 728 } 729 } 730 731 /** 732 * Ignore repeat events for now 733 */ onAnimationRepeat(Animator animation)734 public void onAnimationRepeat(Animator animation) { 735 } 736 737 /** 738 * A start event is received - see if this is an event we are listening for 739 */ onAnimationStart(Animator animation)740 public void onAnimationStart(Animator animation) { 741 if (mRule == Dependency.WITH) { 742 startIfReady(animation); 743 } 744 } 745 746 /** 747 * Check whether the event received is one that the node was waiting for. 748 * If so, mark it as complete and see whether it's time to start 749 * the animation. 750 * @param dependencyAnimation the animation that sent the event. 751 */ startIfReady(Animator dependencyAnimation)752 private void startIfReady(Animator dependencyAnimation) { 753 if (mAnimatorSet.mTerminated) { 754 // if the parent AnimatorSet was canceled, then don't start any dependent anims 755 return; 756 } 757 Dependency dependencyToRemove = null; 758 int numDependencies = mNode.tmpDependencies.size(); 759 for (int i = 0; i < numDependencies; ++i) { 760 Dependency dependency = mNode.tmpDependencies.get(i); 761 if (dependency.rule == mRule && 762 dependency.node.animation == dependencyAnimation) { 763 // rule fired - remove the dependency and listener and check to 764 // see whether it's time to start the animation 765 dependencyToRemove = dependency; 766 dependencyAnimation.removeListener(this); 767 break; 768 } 769 } 770 mNode.tmpDependencies.remove(dependencyToRemove); 771 if (mNode.tmpDependencies.size() == 0) { 772 // all dependencies satisfied: start the animation 773 mNode.animation.start(); 774 mAnimatorSet.mPlayingSet.add(mNode.animation); 775 } 776 } 777 778 } 779 780 private class AnimatorSetListener implements AnimatorListener { 781 782 private AnimatorSet mAnimatorSet; 783 AnimatorSetListener(AnimatorSet animatorSet)784 AnimatorSetListener(AnimatorSet animatorSet) { 785 mAnimatorSet = animatorSet; 786 } 787 onAnimationCancel(Animator animation)788 public void onAnimationCancel(Animator animation) { 789 if (!mTerminated) { 790 // Listeners are already notified of the AnimatorSet canceling in cancel(). 791 // The logic below only kicks in when animations end normally 792 if (mPlayingSet.size() == 0) { 793 if (mListeners != null) { 794 int numListeners = mListeners.size(); 795 for (int i = 0; i < numListeners; ++i) { 796 mListeners.get(i).onAnimationCancel(mAnimatorSet); 797 } 798 } 799 } 800 } 801 } 802 803 @SuppressWarnings("unchecked") onAnimationEnd(Animator animation)804 public void onAnimationEnd(Animator animation) { 805 animation.removeListener(this); 806 mPlayingSet.remove(animation); 807 Node animNode = mAnimatorSet.mNodeMap.get(animation); 808 animNode.done = true; 809 if (!mTerminated) { 810 // Listeners are already notified of the AnimatorSet ending in cancel() or 811 // end(); the logic below only kicks in when animations end normally 812 ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes; 813 boolean allDone = true; 814 int numSortedNodes = sortedNodes.size(); 815 for (int i = 0; i < numSortedNodes; ++i) { 816 if (!sortedNodes.get(i).done) { 817 allDone = false; 818 break; 819 } 820 } 821 if (allDone) { 822 // If this was the last child animation to end, then notify listeners that this 823 // AnimatorSet has ended 824 if (mListeners != null) { 825 ArrayList<AnimatorListener> tmpListeners = 826 (ArrayList<AnimatorListener>) mListeners.clone(); 827 int numListeners = tmpListeners.size(); 828 for (int i = 0; i < numListeners; ++i) { 829 tmpListeners.get(i).onAnimationEnd(mAnimatorSet); 830 } 831 } 832 mAnimatorSet.mStarted = false; 833 mAnimatorSet.mPaused = false; 834 } 835 } 836 } 837 838 // Nothing to do onAnimationRepeat(Animator animation)839 public void onAnimationRepeat(Animator animation) { 840 } 841 842 // Nothing to do onAnimationStart(Animator animation)843 public void onAnimationStart(Animator animation) { 844 } 845 846 } 847 848 /** 849 * This method sorts the current set of nodes, if needed. The sort is a simple 850 * DependencyGraph sort, which goes like this: 851 * - All nodes without dependencies become 'roots' 852 * - while roots list is not null 853 * - for each root r 854 * - add r to sorted list 855 * - remove r as a dependency from any other node 856 * - any nodes with no dependencies are added to the roots list 857 */ sortNodes()858 private void sortNodes() { 859 if (mNeedsSort) { 860 mSortedNodes.clear(); 861 ArrayList<Node> roots = new ArrayList<Node>(); 862 int numNodes = mNodes.size(); 863 for (int i = 0; i < numNodes; ++i) { 864 Node node = mNodes.get(i); 865 if (node.dependencies == null || node.dependencies.size() == 0) { 866 roots.add(node); 867 } 868 } 869 ArrayList<Node> tmpRoots = new ArrayList<Node>(); 870 while (roots.size() > 0) { 871 int numRoots = roots.size(); 872 for (int i = 0; i < numRoots; ++i) { 873 Node root = roots.get(i); 874 mSortedNodes.add(root); 875 if (root.nodeDependents != null) { 876 int numDependents = root.nodeDependents.size(); 877 for (int j = 0; j < numDependents; ++j) { 878 Node node = root.nodeDependents.get(j); 879 node.nodeDependencies.remove(root); 880 if (node.nodeDependencies.size() == 0) { 881 tmpRoots.add(node); 882 } 883 } 884 } 885 } 886 roots.clear(); 887 roots.addAll(tmpRoots); 888 tmpRoots.clear(); 889 } 890 mNeedsSort = false; 891 if (mSortedNodes.size() != mNodes.size()) { 892 throw new IllegalStateException("Circular dependencies cannot exist" 893 + " in AnimatorSet"); 894 } 895 } else { 896 // Doesn't need sorting, but still need to add in the nodeDependencies list 897 // because these get removed as the event listeners fire and the dependencies 898 // are satisfied 899 int numNodes = mNodes.size(); 900 for (int i = 0; i < numNodes; ++i) { 901 Node node = mNodes.get(i); 902 if (node.dependencies != null && node.dependencies.size() > 0) { 903 int numDependencies = node.dependencies.size(); 904 for (int j = 0; j < numDependencies; ++j) { 905 Dependency dependency = node.dependencies.get(j); 906 if (node.nodeDependencies == null) { 907 node.nodeDependencies = new ArrayList<Node>(); 908 } 909 if (!node.nodeDependencies.contains(dependency.node)) { 910 node.nodeDependencies.add(dependency.node); 911 } 912 } 913 } 914 // nodes are 'done' by default; they become un-done when started, and done 915 // again when ended 916 node.done = false; 917 } 918 } 919 } 920 921 /** 922 * @hide 923 */ 924 @Override canReverse()925 public boolean canReverse() { 926 if (!mReversible) { 927 return false; 928 } 929 // Loop to make sure all the Nodes can reverse. 930 for (Node node : mNodes) { 931 if (!node.animation.canReverse() || node.animation.getStartDelay() > 0) { 932 return false; 933 } 934 } 935 return true; 936 } 937 938 /** 939 * @hide 940 */ 941 @Override reverse()942 public void reverse() { 943 if (canReverse()) { 944 for (Node node : mNodes) { 945 node.animation.reverse(); 946 } 947 } 948 } 949 950 /** 951 * Dependency holds information about the node that some other node is 952 * dependent upon and the nature of that dependency. 953 * 954 */ 955 private static class Dependency { 956 static final int WITH = 0; // dependent node must start with this dependency node 957 static final int AFTER = 1; // dependent node must start when this dependency node finishes 958 959 // The node that the other node with this Dependency is dependent upon 960 public Node node; 961 962 // The nature of the dependency (WITH or AFTER) 963 public int rule; 964 Dependency(Node node, int rule)965 public Dependency(Node node, int rule) { 966 this.node = node; 967 this.rule = rule; 968 } 969 } 970 971 /** 972 * A Node is an embodiment of both the Animator that it wraps as well as 973 * any dependencies that are associated with that Animation. This includes 974 * both dependencies upon other nodes (in the dependencies list) as 975 * well as dependencies of other nodes upon this (in the nodeDependents list). 976 */ 977 private static class Node implements Cloneable { 978 public Animator animation; 979 980 /** 981 * These are the dependencies that this node's animation has on other 982 * nodes. For example, if this node's animation should begin with some 983 * other animation ends, then there will be an item in this node's 984 * dependencies list for that other animation's node. 985 */ 986 public ArrayList<Dependency> dependencies = null; 987 988 /** 989 * tmpDependencies is a runtime detail. We use the dependencies list for sorting. 990 * But we also use the list to keep track of when multiple dependencies are satisfied, 991 * but removing each dependency as it is satisfied. We do not want to remove 992 * the dependency itself from the list, because we need to retain that information 993 * if the AnimatorSet is launched in the future. So we create a copy of the dependency 994 * list when the AnimatorSet starts and use this tmpDependencies list to track the 995 * list of satisfied dependencies. 996 */ 997 public ArrayList<Dependency> tmpDependencies = null; 998 999 /** 1000 * nodeDependencies is just a list of the nodes that this Node is dependent upon. 1001 * This information is used in sortNodes(), to determine when a node is a root. 1002 */ 1003 public ArrayList<Node> nodeDependencies = null; 1004 1005 /** 1006 * nodeDepdendents is the list of nodes that have this node as a dependency. This 1007 * is a utility field used in sortNodes to facilitate removing this node as a 1008 * dependency when it is a root node. 1009 */ 1010 public ArrayList<Node> nodeDependents = null; 1011 1012 /** 1013 * Flag indicating whether the animation in this node is finished. This flag 1014 * is used by AnimatorSet to check, as each animation ends, whether all child animations 1015 * are done and it's time to send out an end event for the entire AnimatorSet. 1016 */ 1017 public boolean done = false; 1018 1019 /** 1020 * Constructs the Node with the animation that it encapsulates. A Node has no 1021 * dependencies by default; dependencies are added via the addDependency() 1022 * method. 1023 * 1024 * @param animation The animation that the Node encapsulates. 1025 */ Node(Animator animation)1026 public Node(Animator animation) { 1027 this.animation = animation; 1028 } 1029 1030 /** 1031 * Add a dependency to this Node. The dependency includes information about the 1032 * node that this node is dependency upon and the nature of the dependency. 1033 * @param dependency 1034 */ addDependency(Dependency dependency)1035 public void addDependency(Dependency dependency) { 1036 if (dependencies == null) { 1037 dependencies = new ArrayList<Dependency>(); 1038 nodeDependencies = new ArrayList<Node>(); 1039 } 1040 dependencies.add(dependency); 1041 if (!nodeDependencies.contains(dependency.node)) { 1042 nodeDependencies.add(dependency.node); 1043 } 1044 Node dependencyNode = dependency.node; 1045 if (dependencyNode.nodeDependents == null) { 1046 dependencyNode.nodeDependents = new ArrayList<Node>(); 1047 } 1048 dependencyNode.nodeDependents.add(this); 1049 } 1050 1051 @Override clone()1052 public Node clone() { 1053 try { 1054 Node node = (Node) super.clone(); 1055 node.animation = (Animator) animation.clone(); 1056 return node; 1057 } catch (CloneNotSupportedException e) { 1058 throw new AssertionError(); 1059 } 1060 } 1061 } 1062 1063 /** 1064 * The <code>Builder</code> object is a utility class to facilitate adding animations to a 1065 * <code>AnimatorSet</code> along with the relationships between the various animations. The 1066 * intention of the <code>Builder</code> methods, along with the {@link 1067 * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible 1068 * to express the dependency relationships of animations in a natural way. Developers can also 1069 * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link 1070 * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need, 1071 * but it might be easier in some situations to express the AnimatorSet of animations in pairs. 1072 * <p/> 1073 * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed 1074 * internally via a call to {@link AnimatorSet#play(Animator)}.</p> 1075 * <p/> 1076 * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to 1077 * play when anim2 finishes, and anim4 to play when anim3 finishes:</p> 1078 * <pre> 1079 * AnimatorSet s = new AnimatorSet(); 1080 * s.play(anim1).with(anim2); 1081 * s.play(anim2).before(anim3); 1082 * s.play(anim4).after(anim3); 1083 * </pre> 1084 * <p/> 1085 * <p>Note in the example that both {@link Builder#before(Animator)} and {@link 1086 * Builder#after(Animator)} are used. These are just different ways of expressing the same 1087 * relationship and are provided to make it easier to say things in a way that is more natural, 1088 * depending on the situation.</p> 1089 * <p/> 1090 * <p>It is possible to make several calls into the same <code>Builder</code> object to express 1091 * multiple relationships. However, note that it is only the animation passed into the initial 1092 * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive 1093 * calls to the <code>Builder</code> object. For example, the following code starts both anim2 1094 * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and 1095 * anim3: 1096 * <pre> 1097 * AnimatorSet s = new AnimatorSet(); 1098 * s.play(anim1).before(anim2).before(anim3); 1099 * </pre> 1100 * If the desired result is to play anim1 then anim2 then anim3, this code expresses the 1101 * relationship correctly:</p> 1102 * <pre> 1103 * AnimatorSet s = new AnimatorSet(); 1104 * s.play(anim1).before(anim2); 1105 * s.play(anim2).before(anim3); 1106 * </pre> 1107 * <p/> 1108 * <p>Note that it is possible to express relationships that cannot be resolved and will not 1109 * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no 1110 * sense. In general, circular dependencies like this one (or more indirect ones where a depends 1111 * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets 1112 * that can boil down to a simple, one-way relationship of animations starting with, before, and 1113 * after other, different, animations.</p> 1114 */ 1115 public class Builder { 1116 1117 /** 1118 * This tracks the current node being processed. It is supplied to the play() method 1119 * of AnimatorSet and passed into the constructor of Builder. 1120 */ 1121 private Node mCurrentNode; 1122 1123 /** 1124 * package-private constructor. Builders are only constructed by AnimatorSet, when the 1125 * play() method is called. 1126 * 1127 * @param anim The animation that is the dependency for the other animations passed into 1128 * the other methods of this Builder object. 1129 */ Builder(Animator anim)1130 Builder(Animator anim) { 1131 mCurrentNode = mNodeMap.get(anim); 1132 if (mCurrentNode == null) { 1133 mCurrentNode = new Node(anim); 1134 mNodeMap.put(anim, mCurrentNode); 1135 mNodes.add(mCurrentNode); 1136 } 1137 } 1138 1139 /** 1140 * Sets up the given animation to play at the same time as the animation supplied in the 1141 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object. 1142 * 1143 * @param anim The animation that will play when the animation supplied to the 1144 * {@link AnimatorSet#play(Animator)} method starts. 1145 */ with(Animator anim)1146 public Builder with(Animator anim) { 1147 Node node = mNodeMap.get(anim); 1148 if (node == null) { 1149 node = new Node(anim); 1150 mNodeMap.put(anim, node); 1151 mNodes.add(node); 1152 } 1153 Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH); 1154 node.addDependency(dependency); 1155 return this; 1156 } 1157 1158 /** 1159 * Sets up the given animation to play when the animation supplied in the 1160 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 1161 * ends. 1162 * 1163 * @param anim The animation that will play when the animation supplied to the 1164 * {@link AnimatorSet#play(Animator)} method ends. 1165 */ before(Animator anim)1166 public Builder before(Animator anim) { 1167 mReversible = false; 1168 Node node = mNodeMap.get(anim); 1169 if (node == null) { 1170 node = new Node(anim); 1171 mNodeMap.put(anim, node); 1172 mNodes.add(node); 1173 } 1174 Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER); 1175 node.addDependency(dependency); 1176 return this; 1177 } 1178 1179 /** 1180 * Sets up the given animation to play when the animation supplied in the 1181 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 1182 * to start when the animation supplied in this method call ends. 1183 * 1184 * @param anim The animation whose end will cause the animation supplied to the 1185 * {@link AnimatorSet#play(Animator)} method to play. 1186 */ after(Animator anim)1187 public Builder after(Animator anim) { 1188 mReversible = false; 1189 Node node = mNodeMap.get(anim); 1190 if (node == null) { 1191 node = new Node(anim); 1192 mNodeMap.put(anim, node); 1193 mNodes.add(node); 1194 } 1195 Dependency dependency = new Dependency(node, Dependency.AFTER); 1196 mCurrentNode.addDependency(dependency); 1197 return this; 1198 } 1199 1200 /** 1201 * Sets up the animation supplied in the 1202 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 1203 * to play when the given amount of time elapses. 1204 * 1205 * @param delay The number of milliseconds that should elapse before the 1206 * animation starts. 1207 */ after(long delay)1208 public Builder after(long delay) { 1209 // setup dummy ValueAnimator just to run the clock 1210 ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); 1211 anim.setDuration(delay); 1212 after(anim); 1213 return this; 1214 } 1215 1216 } 1217 1218 } 1219