AnimatorSet.java revision c4bb185d41cfb960ed9a3178a4f8974c351abdb0
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 * AnimatorSet is only reversible when the set contains no sequential animation, and no child 811 * animators have a start delay. 812 * @hide 813 */ 814 @Override 815 public boolean canReverse() { 816 if (!mReversible) { 817 return false; 818 } 819 // Loop to make sure all the Nodes can reverse. 820 int size = mNodes.size(); 821 for (int i = 0; i < size; i++) { 822 Node node = mNodes.get(i); 823 if (!node.mAnimation.canReverse() || node.mAnimation.getStartDelay() > 0) { 824 return false; 825 } 826 } 827 return true; 828 } 829 830 /** 831 * @hide 832 */ 833 @Override 834 public void reverse() { 835 if (canReverse()) { 836 int size = mNodes.size(); 837 for (int i = 0; i < size; i++) { 838 Node node = mNodes.get(i); 839 node.mAnimation.reverse(); 840 } 841 } 842 } 843 844 @Override 845 public String toString() { 846 String returnVal = "AnimatorSet@" + Integer.toHexString(hashCode()) + "{"; 847 int size = mNodes.size(); 848 for (int i = 0; i < size; i++) { 849 Node node = mNodes.get(i); 850 returnVal += "\n " + node.mAnimation.toString(); 851 } 852 return returnVal + "\n}"; 853 } 854 855 private void printChildCount() { 856 // Print out the child count through a level traverse. 857 ArrayList<Node> list = new ArrayList<>(mNodes.size()); 858 list.add(mRootNode); 859 Log.d(TAG, "Current tree: "); 860 int index = 0; 861 while (index < list.size()) { 862 int listSize = list.size(); 863 StringBuilder builder = new StringBuilder(); 864 for (; index < listSize; index++) { 865 Node node = list.get(index); 866 int num = 0; 867 if (node.mChildNodes != null) { 868 for (int i = 0; i < node.mChildNodes.size(); i++) { 869 Node child = node.mChildNodes.get(i); 870 if (child.mLatestParent == node) { 871 num++; 872 list.add(child); 873 } 874 } 875 } 876 builder.append(" "); 877 builder.append(num); 878 } 879 Log.d(TAG, builder.toString()); 880 } 881 } 882 883 private void createDependencyGraph() { 884 if (!mDependencyDirty) { 885 // Check whether any duration of the child animations has changed 886 boolean durationChanged = false; 887 for (int i = 0; i < mNodes.size(); i++) { 888 Animator anim = mNodes.get(i).mAnimation; 889 if (mNodes.get(i).mTotalDuration != anim.getTotalDuration()) { 890 durationChanged = true; 891 break; 892 } 893 } 894 if (!durationChanged) { 895 return; 896 } 897 } 898 899 mDependencyDirty = false; 900 // Traverse all the siblings and make sure they have all the parents 901 int size = mNodes.size(); 902 for (int i = 0; i < size; i++) { 903 mNodes.get(i).mParentsAdded = false; 904 } 905 for (int i = 0; i < size; i++) { 906 Node node = mNodes.get(i); 907 if (node.mParentsAdded) { 908 continue; 909 } 910 911 node.mParentsAdded = true; 912 if (node.mSiblings == null) { 913 continue; 914 } 915 916 // Find all the siblings 917 findSiblings(node, node.mSiblings); 918 node.mSiblings.remove(node); 919 920 // Get parents from all siblings 921 int siblingSize = node.mSiblings.size(); 922 for (int j = 0; j < siblingSize; j++) { 923 node.addParents(node.mSiblings.get(j).mParents); 924 } 925 926 // Now make sure all siblings share the same set of parents 927 for (int j = 0; j < siblingSize; j++) { 928 Node sibling = node.mSiblings.get(j); 929 sibling.addParents(node.mParents); 930 sibling.mParentsAdded = true; 931 } 932 } 933 934 for (int i = 0; i < size; i++) { 935 Node node = mNodes.get(i); 936 if (node != mRootNode && node.mParents == null) { 937 node.addParent(mRootNode); 938 } 939 } 940 941 // Do a DFS on the tree 942 ArrayList<Node> visited = new ArrayList<Node>(mNodes.size()); 943 // Assign start/end time 944 mRootNode.mStartTime = 0; 945 mRootNode.mEndTime = mDelayAnim.getDuration(); 946 updatePlayTime(mRootNode, visited); 947 948 long maxEndTime = 0; 949 for (int i = 0; i < size; i++) { 950 Node node = mNodes.get(i); 951 node.mTotalDuration = node.mAnimation.getTotalDuration(); 952 if (node.mEndTime == DURATION_INFINITE) { 953 maxEndTime = DURATION_INFINITE; 954 break; 955 } else { 956 maxEndTime = node.mEndTime > maxEndTime ? node.mEndTime : maxEndTime; 957 } 958 } 959 mTotalDuration = maxEndTime; 960 } 961 962 /** 963 * Based on parent's start/end time, calculate children's start/end time. If cycle exists in 964 * the graph, all the nodes on the cycle will be marked to start at {@link #DURATION_INFINITE}, 965 * meaning they will ever play. 966 */ 967 private void updatePlayTime(Node parent, ArrayList<Node> visited) { 968 if (parent.mChildNodes == null) { 969 if (parent == mRootNode) { 970 // All the animators are in a cycle 971 for (int i = 0; i < mNodes.size(); i++) { 972 Node node = mNodes.get(i); 973 if (node != mRootNode) { 974 node.mStartTime = DURATION_INFINITE; 975 node.mEndTime = DURATION_INFINITE; 976 } 977 } 978 } 979 return; 980 } 981 982 visited.add(parent); 983 int childrenSize = parent.mChildNodes.size(); 984 for (int i = 0; i < childrenSize; i++) { 985 Node child = parent.mChildNodes.get(i); 986 int index = visited.indexOf(child); 987 if (index >= 0) { 988 // Child has been visited, cycle found. Mark all the nodes in the cycle. 989 for (int j = index; j < visited.size(); j++) { 990 visited.get(j).mLatestParent = null; 991 visited.get(j).mStartTime = DURATION_INFINITE; 992 visited.get(j).mEndTime = DURATION_INFINITE; 993 } 994 child.mStartTime = DURATION_INFINITE; 995 child.mEndTime = DURATION_INFINITE; 996 child.mLatestParent = null; 997 Log.w(TAG, "Cycle found in AnimatorSet: " + this); 998 continue; 999 } 1000 1001 if (child.mStartTime != DURATION_INFINITE) { 1002 if (parent.mEndTime == DURATION_INFINITE) { 1003 child.mLatestParent = parent; 1004 child.mStartTime = DURATION_INFINITE; 1005 child.mEndTime = DURATION_INFINITE; 1006 } else { 1007 if (parent.mEndTime >= child.mStartTime) { 1008 child.mLatestParent = parent; 1009 child.mStartTime = parent.mEndTime; 1010 } 1011 1012 long duration = child.mAnimation.getTotalDuration(); 1013 child.mEndTime = duration == DURATION_INFINITE ? 1014 DURATION_INFINITE : child.mStartTime + duration; 1015 } 1016 } 1017 updatePlayTime(child, visited); 1018 } 1019 visited.remove(parent); 1020 } 1021 1022 // Recursively find all the siblings 1023 private void findSiblings(Node node, ArrayList<Node> siblings) { 1024 if (!siblings.contains(node)) { 1025 siblings.add(node); 1026 if (node.mSiblings == null) { 1027 return; 1028 } 1029 for (int i = 0; i < node.mSiblings.size(); i++) { 1030 findSiblings(node.mSiblings.get(i), siblings); 1031 } 1032 } 1033 } 1034 1035 /** 1036 * @hide 1037 * TODO: For animatorSet defined in XML, we can use a flag to indicate what the play order 1038 * if defined (i.e. sequential or together), then we can use the flag instead of calculate 1039 * dynamically. 1040 * @return whether all the animators in the set are supposed to play together 1041 */ 1042 public boolean shouldPlayTogether() { 1043 updateAnimatorsDuration(); 1044 createDependencyGraph(); 1045 // All the child nodes are set out to play right after the delay animation 1046 return mRootNode.mChildNodes.size() == mNodes.size() - 1; 1047 } 1048 1049 @Override 1050 public long getTotalDuration() { 1051 updateAnimatorsDuration(); 1052 createDependencyGraph(); 1053 return mTotalDuration; 1054 } 1055 1056 private Node getNodeForAnimation(Animator anim) { 1057 Node node = mNodeMap.get(anim); 1058 if (node == null) { 1059 node = new Node(anim); 1060 mNodeMap.put(anim, node); 1061 mNodes.add(node); 1062 } 1063 return node; 1064 } 1065 1066 /** 1067 * A Node is an embodiment of both the Animator that it wraps as well as 1068 * any dependencies that are associated with that Animation. This includes 1069 * both dependencies upon other nodes (in the dependencies list) as 1070 * well as dependencies of other nodes upon this (in the nodeDependents list). 1071 */ 1072 private static class Node implements Cloneable { 1073 Animator mAnimation; 1074 1075 /** 1076 * Child nodes are the nodes associated with animations that will be played immediately 1077 * after current node. 1078 */ 1079 ArrayList<Node> mChildNodes = null; 1080 1081 /** 1082 * Temporary field to hold the clone in AnimatorSet#clone. Cleaned after clone is complete 1083 */ 1084 private Node mTmpClone = null; 1085 1086 /** 1087 * Flag indicating whether the animation in this node is finished. This flag 1088 * is used by AnimatorSet to check, as each animation ends, whether all child animations 1089 * are mEnded and it's time to send out an end event for the entire AnimatorSet. 1090 */ 1091 boolean mEnded = false; 1092 1093 /** 1094 * Nodes with animations that are defined to play simultaneously with the animation 1095 * associated with this current node. 1096 */ 1097 ArrayList<Node> mSiblings; 1098 1099 /** 1100 * Parent nodes are the nodes with animations preceding current node's animation. Parent 1101 * nodes here are derived from user defined animation sequence. 1102 */ 1103 ArrayList<Node> mParents; 1104 1105 /** 1106 * Latest parent is the parent node associated with a animation that finishes after all 1107 * the other parents' animations. 1108 */ 1109 Node mLatestParent = null; 1110 1111 boolean mParentsAdded = false; 1112 long mStartTime = 0; 1113 long mEndTime = 0; 1114 long mTotalDuration = 0; 1115 1116 /** 1117 * Constructs the Node with the animation that it encapsulates. A Node has no 1118 * dependencies by default; dependencies are added via the addDependency() 1119 * method. 1120 * 1121 * @param animation The animation that the Node encapsulates. 1122 */ 1123 public Node(Animator animation) { 1124 this.mAnimation = animation; 1125 } 1126 1127 @Override 1128 public Node clone() { 1129 try { 1130 Node node = (Node) super.clone(); 1131 node.mAnimation = mAnimation.clone(); 1132 if (mChildNodes != null) { 1133 node.mChildNodes = new ArrayList<>(mChildNodes); 1134 } 1135 if (mSiblings != null) { 1136 node.mSiblings = new ArrayList<>(mSiblings); 1137 } 1138 if (mParents != null) { 1139 node.mParents = new ArrayList<>(mParents); 1140 } 1141 node.mEnded = false; 1142 return node; 1143 } catch (CloneNotSupportedException e) { 1144 throw new AssertionError(); 1145 } 1146 } 1147 1148 void addChild(Node node) { 1149 if (mChildNodes == null) { 1150 mChildNodes = new ArrayList<>(); 1151 } 1152 if (!mChildNodes.contains(node)) { 1153 mChildNodes.add(node); 1154 node.addParent(this); 1155 } 1156 } 1157 1158 public void addSibling(Node node) { 1159 if (mSiblings == null) { 1160 mSiblings = new ArrayList<Node>(); 1161 } 1162 if (!mSiblings.contains(node)) { 1163 mSiblings.add(node); 1164 node.addSibling(this); 1165 } 1166 } 1167 1168 public void addParent(Node node) { 1169 if (mParents == null) { 1170 mParents = new ArrayList<Node>(); 1171 } 1172 if (!mParents.contains(node)) { 1173 mParents.add(node); 1174 node.addChild(this); 1175 } 1176 } 1177 1178 public void addParents(ArrayList<Node> parents) { 1179 if (parents == null) { 1180 return; 1181 } 1182 int size = parents.size(); 1183 for (int i = 0; i < size; i++) { 1184 addParent(parents.get(i)); 1185 } 1186 } 1187 } 1188 1189 /** 1190 * The <code>Builder</code> object is a utility class to facilitate adding animations to a 1191 * <code>AnimatorSet</code> along with the relationships between the various animations. The 1192 * intention of the <code>Builder</code> methods, along with the {@link 1193 * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible 1194 * to express the dependency relationships of animations in a natural way. Developers can also 1195 * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link 1196 * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need, 1197 * but it might be easier in some situations to express the AnimatorSet of animations in pairs. 1198 * <p/> 1199 * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed 1200 * internally via a call to {@link AnimatorSet#play(Animator)}.</p> 1201 * <p/> 1202 * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to 1203 * play when anim2 finishes, and anim4 to play when anim3 finishes:</p> 1204 * <pre> 1205 * AnimatorSet s = new AnimatorSet(); 1206 * s.play(anim1).with(anim2); 1207 * s.play(anim2).before(anim3); 1208 * s.play(anim4).after(anim3); 1209 * </pre> 1210 * <p/> 1211 * <p>Note in the example that both {@link Builder#before(Animator)} and {@link 1212 * Builder#after(Animator)} are used. These are just different ways of expressing the same 1213 * relationship and are provided to make it easier to say things in a way that is more natural, 1214 * depending on the situation.</p> 1215 * <p/> 1216 * <p>It is possible to make several calls into the same <code>Builder</code> object to express 1217 * multiple relationships. However, note that it is only the animation passed into the initial 1218 * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive 1219 * calls to the <code>Builder</code> object. For example, the following code starts both anim2 1220 * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and 1221 * anim3: 1222 * <pre> 1223 * AnimatorSet s = new AnimatorSet(); 1224 * s.play(anim1).before(anim2).before(anim3); 1225 * </pre> 1226 * If the desired result is to play anim1 then anim2 then anim3, this code expresses the 1227 * relationship correctly:</p> 1228 * <pre> 1229 * AnimatorSet s = new AnimatorSet(); 1230 * s.play(anim1).before(anim2); 1231 * s.play(anim2).before(anim3); 1232 * </pre> 1233 * <p/> 1234 * <p>Note that it is possible to express relationships that cannot be resolved and will not 1235 * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no 1236 * sense. In general, circular dependencies like this one (or more indirect ones where a depends 1237 * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets 1238 * that can boil down to a simple, one-way relationship of animations starting with, before, and 1239 * after other, different, animations.</p> 1240 */ 1241 public class Builder { 1242 1243 /** 1244 * This tracks the current node being processed. It is supplied to the play() method 1245 * of AnimatorSet and passed into the constructor of Builder. 1246 */ 1247 private Node mCurrentNode; 1248 1249 /** 1250 * package-private constructor. Builders are only constructed by AnimatorSet, when the 1251 * play() method is called. 1252 * 1253 * @param anim The animation that is the dependency for the other animations passed into 1254 * the other methods of this Builder object. 1255 */ 1256 Builder(Animator anim) { 1257 mDependencyDirty = true; 1258 mCurrentNode = getNodeForAnimation(anim); 1259 } 1260 1261 /** 1262 * Sets up the given animation to play at the same time as the animation supplied in the 1263 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object. 1264 * 1265 * @param anim The animation that will play when the animation supplied to the 1266 * {@link AnimatorSet#play(Animator)} method starts. 1267 */ 1268 public Builder with(Animator anim) { 1269 Node node = getNodeForAnimation(anim); 1270 mCurrentNode.addSibling(node); 1271 return this; 1272 } 1273 1274 /** 1275 * Sets up the given animation to play when the animation supplied in the 1276 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 1277 * ends. 1278 * 1279 * @param anim The animation that will play when the animation supplied to the 1280 * {@link AnimatorSet#play(Animator)} method ends. 1281 */ 1282 public Builder before(Animator anim) { 1283 mReversible = false; 1284 Node node = getNodeForAnimation(anim); 1285 mCurrentNode.addChild(node); 1286 return this; 1287 } 1288 1289 /** 1290 * Sets up the given animation to play when the animation supplied in the 1291 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 1292 * to start when the animation supplied in this method call ends. 1293 * 1294 * @param anim The animation whose end will cause the animation supplied to the 1295 * {@link AnimatorSet#play(Animator)} method to play. 1296 */ 1297 public Builder after(Animator anim) { 1298 mReversible = false; 1299 Node node = getNodeForAnimation(anim); 1300 mCurrentNode.addParent(node); 1301 return this; 1302 } 1303 1304 /** 1305 * Sets up the animation supplied in the 1306 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 1307 * to play when the given amount of time elapses. 1308 * 1309 * @param delay The number of milliseconds that should elapse before the 1310 * animation starts. 1311 */ 1312 public Builder after(long delay) { 1313 // setup dummy ValueAnimator just to run the clock 1314 ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); 1315 anim.setDuration(delay); 1316 after(anim); 1317 return this; 1318 } 1319 1320 } 1321 1322} 1323