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