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