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