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