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