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