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