AnimatorSet.java revision 430742f09063574271e6c4091de13b9b9e762514
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.animation; 18 19import java.util.ArrayList; 20import java.util.Collection; 21import java.util.HashMap; 22import java.util.List; 23 24/** 25 * This class plays a set of {@link Animator} objects in the specified order. Animations 26 * can be set up to play together, in sequence, or after a specified delay. 27 * 28 * <p>There are two different approaches to adding animations to a <code>AnimatorSet</code>: 29 * either the {@link AnimatorSet#playTogether(Animator[]) playTogether()} or 30 * {@link AnimatorSet#playSequentially(Animator[]) playSequentially()} methods can be called to add 31 * a set of animations all at once, or the {@link AnimatorSet#play(Animator)} can be 32 * used in conjunction with methods in the {@link AnimatorSet.Builder Builder} 33 * class to add animations 34 * one by one.</p> 35 * 36 * <p>It is possible to set up a <code>AnimatorSet</code> with circular dependencies between 37 * its animations. For example, an animation a1 could be set up to start before animation a2, a2 38 * before a3, and a3 before a1. The results of this configuration are undefined, but will typically 39 * result in none of the affected animations being played. Because of this (and because 40 * circular dependencies do not make logical sense anyway), circular dependencies 41 * should be avoided, and the dependency flow of animations should only be in one direction. 42 * 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 */ 50public 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 251 public void setInterpolator(TimeInterpolator interpolator) { 252 mInterpolator = interpolator; 253 } 254 255 @Override 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 */ 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 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 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 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 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 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 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 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 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 445 public void setupStartValues() { 446 for (Node node : mNodes) { 447 node.animation.setupStartValues(); 448 } 449 } 450 451 @Override 452 public void setupEndValues() { 453 for (Node node : mNodes) { 454 node.animation.setupEndValues(); 455 } 456 } 457 458 /** 459 * {@inheritDoc} 460 * 461 * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which 462 * it is responsible. The details of when exactly those animations are started depends on 463 * the dependency relationships that have been set up between the animations. 464 */ 465 @SuppressWarnings("unchecked") 466 @Override 467 public void start() { 468 mTerminated = false; 469 mStarted = true; 470 471 if (mDuration >= 0) { 472 // If the duration was set on this AnimatorSet, pass it along to all child animations 473 for (Node node : mNodes) { 474 // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to 475 // insert "play-after" delays 476 node.animation.setDuration(mDuration); 477 } 478 } 479 if (mInterpolator != null) { 480 for (Node node : mNodes) { 481 node.animation.setInterpolator(mInterpolator); 482 } 483 } 484 // First, sort the nodes (if necessary). This will ensure that sortedNodes 485 // contains the animation nodes in the correct order. 486 sortNodes(); 487 488 int numSortedNodes = mSortedNodes.size(); 489 for (int i = 0; i < numSortedNodes; ++i) { 490 Node node = mSortedNodes.get(i); 491 // First, clear out the old listeners 492 ArrayList<AnimatorListener> oldListeners = node.animation.getListeners(); 493 if (oldListeners != null && oldListeners.size() > 0) { 494 final ArrayList<AnimatorListener> clonedListeners = new 495 ArrayList<AnimatorListener>(oldListeners); 496 497 for (AnimatorListener listener : clonedListeners) { 498 if (listener instanceof DependencyListener || 499 listener instanceof AnimatorSetListener) { 500 node.animation.removeListener(listener); 501 } 502 } 503 } 504 } 505 506 // nodesToStart holds the list of nodes to be started immediately. We don't want to 507 // start the animations in the loop directly because we first need to set up 508 // dependencies on all of the nodes. For example, we don't want to start an animation 509 // when some other animation also wants to start when the first animation begins. 510 final ArrayList<Node> nodesToStart = new ArrayList<Node>(); 511 for (int i = 0; i < numSortedNodes; ++i) { 512 Node node = mSortedNodes.get(i); 513 if (mSetListener == null) { 514 mSetListener = new AnimatorSetListener(this); 515 } 516 if (node.dependencies == null || node.dependencies.size() == 0) { 517 nodesToStart.add(node); 518 } else { 519 int numDependencies = node.dependencies.size(); 520 for (int j = 0; j < numDependencies; ++j) { 521 Dependency dependency = node.dependencies.get(j); 522 dependency.node.animation.addListener( 523 new DependencyListener(this, node, dependency.rule)); 524 } 525 node.tmpDependencies = (ArrayList<Dependency>) node.dependencies.clone(); 526 } 527 node.animation.addListener(mSetListener); 528 } 529 // Now that all dependencies are set up, start the animations that should be started. 530 if (mStartDelay <= 0) { 531 for (Node node : nodesToStart) { 532 node.animation.start(); 533 mPlayingSet.add(node.animation); 534 } 535 } else { 536 mDelayAnim = ValueAnimator.ofFloat(0f, 1f); 537 mDelayAnim.setDuration(mStartDelay); 538 mDelayAnim.addListener(new AnimatorListenerAdapter() { 539 boolean canceled = false; 540 public void onAnimationCancel(Animator anim) { 541 canceled = true; 542 } 543 public void onAnimationEnd(Animator anim) { 544 if (!canceled) { 545 int numNodes = nodesToStart.size(); 546 for (int i = 0; i < numNodes; ++i) { 547 Node node = nodesToStart.get(i); 548 node.animation.start(); 549 mPlayingSet.add(node.animation); 550 } 551 } 552 } 553 }); 554 mDelayAnim.start(); 555 } 556 if (mListeners != null) { 557 ArrayList<AnimatorListener> tmpListeners = 558 (ArrayList<AnimatorListener>) mListeners.clone(); 559 int numListeners = tmpListeners.size(); 560 for (int i = 0; i < numListeners; ++i) { 561 tmpListeners.get(i).onAnimationStart(this); 562 } 563 } 564 if (mNodes.size() == 0 && mStartDelay == 0) { 565 // Handle unusual case where empty AnimatorSet is started - should send out 566 // end event immediately since the event will not be sent out at all otherwise 567 mStarted = false; 568 if (mListeners != null) { 569 ArrayList<AnimatorListener> tmpListeners = 570 (ArrayList<AnimatorListener>) mListeners.clone(); 571 int numListeners = tmpListeners.size(); 572 for (int i = 0; i < numListeners; ++i) { 573 tmpListeners.get(i).onAnimationEnd(this); 574 } 575 } 576 } 577 } 578 579 @Override 580 public AnimatorSet clone() { 581 final AnimatorSet anim = (AnimatorSet) super.clone(); 582 /* 583 * The basic clone() operation copies all items. This doesn't work very well for 584 * AnimatorSet, because it will copy references that need to be recreated and state 585 * that may not apply. What we need to do now is put the clone in an uninitialized 586 * state, with fresh, empty data structures. Then we will build up the nodes list 587 * manually, as we clone each Node (and its animation). The clone will then be sorted, 588 * and will populate any appropriate lists, when it is started. 589 */ 590 anim.mNeedsSort = true; 591 anim.mTerminated = false; 592 anim.mStarted = false; 593 anim.mPlayingSet = new ArrayList<Animator>(); 594 anim.mNodeMap = new HashMap<Animator, Node>(); 595 anim.mNodes = new ArrayList<Node>(); 596 anim.mSortedNodes = new ArrayList<Node>(); 597 598 // Walk through the old nodes list, cloning each node and adding it to the new nodemap. 599 // One problem is that the old node dependencies point to nodes in the old AnimatorSet. 600 // We need to track the old/new nodes in order to reconstruct the dependencies in the clone. 601 HashMap<Node, Node> nodeCloneMap = new HashMap<Node, Node>(); // <old, new> 602 for (Node node : mNodes) { 603 Node nodeClone = node.clone(); 604 nodeCloneMap.put(node, nodeClone); 605 anim.mNodes.add(nodeClone); 606 anim.mNodeMap.put(nodeClone.animation, nodeClone); 607 // Clear out the dependencies in the clone; we'll set these up manually later 608 nodeClone.dependencies = null; 609 nodeClone.tmpDependencies = null; 610 nodeClone.nodeDependents = null; 611 nodeClone.nodeDependencies = null; 612 // clear out any listeners that were set up by the AnimatorSet; these will 613 // be set up when the clone's nodes are sorted 614 ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners(); 615 if (cloneListeners != null) { 616 ArrayList<AnimatorListener> listenersToRemove = null; 617 for (AnimatorListener listener : cloneListeners) { 618 if (listener instanceof AnimatorSetListener) { 619 if (listenersToRemove == null) { 620 listenersToRemove = new ArrayList<AnimatorListener>(); 621 } 622 listenersToRemove.add(listener); 623 } 624 } 625 if (listenersToRemove != null) { 626 for (AnimatorListener listener : listenersToRemove) { 627 cloneListeners.remove(listener); 628 } 629 } 630 } 631 } 632 // Now that we've cloned all of the nodes, we're ready to walk through their 633 // dependencies, mapping the old dependencies to the new nodes 634 for (Node node : mNodes) { 635 Node nodeClone = nodeCloneMap.get(node); 636 if (node.dependencies != null) { 637 for (Dependency dependency : node.dependencies) { 638 Node clonedDependencyNode = nodeCloneMap.get(dependency.node); 639 Dependency cloneDependency = new Dependency(clonedDependencyNode, 640 dependency.rule); 641 nodeClone.addDependency(cloneDependency); 642 } 643 } 644 } 645 646 return anim; 647 } 648 649 /** 650 * This class is the mechanism by which animations are started based on events in other 651 * animations. If an animation has multiple dependencies on other animations, then 652 * all dependencies must be satisfied before the animation is started. 653 */ 654 private static class DependencyListener implements AnimatorListener { 655 656 private AnimatorSet mAnimatorSet; 657 658 // The node upon which the dependency is based. 659 private Node mNode; 660 661 // The Dependency rule (WITH or AFTER) that the listener should wait for on 662 // the node 663 private int mRule; 664 665 public DependencyListener(AnimatorSet animatorSet, Node node, int rule) { 666 this.mAnimatorSet = animatorSet; 667 this.mNode = node; 668 this.mRule = rule; 669 } 670 671 /** 672 * Ignore cancel events for now. We may want to handle this eventually, 673 * to prevent follow-on animations from running when some dependency 674 * animation is canceled. 675 */ 676 public void onAnimationCancel(Animator animation) { 677 } 678 679 /** 680 * An end event is received - see if this is an event we are listening for 681 */ 682 public void onAnimationEnd(Animator animation) { 683 if (mRule == Dependency.AFTER) { 684 startIfReady(animation); 685 } 686 } 687 688 /** 689 * Ignore repeat events for now 690 */ 691 public void onAnimationRepeat(Animator animation) { 692 } 693 694 /** 695 * A start event is received - see if this is an event we are listening for 696 */ 697 public void onAnimationStart(Animator animation) { 698 if (mRule == Dependency.WITH) { 699 startIfReady(animation); 700 } 701 } 702 703 /** 704 * Check whether the event received is one that the node was waiting for. 705 * If so, mark it as complete and see whether it's time to start 706 * the animation. 707 * @param dependencyAnimation the animation that sent the event. 708 */ 709 private void startIfReady(Animator dependencyAnimation) { 710 if (mAnimatorSet.mTerminated) { 711 // if the parent AnimatorSet was canceled, then don't start any dependent anims 712 return; 713 } 714 Dependency dependencyToRemove = null; 715 int numDependencies = mNode.tmpDependencies.size(); 716 for (int i = 0; i < numDependencies; ++i) { 717 Dependency dependency = mNode.tmpDependencies.get(i); 718 if (dependency.rule == mRule && 719 dependency.node.animation == dependencyAnimation) { 720 // rule fired - remove the dependency and listener and check to 721 // see whether it's time to start the animation 722 dependencyToRemove = dependency; 723 dependencyAnimation.removeListener(this); 724 break; 725 } 726 } 727 mNode.tmpDependencies.remove(dependencyToRemove); 728 if (mNode.tmpDependencies.size() == 0) { 729 // all dependencies satisfied: start the animation 730 mNode.animation.start(); 731 mAnimatorSet.mPlayingSet.add(mNode.animation); 732 } 733 } 734 735 } 736 737 private class AnimatorSetListener implements AnimatorListener { 738 739 private AnimatorSet mAnimatorSet; 740 741 AnimatorSetListener(AnimatorSet animatorSet) { 742 mAnimatorSet = animatorSet; 743 } 744 745 public void onAnimationCancel(Animator animation) { 746 if (!mTerminated) { 747 // Listeners are already notified of the AnimatorSet canceling in cancel(). 748 // The logic below only kicks in when animations end normally 749 if (mPlayingSet.size() == 0) { 750 if (mListeners != null) { 751 int numListeners = mListeners.size(); 752 for (int i = 0; i < numListeners; ++i) { 753 mListeners.get(i).onAnimationCancel(mAnimatorSet); 754 } 755 } 756 } 757 } 758 } 759 760 @SuppressWarnings("unchecked") 761 public void onAnimationEnd(Animator animation) { 762 animation.removeListener(this); 763 mPlayingSet.remove(animation); 764 Node animNode = mAnimatorSet.mNodeMap.get(animation); 765 animNode.done = true; 766 if (!mTerminated) { 767 // Listeners are already notified of the AnimatorSet ending in cancel() or 768 // end(); the logic below only kicks in when animations end normally 769 ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes; 770 boolean allDone = true; 771 int numSortedNodes = sortedNodes.size(); 772 for (int i = 0; i < numSortedNodes; ++i) { 773 if (!sortedNodes.get(i).done) { 774 allDone = false; 775 break; 776 } 777 } 778 if (allDone) { 779 // If this was the last child animation to end, then notify listeners that this 780 // AnimatorSet has ended 781 if (mListeners != null) { 782 ArrayList<AnimatorListener> tmpListeners = 783 (ArrayList<AnimatorListener>) mListeners.clone(); 784 int numListeners = tmpListeners.size(); 785 for (int i = 0; i < numListeners; ++i) { 786 tmpListeners.get(i).onAnimationEnd(mAnimatorSet); 787 } 788 } 789 mAnimatorSet.mStarted = false; 790 } 791 } 792 } 793 794 // Nothing to do 795 public void onAnimationRepeat(Animator animation) { 796 } 797 798 // Nothing to do 799 public void onAnimationStart(Animator animation) { 800 } 801 802 } 803 804 /** 805 * This method sorts the current set of nodes, if needed. The sort is a simple 806 * DependencyGraph sort, which goes like this: 807 * - All nodes without dependencies become 'roots' 808 * - while roots list is not null 809 * - for each root r 810 * - add r to sorted list 811 * - remove r as a dependency from any other node 812 * - any nodes with no dependencies are added to the roots list 813 */ 814 private void sortNodes() { 815 if (mNeedsSort) { 816 mSortedNodes.clear(); 817 ArrayList<Node> roots = new ArrayList<Node>(); 818 int numNodes = mNodes.size(); 819 for (int i = 0; i < numNodes; ++i) { 820 Node node = mNodes.get(i); 821 if (node.dependencies == null || node.dependencies.size() == 0) { 822 roots.add(node); 823 } 824 } 825 ArrayList<Node> tmpRoots = new ArrayList<Node>(); 826 while (roots.size() > 0) { 827 int numRoots = roots.size(); 828 for (int i = 0; i < numRoots; ++i) { 829 Node root = roots.get(i); 830 mSortedNodes.add(root); 831 if (root.nodeDependents != null) { 832 int numDependents = root.nodeDependents.size(); 833 for (int j = 0; j < numDependents; ++j) { 834 Node node = root.nodeDependents.get(j); 835 node.nodeDependencies.remove(root); 836 if (node.nodeDependencies.size() == 0) { 837 tmpRoots.add(node); 838 } 839 } 840 } 841 } 842 roots.clear(); 843 roots.addAll(tmpRoots); 844 tmpRoots.clear(); 845 } 846 mNeedsSort = false; 847 if (mSortedNodes.size() != mNodes.size()) { 848 throw new IllegalStateException("Circular dependencies cannot exist" 849 + " in AnimatorSet"); 850 } 851 } else { 852 // Doesn't need sorting, but still need to add in the nodeDependencies list 853 // because these get removed as the event listeners fire and the dependencies 854 // are satisfied 855 int numNodes = mNodes.size(); 856 for (int i = 0; i < numNodes; ++i) { 857 Node node = mNodes.get(i); 858 if (node.dependencies != null && node.dependencies.size() > 0) { 859 int numDependencies = node.dependencies.size(); 860 for (int j = 0; j < numDependencies; ++j) { 861 Dependency dependency = node.dependencies.get(j); 862 if (node.nodeDependencies == null) { 863 node.nodeDependencies = new ArrayList<Node>(); 864 } 865 if (!node.nodeDependencies.contains(dependency.node)) { 866 node.nodeDependencies.add(dependency.node); 867 } 868 } 869 } 870 // nodes are 'done' by default; they become un-done when started, and done 871 // again when ended 872 node.done = false; 873 } 874 } 875 } 876 877 /** 878 * Dependency holds information about the node that some other node is 879 * dependent upon and the nature of that dependency. 880 * 881 */ 882 private static class Dependency { 883 static final int WITH = 0; // dependent node must start with this dependency node 884 static final int AFTER = 1; // dependent node must start when this dependency node finishes 885 886 // The node that the other node with this Dependency is dependent upon 887 public Node node; 888 889 // The nature of the dependency (WITH or AFTER) 890 public int rule; 891 892 public Dependency(Node node, int rule) { 893 this.node = node; 894 this.rule = rule; 895 } 896 } 897 898 /** 899 * A Node is an embodiment of both the Animator that it wraps as well as 900 * any dependencies that are associated with that Animation. This includes 901 * both dependencies upon other nodes (in the dependencies list) as 902 * well as dependencies of other nodes upon this (in the nodeDependents list). 903 */ 904 private static class Node implements Cloneable { 905 public Animator animation; 906 907 /** 908 * These are the dependencies that this node's animation has on other 909 * nodes. For example, if this node's animation should begin with some 910 * other animation ends, then there will be an item in this node's 911 * dependencies list for that other animation's node. 912 */ 913 public ArrayList<Dependency> dependencies = null; 914 915 /** 916 * tmpDependencies is a runtime detail. We use the dependencies list for sorting. 917 * But we also use the list to keep track of when multiple dependencies are satisfied, 918 * but removing each dependency as it is satisfied. We do not want to remove 919 * the dependency itself from the list, because we need to retain that information 920 * if the AnimatorSet is launched in the future. So we create a copy of the dependency 921 * list when the AnimatorSet starts and use this tmpDependencies list to track the 922 * list of satisfied dependencies. 923 */ 924 public ArrayList<Dependency> tmpDependencies = null; 925 926 /** 927 * nodeDependencies is just a list of the nodes that this Node is dependent upon. 928 * This information is used in sortNodes(), to determine when a node is a root. 929 */ 930 public ArrayList<Node> nodeDependencies = null; 931 932 /** 933 * nodeDepdendents is the list of nodes that have this node as a dependency. This 934 * is a utility field used in sortNodes to facilitate removing this node as a 935 * dependency when it is a root node. 936 */ 937 public ArrayList<Node> nodeDependents = null; 938 939 /** 940 * Flag indicating whether the animation in this node is finished. This flag 941 * is used by AnimatorSet to check, as each animation ends, whether all child animations 942 * are done and it's time to send out an end event for the entire AnimatorSet. 943 */ 944 public boolean done = false; 945 946 /** 947 * Constructs the Node with the animation that it encapsulates. A Node has no 948 * dependencies by default; dependencies are added via the addDependency() 949 * method. 950 * 951 * @param animation The animation that the Node encapsulates. 952 */ 953 public Node(Animator animation) { 954 this.animation = animation; 955 } 956 957 /** 958 * Add a dependency to this Node. The dependency includes information about the 959 * node that this node is dependency upon and the nature of the dependency. 960 * @param dependency 961 */ 962 public void addDependency(Dependency dependency) { 963 if (dependencies == null) { 964 dependencies = new ArrayList<Dependency>(); 965 nodeDependencies = new ArrayList<Node>(); 966 } 967 dependencies.add(dependency); 968 if (!nodeDependencies.contains(dependency.node)) { 969 nodeDependencies.add(dependency.node); 970 } 971 Node dependencyNode = dependency.node; 972 if (dependencyNode.nodeDependents == null) { 973 dependencyNode.nodeDependents = new ArrayList<Node>(); 974 } 975 dependencyNode.nodeDependents.add(this); 976 } 977 978 @Override 979 public Node clone() { 980 try { 981 Node node = (Node) super.clone(); 982 node.animation = (Animator) animation.clone(); 983 return node; 984 } catch (CloneNotSupportedException e) { 985 throw new AssertionError(); 986 } 987 } 988 } 989 990 /** 991 * The <code>Builder</code> object is a utility class to facilitate adding animations to a 992 * <code>AnimatorSet</code> along with the relationships between the various animations. The 993 * intention of the <code>Builder</code> methods, along with the {@link 994 * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible 995 * to express the dependency relationships of animations in a natural way. Developers can also 996 * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link 997 * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need, 998 * but it might be easier in some situations to express the AnimatorSet of animations in pairs. 999 * <p/> 1000 * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed 1001 * internally via a call to {@link AnimatorSet#play(Animator)}.</p> 1002 * <p/> 1003 * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to 1004 * play when anim2 finishes, and anim4 to play when anim3 finishes:</p> 1005 * <pre> 1006 * AnimatorSet s = new AnimatorSet(); 1007 * s.play(anim1).with(anim2); 1008 * s.play(anim2).before(anim3); 1009 * s.play(anim4).after(anim3); 1010 * </pre> 1011 * <p/> 1012 * <p>Note in the example that both {@link Builder#before(Animator)} and {@link 1013 * Builder#after(Animator)} are used. These are just different ways of expressing the same 1014 * relationship and are provided to make it easier to say things in a way that is more natural, 1015 * depending on the situation.</p> 1016 * <p/> 1017 * <p>It is possible to make several calls into the same <code>Builder</code> object to express 1018 * multiple relationships. However, note that it is only the animation passed into the initial 1019 * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive 1020 * calls to the <code>Builder</code> object. For example, the following code starts both anim2 1021 * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and 1022 * anim3: 1023 * <pre> 1024 * AnimatorSet s = new AnimatorSet(); 1025 * s.play(anim1).before(anim2).before(anim3); 1026 * </pre> 1027 * If the desired result is to play anim1 then anim2 then anim3, this code expresses the 1028 * relationship correctly:</p> 1029 * <pre> 1030 * AnimatorSet s = new AnimatorSet(); 1031 * s.play(anim1).before(anim2); 1032 * s.play(anim2).before(anim3); 1033 * </pre> 1034 * <p/> 1035 * <p>Note that it is possible to express relationships that cannot be resolved and will not 1036 * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no 1037 * sense. In general, circular dependencies like this one (or more indirect ones where a depends 1038 * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets 1039 * that can boil down to a simple, one-way relationship of animations starting with, before, and 1040 * after other, different, animations.</p> 1041 */ 1042 public class Builder { 1043 1044 /** 1045 * This tracks the current node being processed. It is supplied to the play() method 1046 * of AnimatorSet and passed into the constructor of Builder. 1047 */ 1048 private Node mCurrentNode; 1049 1050 /** 1051 * package-private constructor. Builders are only constructed by AnimatorSet, when the 1052 * play() method is called. 1053 * 1054 * @param anim The animation that is the dependency for the other animations passed into 1055 * the other methods of this Builder object. 1056 */ 1057 Builder(Animator anim) { 1058 mCurrentNode = mNodeMap.get(anim); 1059 if (mCurrentNode == null) { 1060 mCurrentNode = new Node(anim); 1061 mNodeMap.put(anim, mCurrentNode); 1062 mNodes.add(mCurrentNode); 1063 } 1064 } 1065 1066 /** 1067 * Sets up the given animation to play at the same time as the animation supplied in the 1068 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object. 1069 * 1070 * @param anim The animation that will play when the animation supplied to the 1071 * {@link AnimatorSet#play(Animator)} method starts. 1072 */ 1073 public Builder with(Animator anim) { 1074 Node node = mNodeMap.get(anim); 1075 if (node == null) { 1076 node = new Node(anim); 1077 mNodeMap.put(anim, node); 1078 mNodes.add(node); 1079 } 1080 Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH); 1081 node.addDependency(dependency); 1082 return this; 1083 } 1084 1085 /** 1086 * Sets up the given animation to play when the animation supplied in the 1087 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 1088 * ends. 1089 * 1090 * @param anim The animation that will play when the animation supplied to the 1091 * {@link AnimatorSet#play(Animator)} method ends. 1092 */ 1093 public Builder before(Animator anim) { 1094 Node node = mNodeMap.get(anim); 1095 if (node == null) { 1096 node = new Node(anim); 1097 mNodeMap.put(anim, node); 1098 mNodes.add(node); 1099 } 1100 Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER); 1101 node.addDependency(dependency); 1102 return this; 1103 } 1104 1105 /** 1106 * Sets up the given animation to play when the animation supplied in the 1107 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 1108 * to start when the animation supplied in this method call ends. 1109 * 1110 * @param anim The animation whose end will cause the animation supplied to the 1111 * {@link AnimatorSet#play(Animator)} method to play. 1112 */ 1113 public Builder after(Animator anim) { 1114 Node node = mNodeMap.get(anim); 1115 if (node == null) { 1116 node = new Node(anim); 1117 mNodeMap.put(anim, node); 1118 mNodes.add(node); 1119 } 1120 Dependency dependency = new Dependency(node, Dependency.AFTER); 1121 mCurrentNode.addDependency(dependency); 1122 return this; 1123 } 1124 1125 /** 1126 * Sets up the animation supplied in the 1127 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 1128 * to play when the given amount of time elapses. 1129 * 1130 * @param delay The number of milliseconds that should elapse before the 1131 * animation starts. 1132 */ 1133 public Builder after(long delay) { 1134 // setup dummy ValueAnimator just to run the clock 1135 ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); 1136 anim.setDuration(delay); 1137 after(anim); 1138 return this; 1139 } 1140 1141 } 1142 1143} 1144