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