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