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