AnimatorSet.java revision a18a86b43e40e3c15dcca0ae0148d641be9b25fe
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 17package android.animation; 18 19import android.view.animation.Interpolator; 20 21import java.util.ArrayList; 22import java.util.HashMap; 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 */ 43public final class AnimatorSet extends Animator { 44 45 /** 46 * Internal variables 47 * NOTE: This object implements the clone() method, making a deep copy of any referenced 48 * objects. As other non-trivial fields are added to this class, make sure to add logic 49 * to clone() to make deep copies of them. 50 */ 51 52 /** 53 * Tracks animations currently being played, so that we know what to 54 * cancel or end when cancel() or end() is called on this AnimatorSet 55 */ 56 private ArrayList<Animator> mPlayingSet = new ArrayList<Animator>(); 57 58 /** 59 * Contains all nodes, mapped to their respective Animators. When new 60 * dependency information is added for an Animator, we want to add it 61 * to a single node representing that Animator, not create a new Node 62 * if one already exists. 63 */ 64 private HashMap<Animator, Node> mNodeMap = new HashMap<Animator, Node>(); 65 66 /** 67 * Set of all nodes created for this AnimatorSet. This list is used upon 68 * starting the set, and the nodes are placed in sorted order into the 69 * sortedNodes collection. 70 */ 71 private ArrayList<Node> mNodes = new ArrayList<Node>(); 72 73 /** 74 * The sorted list of nodes. This is the order in which the animations will 75 * be played. The details about when exactly they will be played depend 76 * on the dependency relationships of the nodes. 77 */ 78 private ArrayList<Node> mSortedNodes = new ArrayList<Node>(); 79 80 /** 81 * Flag indicating whether the nodes should be sorted prior to playing. This 82 * flag allows us to cache the previous sorted nodes so that if the sequence 83 * is replayed with no changes, it does not have to re-sort the nodes again. 84 */ 85 private boolean mNeedsSort = true; 86 87 private AnimatorSetListener mSetListener = null; 88 89 /** 90 * Flag indicating that the AnimatorSet has been canceled (by calling cancel() or end()). 91 * This flag is used to avoid starting other animations when currently-playing 92 * child animations of this AnimatorSet end. 93 */ 94 boolean mCanceled = false; 95 96 // The amount of time in ms to delay starting the animation after start() is called 97 private long mStartDelay = 0; 98 99 100 // How long the child animations should last in ms. The default value is negative, which 101 // simply means that there is no duration set on the AnimatorSet. When a real duration is 102 // set, it is passed along to the child animations. 103 private long mDuration = -1; 104 105 106 /** 107 * Sets up this AnimatorSet to play all of the supplied animations at the same time. 108 * 109 * @param items The animations that will be started simultaneously. 110 */ 111 public void playTogether(Animator... items) { 112 if (items != null) { 113 mNeedsSort = true; 114 Builder builder = play(items[0]); 115 for (int i = 1; i < items.length; ++i) { 116 builder.with(items[i]); 117 } 118 } 119 } 120 121 /** 122 * Sets up this AnimatorSet to play each of the supplied animations when the 123 * previous animation ends. 124 * 125 * @param items The aniamtions that will be started one after another. 126 */ 127 public void playSequentially(Animator... items) { 128 if (items != null) { 129 mNeedsSort = true; 130 if (items.length == 1) { 131 play(items[0]); 132 } else { 133 for (int i = 0; i < items.length - 1; ++i) { 134 play(items[i]).before(items[i+1]); 135 } 136 } 137 } 138 } 139 140 /** 141 * Returns the current list of child Animator objects controlled by this 142 * AnimatorSet. This is a copy of the internal list; modifications to the returned list 143 * will not affect the AnimatorSet, although changes to the underlying Animator objects 144 * will affect those objects being managed by the AnimatorSet. 145 * 146 * @return ArrayList<Animator> The list of child animations of this AnimatorSet. 147 */ 148 public ArrayList<Animator> getChildAnimations() { 149 ArrayList<Animator> childList = new ArrayList<Animator>(); 150 for (Node node : mNodes) { 151 childList.add(node.animation); 152 } 153 return childList; 154 } 155 156 /** 157 * Sets the target object for all current {@link #getChildAnimations() child animations} 158 * of this AnimatorSet that take targets ({@link ObjectAnimator} and 159 * AnimatorSet). 160 * 161 * @param target The object being animated 162 */ 163 @Override 164 public void setTarget(Object target) { 165 for (Node node : mNodes) { 166 Animator animation = node.animation; 167 if (animation instanceof AnimatorSet) { 168 ((AnimatorSet)animation).setTarget(target); 169 } else if (animation instanceof ObjectAnimator) { 170 ((ObjectAnimator)animation).setTarget(target); 171 } 172 } 173 } 174 175 /** 176 * Sets the Interpolator for all current {@link #getChildAnimations() child animations} 177 * of this AnimatorSet. 178 * 179 * @param interpolator the interpolator to be used by each child animation of this AnimatorSet 180 */ 181 @Override 182 public void setInterpolator(Interpolator interpolator) { 183 for (Node node : mNodes) { 184 node.animation.setInterpolator(interpolator); 185 } 186 } 187 188 /** 189 * This method creates a <code>Builder</code> object, which is used to 190 * set up playing constraints. This initial <code>play()</code> method 191 * tells the <code>Builder</code> the animation that is the dependency for 192 * the succeeding commands to the <code>Builder</code>. For example, 193 * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play 194 * <code>a1</code> and <code>a2</code> at the same time, 195 * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play 196 * <code>a1</code> first, followed by <code>a2</code>, and 197 * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play 198 * <code>a2</code> first, followed by <code>a1</code>. 199 * 200 * <p>Note that <code>play()</code> is the only way to tell the 201 * <code>Builder</code> the animation upon which the dependency is created, 202 * so successive calls to the various functions in <code>Builder</code> 203 * will all refer to the initial parameter supplied in <code>play()</code> 204 * as the dependency of the other animations. For example, calling 205 * <code>play(a1).before(a2).before(a3)</code> will play both <code>a2</code> 206 * and <code>a3</code> when a1 ends; it does not set up a dependency between 207 * <code>a2</code> and <code>a3</code>.</p> 208 * 209 * @param anim The animation that is the dependency used in later calls to the 210 * methods in the returned <code>Builder</code> object. A null parameter will result 211 * in a null <code>Builder</code> return value. 212 * @return Builder The object that constructs the AnimatorSet based on the dependencies 213 * outlined in the calls to <code>play</code> and the other methods in the 214 * <code>Builder</code object. 215 */ 216 public Builder play(Animator anim) { 217 if (anim != null) { 218 mNeedsSort = true; 219 return new Builder(anim); 220 } 221 return null; 222 } 223 224 /** 225 * {@inheritDoc} 226 * 227 * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it is 228 * responsible for.</p> 229 */ 230 @SuppressWarnings("unchecked") 231 @Override 232 public void cancel() { 233 mCanceled = true; 234 if (mListeners != null) { 235 ArrayList<AnimatorListener> tmpListeners = 236 (ArrayList<AnimatorListener>) mListeners.clone(); 237 for (AnimatorListener listener : tmpListeners) { 238 listener.onAnimationCancel(this); 239 } 240 } 241 if (mSortedNodes.size() > 0) { 242 for (Node node : mSortedNodes) { 243 node.animation.cancel(); 244 } 245 } 246 } 247 248 /** 249 * {@inheritDoc} 250 * 251 * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is 252 * responsible for.</p> 253 */ 254 @Override 255 public void end() { 256 mCanceled = true; 257 if (mSortedNodes.size() != mNodes.size()) { 258 // hasn't been started yet - sort the nodes now, then end them 259 sortNodes(); 260 for (Node node : mSortedNodes) { 261 if (mSetListener == null) { 262 mSetListener = new AnimatorSetListener(this); 263 } 264 node.animation.addListener(mSetListener); 265 } 266 } 267 if (mSortedNodes.size() > 0) { 268 for (Node node : mSortedNodes) { 269 node.animation.end(); 270 } 271 } 272 } 273 274 /** 275 * Returns true if any of the child animations of this AnimatorSet have been started and have not 276 * yet ended. 277 * @return Whether this AnimatorSet has been started and has not yet ended. 278 */ 279 @Override 280 public boolean isRunning() { 281 for (Node node : mNodes) { 282 if (node.animation.isRunning()) { 283 return true; 284 } 285 } 286 return false; 287 } 288 289 /** 290 * The amount of time, in milliseconds, to delay starting the animation after 291 * {@link #start()} is called. 292 * 293 * @return the number of milliseconds to delay running the animation 294 */ 295 @Override 296 public long getStartDelay() { 297 return mStartDelay; 298 } 299 300 /** 301 * The amount of time, in milliseconds, to delay starting the animation after 302 * {@link #start()} is called. 303 304 * @param startDelay The amount of the delay, in milliseconds 305 */ 306 @Override 307 public void setStartDelay(long startDelay) { 308 mStartDelay = startDelay; 309 } 310 311 /** 312 * Gets the length of each of the child animations of this AnimatorSet. This value may 313 * be less than 0, which indicates that no duration has been set on this AnimatorSet 314 * and each of the child animations will use their own duration. 315 * 316 * @return The length of the animation, in milliseconds, of each of the child 317 * animations of this AnimatorSet. 318 */ 319 @Override 320 public long getDuration() { 321 return mDuration; 322 } 323 324 /** 325 * Sets the length of each of the current child animations of this AnimatorSet. By default, 326 * each child animation will use its own duration. If the duration is set on the AnimatorSet, 327 * then each child animation inherits this duration. 328 * 329 * @param duration The length of the animation, in milliseconds, of each of the child 330 * animations of this AnimatorSet. 331 */ 332 @Override 333 public void setDuration(long duration) { 334 if (duration < 0) { 335 throw new IllegalArgumentException("duration must be a value of zero or greater"); 336 } 337 for (Node node : mNodes) { 338 // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to 339 // insert "play-after" delays 340 node.animation.setDuration(duration); 341 } 342 mDuration = duration; 343 } 344 345 /** 346 * {@inheritDoc} 347 * 348 * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which 349 * it is responsible. The details of when exactly those animations are started depends on 350 * the dependency relationships that have been set up between the animations. 351 */ 352 @SuppressWarnings("unchecked") 353 @Override 354 public void start() { 355 mCanceled = false; 356 357 // First, sort the nodes (if necessary). This will ensure that sortedNodes 358 // contains the animation nodes in the correct order. 359 sortNodes(); 360 361 // nodesToStart holds the list of nodes to be started immediately. We don't want to 362 // start the animations in the loop directly because we first need to set up 363 // dependencies on all of the nodes. For example, we don't want to start an animation 364 // when some other animation also wants to start when the first animation begins. 365 final ArrayList<Node> nodesToStart = new ArrayList<Node>(); 366 for (Node node : mSortedNodes) { 367 if (mSetListener == null) { 368 mSetListener = new AnimatorSetListener(this); 369 } 370 if (node.dependencies == null || node.dependencies.size() == 0) { 371 nodesToStart.add(node); 372 } else { 373 for (Dependency dependency : node.dependencies) { 374 dependency.node.animation.addListener( 375 new DependencyListener(this, node, dependency.rule)); 376 } 377 node.tmpDependencies = (ArrayList<Dependency>) node.dependencies.clone(); 378 } 379 node.animation.addListener(mSetListener); 380 } 381 // Now that all dependencies are set up, start the animations that should be started. 382 if (mStartDelay <= 0) { 383 for (Node node : nodesToStart) { 384 node.animation.start(); 385 mPlayingSet.add(node.animation); 386 } 387 } else { 388 // TODO: Need to cancel out of the delay appropriately 389 ValueAnimator delayAnim = new ValueAnimator(mStartDelay, 0f, 1f); 390 delayAnim.addListener(new AnimatorListenerAdapter() { 391 public void onAnimationEnd(Animator anim) { 392 for (Node node : nodesToStart) { 393 node.animation.start(); 394 mPlayingSet.add(node.animation); 395 } 396 } 397 }); 398 } 399 if (mListeners != null) { 400 ArrayList<AnimatorListener> tmpListeners = 401 (ArrayList<AnimatorListener>) mListeners.clone(); 402 for (AnimatorListener listener : tmpListeners) { 403 listener.onAnimationStart(this); 404 } 405 } 406 } 407 408 @Override 409 public AnimatorSet clone() { 410 final AnimatorSet anim = (AnimatorSet) super.clone(); 411 /* 412 * The basic clone() operation copies all items. This doesn't work very well for 413 * AnimatorSet, because it will copy references that need to be recreated and state 414 * that may not apply. What we need to do now is put the clone in an uninitialized 415 * state, with fresh, empty data structures. Then we will build up the nodes list 416 * manually, as we clone each Node (and its animation). The clone will then be sorted, 417 * and will populate any appropriate lists, when it is started. 418 */ 419 anim.mNeedsSort = true; 420 anim.mCanceled = false; 421 anim.mPlayingSet = new ArrayList<Animator>(); 422 anim.mNodeMap = new HashMap<Animator, Node>(); 423 anim.mNodes = new ArrayList<Node>(); 424 anim.mSortedNodes = new ArrayList<Node>(); 425 426 // Walk through the old nodes list, cloning each node and adding it to the new nodemap. 427 // One problem is that the old node dependencies point to nodes in the old AnimatorSet. 428 // We need to track the old/new nodes in order to reconstruct the dependencies in the clone. 429 HashMap<Node, Node> nodeCloneMap = new HashMap<Node, Node>(); // <old, new> 430 for (Node node : mNodes) { 431 Node nodeClone = node.clone(); 432 nodeCloneMap.put(node, nodeClone); 433 anim.mNodes.add(nodeClone); 434 anim.mNodeMap.put(nodeClone.animation, nodeClone); 435 // Clear out the dependencies in the clone; we'll set these up manually later 436 nodeClone.dependencies = null; 437 nodeClone.tmpDependencies = null; 438 nodeClone.nodeDependents = null; 439 nodeClone.nodeDependencies = null; 440 // clear out any listeners that were set up by the AnimatorSet; these will 441 // be set up when the clone's nodes are sorted 442 ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners(); 443 if (cloneListeners != null) { 444 ArrayList<AnimatorListener> listenersToRemove = null; 445 for (AnimatorListener listener : cloneListeners) { 446 if (listener instanceof AnimatorSetListener) { 447 if (listenersToRemove == null) { 448 listenersToRemove = new ArrayList<AnimatorListener>(); 449 } 450 listenersToRemove.add(listener); 451 } 452 } 453 if (listenersToRemove != null) { 454 for (AnimatorListener listener : listenersToRemove) { 455 cloneListeners.remove(listener); 456 } 457 } 458 } 459 } 460 // Now that we've cloned all of the nodes, we're ready to walk through their 461 // dependencies, mapping the old dependencies to the new nodes 462 for (Node node : mNodes) { 463 Node nodeClone = nodeCloneMap.get(node); 464 if (node.dependencies != null) { 465 for (Dependency dependency : node.dependencies) { 466 Node clonedDependencyNode = nodeCloneMap.get(dependency.node); 467 Dependency cloneDependency = new Dependency(clonedDependencyNode, 468 dependency.rule); 469 nodeClone.addDependency(cloneDependency); 470 } 471 } 472 } 473 474 return anim; 475 } 476 477 /** 478 * This class is the mechanism by which animations are started based on events in other 479 * animations. If an animation has multiple dependencies on other animations, then 480 * all dependencies must be satisfied before the animation is started. 481 */ 482 private static class DependencyListener implements AnimatorListener { 483 484 private AnimatorSet mAnimatorSet; 485 486 // The node upon which the dependency is based. 487 private Node mNode; 488 489 // The Dependency rule (WITH or AFTER) that the listener should wait for on 490 // the node 491 private int mRule; 492 493 public DependencyListener(AnimatorSet animatorSet, Node node, int rule) { 494 this.mAnimatorSet = animatorSet; 495 this.mNode = node; 496 this.mRule = rule; 497 } 498 499 /** 500 * Ignore cancel events for now. We may want to handle this eventually, 501 * to prevent follow-on animations from running when some dependency 502 * animation is canceled. 503 */ 504 public void onAnimationCancel(Animator animation) { 505 } 506 507 /** 508 * An end event is received - see if this is an event we are listening for 509 */ 510 public void onAnimationEnd(Animator animation) { 511 if (mRule == Dependency.AFTER) { 512 startIfReady(animation); 513 } 514 } 515 516 /** 517 * Ignore repeat events for now 518 */ 519 public void onAnimationRepeat(Animator animation) { 520 } 521 522 /** 523 * A start event is received - see if this is an event we are listening for 524 */ 525 public void onAnimationStart(Animator animation) { 526 if (mRule == Dependency.WITH) { 527 startIfReady(animation); 528 } 529 } 530 531 /** 532 * Check whether the event received is one that the node was waiting for. 533 * If so, mark it as complete and see whether it's time to start 534 * the animation. 535 * @param dependencyAnimation the animation that sent the event. 536 */ 537 private void startIfReady(Animator dependencyAnimation) { 538 if (mAnimatorSet.mCanceled) { 539 // if the parent AnimatorSet was canceled, then don't start any dependent anims 540 return; 541 } 542 Dependency dependencyToRemove = null; 543 for (Dependency dependency : mNode.tmpDependencies) { 544 if (dependency.rule == mRule && 545 dependency.node.animation == dependencyAnimation) { 546 // rule fired - remove the dependency and listener and check to 547 // see whether it's time to start the animation 548 dependencyToRemove = dependency; 549 dependencyAnimation.removeListener(this); 550 break; 551 } 552 } 553 mNode.tmpDependencies.remove(dependencyToRemove); 554 if (mNode.tmpDependencies.size() == 0) { 555 // all dependencies satisfied: start the animation 556 mNode.animation.start(); 557 mAnimatorSet.mPlayingSet.add(mNode.animation); 558 } 559 } 560 561 } 562 563 private class AnimatorSetListener implements AnimatorListener { 564 565 private AnimatorSet mAnimatorSet; 566 567 AnimatorSetListener(AnimatorSet animatorSet) { 568 mAnimatorSet = animatorSet; 569 } 570 571 public void onAnimationCancel(Animator animation) { 572 if (mPlayingSet.size() == 0) { 573 if (mListeners != null) { 574 for (AnimatorListener listener : mListeners) { 575 listener.onAnimationCancel(mAnimatorSet); 576 } 577 } 578 } 579 } 580 581 @SuppressWarnings("unchecked") 582 public void onAnimationEnd(Animator animation) { 583 animation.removeListener(this); 584 mPlayingSet.remove(animation); 585 Node animNode = mAnimatorSet.mNodeMap.get(animation); 586 animNode.done = true; 587 ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes; 588 boolean allDone = true; 589 for (Node node : sortedNodes) { 590 if (!node.done) { 591 allDone = false; 592 break; 593 } 594 } 595 if (allDone) { 596 // If this was the last child animation to end, then notify listeners that this 597 // AnimatorSet has ended 598 if (mListeners != null) { 599 ArrayList<AnimatorListener> tmpListeners = 600 (ArrayList<AnimatorListener>) mListeners.clone(); 601 for (AnimatorListener listener : tmpListeners) { 602 listener.onAnimationEnd(mAnimatorSet); 603 } 604 } 605 } 606 } 607 608 // Nothing to do 609 public void onAnimationRepeat(Animator animation) { 610 } 611 612 // Nothing to do 613 public void onAnimationStart(Animator animation) { 614 } 615 616 } 617 618 /** 619 * This method sorts the current set of nodes, if needed. The sort is a simple 620 * DependencyGraph sort, which goes like this: 621 * - All nodes without dependencies become 'roots' 622 * - while roots list is not null 623 * - for each root r 624 * - add r to sorted list 625 * - remove r as a dependency from any other node 626 * - any nodes with no dependencies are added to the roots list 627 */ 628 private void sortNodes() { 629 if (mNeedsSort) { 630 mSortedNodes.clear(); 631 ArrayList<Node> roots = new ArrayList<Node>(); 632 for (Node node : mNodes) { 633 if (node.dependencies == null || node.dependencies.size() == 0) { 634 roots.add(node); 635 } 636 } 637 ArrayList<Node> tmpRoots = new ArrayList<Node>(); 638 while (roots.size() > 0) { 639 for (Node root : roots) { 640 mSortedNodes.add(root); 641 if (root.nodeDependents != null) { 642 for (Node node : root.nodeDependents) { 643 node.nodeDependencies.remove(root); 644 if (node.nodeDependencies.size() == 0) { 645 tmpRoots.add(node); 646 } 647 } 648 } 649 } 650 roots.clear(); 651 roots.addAll(tmpRoots); 652 tmpRoots.clear(); 653 } 654 mNeedsSort = false; 655 if (mSortedNodes.size() != mNodes.size()) { 656 throw new IllegalStateException("Circular dependencies cannot exist" 657 + " in AnimatorSet"); 658 } 659 } else { 660 // Doesn't need sorting, but still need to add in the nodeDependencies list 661 // because these get removed as the event listeners fire and the dependencies 662 // are satisfied 663 for (Node node : mNodes) { 664 if (node.dependencies != null && node.dependencies.size() > 0) { 665 for (Dependency dependency : node.dependencies) { 666 if (node.nodeDependencies == null) { 667 node.nodeDependencies = new ArrayList<Node>(); 668 } 669 if (!node.nodeDependencies.contains(dependency.node)) { 670 node.nodeDependencies.add(dependency.node); 671 } 672 } 673 } 674 node.done = false; 675 } 676 } 677 } 678 679 /** 680 * Dependency holds information about the node that some other node is 681 * dependent upon and the nature of that dependency. 682 * 683 */ 684 private static class Dependency { 685 static final int WITH = 0; // dependent node must start with this dependency node 686 static final int AFTER = 1; // dependent node must start when this dependency node finishes 687 688 // The node that the other node with this Dependency is dependent upon 689 public Node node; 690 691 // The nature of the dependency (WITH or AFTER) 692 public int rule; 693 694 public Dependency(Node node, int rule) { 695 this.node = node; 696 this.rule = rule; 697 } 698 } 699 700 /** 701 * A Node is an embodiment of both the Animator that it wraps as well as 702 * any dependencies that are associated with that Animation. This includes 703 * both dependencies upon other nodes (in the dependencies list) as 704 * well as dependencies of other nodes upon this (in the nodeDependents list). 705 */ 706 private static class Node implements Cloneable { 707 public Animator animation; 708 709 /** 710 * These are the dependencies that this node's animation has on other 711 * nodes. For example, if this node's animation should begin with some 712 * other animation ends, then there will be an item in this node's 713 * dependencies list for that other animation's node. 714 */ 715 public ArrayList<Dependency> dependencies = null; 716 717 /** 718 * tmpDependencies is a runtime detail. We use the dependencies list for sorting. 719 * But we also use the list to keep track of when multiple dependencies are satisfied, 720 * but removing each dependency as it is satisfied. We do not want to remove 721 * the dependency itself from the list, because we need to retain that information 722 * if the AnimatorSet is launched in the future. So we create a copy of the dependency 723 * list when the AnimatorSet starts and use this tmpDependencies list to track the 724 * list of satisfied dependencies. 725 */ 726 public ArrayList<Dependency> tmpDependencies = null; 727 728 /** 729 * nodeDependencies is just a list of the nodes that this Node is dependent upon. 730 * This information is used in sortNodes(), to determine when a node is a root. 731 */ 732 public ArrayList<Node> nodeDependencies = null; 733 734 /** 735 * nodeDepdendents is the list of nodes that have this node as a dependency. This 736 * is a utility field used in sortNodes to facilitate removing this node as a 737 * dependency when it is a root node. 738 */ 739 public ArrayList<Node> nodeDependents = null; 740 741 /** 742 * Flag indicating whether the animation in this node is finished. This flag 743 * is used by AnimatorSet to check, as each animation ends, whether all child animations 744 * are done and it's time to send out an end event for the entire AnimatorSet. 745 */ 746 public boolean done = false; 747 748 /** 749 * Constructs the Node with the animation that it encapsulates. A Node has no 750 * dependencies by default; dependencies are added via the addDependency() 751 * method. 752 * 753 * @param animation The animation that the Node encapsulates. 754 */ 755 public Node(Animator animation) { 756 this.animation = animation; 757 } 758 759 /** 760 * Add a dependency to this Node. The dependency includes information about the 761 * node that this node is dependency upon and the nature of the dependency. 762 * @param dependency 763 */ 764 public void addDependency(Dependency dependency) { 765 if (dependencies == null) { 766 dependencies = new ArrayList<Dependency>(); 767 nodeDependencies = new ArrayList<Node>(); 768 } 769 dependencies.add(dependency); 770 if (!nodeDependencies.contains(dependency.node)) { 771 nodeDependencies.add(dependency.node); 772 } 773 Node dependencyNode = dependency.node; 774 if (dependencyNode.nodeDependents == null) { 775 dependencyNode.nodeDependents = new ArrayList<Node>(); 776 } 777 dependencyNode.nodeDependents.add(this); 778 } 779 780 @Override 781 public Node clone() { 782 try { 783 Node node = (Node) super.clone(); 784 node.animation = (Animator) animation.clone(); 785 return node; 786 } catch (CloneNotSupportedException e) { 787 throw new AssertionError(); 788 } 789 } 790 } 791 792 /** 793 * The <code>Builder</code> object is a utility class to facilitate adding animations to a 794 * <code>AnimatorSet</code> along with the relationships between the various animations. The 795 * intention of the <code>Builder</code> methods, along with the {@link 796 * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible to 797 * express the dependency relationships of animations in a natural way. Developers can also use 798 * the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link 799 * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need, 800 * but it might be easier in some situations to express the AnimatorSet of animations in pairs. 801 * <p/> 802 * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed 803 * internally via a call to {@link AnimatorSet#play(Animator)}.</p> 804 * <p/> 805 * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to 806 * play when anim2 finishes, and anim4 to play when anim3 finishes:</p> 807 * <pre> 808 * AnimatorSet s = new AnimatorSet(); 809 * s.play(anim1).with(anim2); 810 * s.play(anim2).before(anim3); 811 * s.play(anim4).after(anim3); 812 * </pre> 813 * <p/> 814 * <p>Note in the example that both {@link Builder#before(Animator)} and {@link 815 * Builder#after(Animator)} are used. These are just different ways of expressing the same 816 * relationship and are provided to make it easier to say things in a way that is more natural, 817 * depending on the situation.</p> 818 * <p/> 819 * <p>It is possible to make several calls into the same <code>Builder</code> object to express 820 * multiple relationships. However, note that it is only the animation passed into the initial 821 * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive 822 * calls to the <code>Builder</code> object. For example, the following code starts both anim2 823 * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and 824 * anim3: 825 * <pre> 826 * AnimatorSet s = new AnimatorSet(); 827 * s.play(anim1).before(anim2).before(anim3); 828 * </pre> 829 * If the desired result is to play anim1 then anim2 then anim3, this code expresses the 830 * relationship correctly:</p> 831 * <pre> 832 * AnimatorSet s = new AnimatorSet(); 833 * s.play(anim1).before(anim2); 834 * s.play(anim2).before(anim3); 835 * </pre> 836 * <p/> 837 * <p>Note that it is possible to express relationships that cannot be resolved and will not 838 * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no 839 * sense. In general, circular dependencies like this one (or more indirect ones where a depends 840 * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets 841 * that can boil down to a simple, one-way relationship of animations starting with, before, and 842 * after other, different, animations.</p> 843 */ 844 public class Builder { 845 846 /** 847 * This tracks the current node being processed. It is supplied to the play() method 848 * of AnimatorSet and passed into the constructor of Builder. 849 */ 850 private Node mCurrentNode; 851 852 /** 853 * package-private constructor. Builders are only constructed by AnimatorSet, when the 854 * play() method is called. 855 * 856 * @param anim The animation that is the dependency for the other animations passed into 857 * the other methods of this Builder object. 858 */ 859 Builder(Animator anim) { 860 mCurrentNode = mNodeMap.get(anim); 861 if (mCurrentNode == null) { 862 mCurrentNode = new Node(anim); 863 mNodeMap.put(anim, mCurrentNode); 864 mNodes.add(mCurrentNode); 865 } 866 } 867 868 /** 869 * Sets up the given animation to play at the same time as the animation supplied in the 870 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object. 871 * 872 * @param anim The animation that will play when the animation supplied to the 873 * {@link AnimatorSet#play(Animator)} method starts. 874 */ 875 public void with(Animator anim) { 876 Node node = mNodeMap.get(anim); 877 if (node == null) { 878 node = new Node(anim); 879 mNodeMap.put(anim, node); 880 mNodes.add(node); 881 } 882 Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH); 883 node.addDependency(dependency); 884 } 885 886 /** 887 * Sets up the given animation to play when the animation supplied in the 888 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 889 * ends. 890 * 891 * @param anim The animation that will play when the animation supplied to the 892 * {@link AnimatorSet#play(Animator)} method ends. 893 */ 894 public void before(Animator anim) { 895 Node node = mNodeMap.get(anim); 896 if (node == null) { 897 node = new Node(anim); 898 mNodeMap.put(anim, node); 899 mNodes.add(node); 900 } 901 Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER); 902 node.addDependency(dependency); 903 } 904 905 /** 906 * Sets up the given animation to play when the animation supplied in the 907 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 908 * to start when the animation supplied in this method call ends. 909 * 910 * @param anim The animation whose end will cause the animation supplied to the 911 * {@link AnimatorSet#play(Animator)} method to play. 912 */ 913 public void after(Animator anim) { 914 Node node = mNodeMap.get(anim); 915 if (node == null) { 916 node = new Node(anim); 917 mNodeMap.put(anim, node); 918 mNodes.add(node); 919 } 920 Dependency dependency = new Dependency(node, Dependency.AFTER); 921 mCurrentNode.addDependency(dependency); 922 } 923 924 /** 925 * Sets up the animation supplied in the 926 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 927 * to play when the given amount of time elapses. 928 * 929 * @param delay The number of milliseconds that should elapse before the 930 * animation starts. 931 */ 932 public void after(long delay) { 933 // setup dummy ValueAnimator just to run the clock 934 after(new ValueAnimator(delay, 0f, 1f)); 935 } 936 937 } 938 939} 940