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