AnimatorSet.java revision dbf69e4652d439892080dff3353b1cfd5b4bf6bc
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. 412 * @return Whether this AnimatorSet has been started and has not yet ended. 413 */ 414 @Override 415 public boolean isRunning() { 416 int size = mNodes.size(); 417 for (int i = 0; i < size; i++) { 418 if (mNodes.get(i).mAnimation.isRunning()) { 419 return true; 420 } 421 } 422 return false; 423 } 424 425 @Override 426 public boolean isStarted() { 427 return mStarted; 428 } 429 430 /** 431 * The amount of time, in milliseconds, to delay starting the animation after 432 * {@link #start()} is called. 433 * 434 * @return the number of milliseconds to delay running the animation 435 */ 436 @Override 437 public long getStartDelay() { 438 return mStartDelay; 439 } 440 441 /** 442 * The amount of time, in milliseconds, to delay starting the animation after 443 * {@link #start()} is called. 444 445 * @param startDelay The amount of the delay, in milliseconds 446 */ 447 @Override 448 public void setStartDelay(long startDelay) { 449 if (mStartDelay > 0) { 450 mReversible = false; 451 } 452 long delta = startDelay - mStartDelay; 453 mStartDelay = startDelay; 454 if (!mDependencyDirty) { 455 // Dependency graph already constructed, update all the nodes' start/end time 456 int size = mNodes.size(); 457 for (int i = 0; i < size; i++) { 458 Node node = mNodes.get(i); 459 if (node == mRootNode) { 460 node.mEndTime = mStartDelay; 461 } else { 462 node.mStartTime = node.mStartTime == DURATION_INFINITE ? 463 DURATION_INFINITE : node.mStartTime + delta; 464 node.mEndTime = node.mEndTime == DURATION_INFINITE ? 465 DURATION_INFINITE : node.mEndTime + delta; 466 467 } 468 } 469 } 470 } 471 472 /** 473 * Gets the length of each of the child animations of this AnimatorSet. This value may 474 * be less than 0, which indicates that no duration has been set on this AnimatorSet 475 * and each of the child animations will use their own duration. 476 * 477 * @return The length of the animation, in milliseconds, of each of the child 478 * animations of this AnimatorSet. 479 */ 480 @Override 481 public long getDuration() { 482 return mDuration; 483 } 484 485 /** 486 * Sets the length of each of the current child animations of this AnimatorSet. By default, 487 * each child animation will use its own duration. If the duration is set on the AnimatorSet, 488 * then each child animation inherits this duration. 489 * 490 * @param duration The length of the animation, in milliseconds, of each of the child 491 * animations of this AnimatorSet. 492 */ 493 @Override 494 public AnimatorSet setDuration(long duration) { 495 if (duration < 0) { 496 throw new IllegalArgumentException("duration must be a value of zero or greater"); 497 } 498 mDependencyDirty = true; 499 // Just record the value for now - it will be used later when the AnimatorSet starts 500 mDuration = duration; 501 return this; 502 } 503 504 @Override 505 public void setupStartValues() { 506 int size = mNodes.size(); 507 for (int i = 0; i < size; i++) { 508 Node node = mNodes.get(i); 509 node.mAnimation.setupStartValues(); 510 } 511 } 512 513 @Override 514 public void setupEndValues() { 515 int size = mNodes.size(); 516 for (int i = 0; i < size; i++) { 517 Node node = mNodes.get(i); 518 node.mAnimation.setupEndValues(); 519 } 520 } 521 522 @Override 523 public void pause() { 524 boolean previouslyPaused = mPaused; 525 super.pause(); 526 if (!previouslyPaused && mPaused) { 527 if (mDelayAnim != null) { 528 mDelayAnim.pause(); 529 } else { 530 int size = mNodes.size(); 531 for (int i = 0; i < size; i++) { 532 Node node = mNodes.get(i); 533 node.mAnimation.pause(); 534 } 535 } 536 } 537 } 538 539 @Override 540 public void resume() { 541 boolean previouslyPaused = mPaused; 542 super.resume(); 543 if (previouslyPaused && !mPaused) { 544 if (mDelayAnim != null) { 545 mDelayAnim.resume(); 546 } else { 547 int size = mNodes.size(); 548 for (int i = 0; i < size; i++) { 549 Node node = mNodes.get(i); 550 node.mAnimation.resume(); 551 } 552 } 553 } 554 } 555 556 /** 557 * {@inheritDoc} 558 * 559 * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which 560 * it is responsible. The details of when exactly those animations are started depends on 561 * the dependency relationships that have been set up between the animations. 562 */ 563 @SuppressWarnings("unchecked") 564 @Override 565 public void start() { 566 mTerminated = false; 567 mStarted = true; 568 mPaused = false; 569 570 int size = mNodes.size(); 571 for (int i = 0; i < size; i++) { 572 Node node = mNodes.get(i); 573 node.mEnded = false; 574 node.mAnimation.setAllowRunningAsynchronously(false); 575 } 576 577 if (mInterpolator != null) { 578 for (int i = 0; i < size; i++) { 579 Node node = mNodes.get(i); 580 node.mAnimation.setInterpolator(mInterpolator); 581 } 582 } 583 584 updateAnimatorsDuration(); 585 createDependencyGraph(); 586 587 // Now that all dependencies are set up, start the animations that should be started. 588 start(mRootNode); 589 if (mListeners != null) { 590 ArrayList<AnimatorListener> tmpListeners = 591 (ArrayList<AnimatorListener>) mListeners.clone(); 592 int numListeners = tmpListeners.size(); 593 for (int i = 0; i < numListeners; ++i) { 594 tmpListeners.get(i).onAnimationStart(this); 595 } 596 } 597 if (mNodes.size() == 0 && mStartDelay == 0) { 598 // Handle unusual case where empty AnimatorSet is started - should send out 599 // end event immediately since the event will not be sent out at all otherwise 600 mStarted = false; 601 if (mListeners != null) { 602 ArrayList<AnimatorListener> tmpListeners = 603 (ArrayList<AnimatorListener>) mListeners.clone(); 604 int numListeners = tmpListeners.size(); 605 for (int i = 0; i < numListeners; ++i) { 606 tmpListeners.get(i).onAnimationEnd(this); 607 } 608 } 609 } 610 } 611 612 private void updateAnimatorsDuration() { 613 if (mDuration >= 0) { 614 // If the duration was set on this AnimatorSet, pass it along to all child animations 615 int size = mNodes.size(); 616 for (int i = 0; i < size; i++) { 617 Node node = mNodes.get(i); 618 // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to 619 // insert "play-after" delays 620 node.mAnimation.setDuration(mDuration); 621 } 622 } 623 mDelayAnim.setDuration(mStartDelay); 624 } 625 626 void start(final Node node) { 627 final Animator anim = node.mAnimation; 628 mPlayingSet.add(anim); 629 anim.addListener(mSetListener); 630 anim.start(); 631 } 632 633 @Override 634 public AnimatorSet clone() { 635 final AnimatorSet anim = (AnimatorSet) super.clone(); 636 /* 637 * The basic clone() operation copies all items. This doesn't work very well for 638 * AnimatorSet, because it will copy references that need to be recreated and state 639 * that may not apply. What we need to do now is put the clone in an uninitialized 640 * state, with fresh, empty data structures. Then we will build up the nodes list 641 * manually, as we clone each Node (and its animation). The clone will then be sorted, 642 * and will populate any appropriate lists, when it is started. 643 */ 644 final int nodeCount = mNodes.size(); 645 anim.mTerminated = false; 646 anim.mStarted = false; 647 anim.mPlayingSet = new ArrayList<Animator>(); 648 anim.mNodeMap = new ArrayMap<Animator, Node>(); 649 anim.mNodes = new ArrayList<Node>(nodeCount); 650 anim.mReversible = mReversible; 651 anim.mSetListener = new AnimatorSetListener(anim); 652 653 // Walk through the old nodes list, cloning each node and adding it to the new nodemap. 654 // One problem is that the old node dependencies point to nodes in the old AnimatorSet. 655 // We need to track the old/new nodes in order to reconstruct the dependencies in the clone. 656 657 for (int n = 0; n < nodeCount; n++) { 658 final Node node = mNodes.get(n); 659 Node nodeClone = node.clone(); 660 node.mTmpClone = nodeClone; 661 anim.mNodes.add(nodeClone); 662 anim.mNodeMap.put(nodeClone.mAnimation, nodeClone); 663 664 // clear out any listeners that were set up by the AnimatorSet 665 final ArrayList<AnimatorListener> cloneListeners = nodeClone.mAnimation.getListeners(); 666 if (cloneListeners != null) { 667 for (int i = cloneListeners.size() - 1; i >= 0; i--) { 668 final AnimatorListener listener = cloneListeners.get(i); 669 if (listener instanceof AnimatorSetListener) { 670 cloneListeners.remove(i); 671 } 672 } 673 } 674 } 675 676 anim.mRootNode = mRootNode.mTmpClone; 677 anim.mDelayAnim = (ValueAnimator) anim.mRootNode.mAnimation; 678 679 // Now that we've cloned all of the nodes, we're ready to walk through their 680 // dependencies, mapping the old dependencies to the new nodes 681 for (int i = 0; i < nodeCount; i++) { 682 Node node = mNodes.get(i); 683 // Update dependencies for node's clone 684 node.mTmpClone.mLatestParent = node.mLatestParent == null ? 685 null : node.mLatestParent.mTmpClone; 686 int size = node.mChildNodes == null ? 0 : node.mChildNodes.size(); 687 for (int j = 0; j < size; j++) { 688 node.mTmpClone.mChildNodes.set(j, node.mChildNodes.get(j).mTmpClone); 689 } 690 size = node.mSiblings == null ? 0 : node.mSiblings.size(); 691 for (int j = 0; j < size; j++) { 692 node.mTmpClone.mSiblings.set(j, node.mSiblings.get(j).mTmpClone); 693 } 694 size = node.mParents == null ? 0 : node.mParents.size(); 695 for (int j = 0; j < size; j++) { 696 node.mTmpClone.mParents.set(j, node.mParents.get(j).mTmpClone); 697 } 698 } 699 700 for (int n = 0; n < nodeCount; n++) { 701 mNodes.get(n).mTmpClone = null; 702 } 703 return anim; 704 } 705 706 707 private class AnimatorSetListener implements AnimatorListener { 708 709 private AnimatorSet mAnimatorSet; 710 711 AnimatorSetListener(AnimatorSet animatorSet) { 712 mAnimatorSet = animatorSet; 713 } 714 715 public void onAnimationCancel(Animator animation) { 716 717 if (!mTerminated) { 718 // Listeners are already notified of the AnimatorSet canceling in cancel(). 719 // The logic below only kicks in when animations end normally 720 if (mAnimatorSet.mPlayingSet.size() == 0) { 721 ArrayList<AnimatorListener> listeners = mAnimatorSet.mListeners; 722 if (listeners != null) { 723 int numListeners = listeners.size(); 724 for (int i = 0; i < numListeners; ++i) { 725 listeners.get(i).onAnimationCancel(mAnimatorSet); 726 } 727 } 728 } 729 } 730 } 731 732 @SuppressWarnings("unchecked") 733 public void onAnimationEnd(Animator animation) { 734 animation.removeListener(this); 735 mAnimatorSet.mPlayingSet.remove(animation); 736 Node animNode = mAnimatorSet.mNodeMap.get(animation); 737 animNode.mEnded = true; 738 739 if (!mTerminated) { 740 List<Node> children = animNode.mChildNodes; 741 // Start children animations, if any. 742 int childrenSize = children == null ? 0 : children.size(); 743 for (int i = 0; i < childrenSize; i++) { 744 if (children.get(i).mLatestParent == animNode) { 745 mAnimatorSet.start(children.get(i)); 746 } 747 } 748 // Listeners are already notified of the AnimatorSet ending in cancel() or 749 // end(); the logic below only kicks in when animations end normally 750 boolean allDone = true; 751 // Traverse the tree and find if there's any unfinished node 752 int size = mNodes.size(); 753 for (int i = 0; i < size; i++) { 754 if (!mNodes.get(i).mEnded) { 755 allDone = false; 756 break; 757 } 758 } 759 if (allDone) { 760 // If this was the last child animation to end, then notify listeners that this 761 // AnimatorSet has ended 762 if (mListeners != null) { 763 ArrayList<AnimatorListener> tmpListeners = 764 (ArrayList<AnimatorListener>) mListeners.clone(); 765 int numListeners = tmpListeners.size(); 766 for (int i = 0; i < numListeners; ++i) { 767 tmpListeners.get(i).onAnimationEnd(mAnimatorSet); 768 } 769 } 770 mAnimatorSet.mStarted = false; 771 mAnimatorSet.mPaused = false; 772 } 773 } 774 } 775 776 // Nothing to do 777 public void onAnimationRepeat(Animator animation) { 778 } 779 780 // Nothing to do 781 public void onAnimationStart(Animator animation) { 782 } 783 784 } 785 786 /** 787 * @hide 788 */ 789 @Override 790 public boolean canReverse() { 791 if (!mReversible) { 792 return false; 793 } 794 // Loop to make sure all the Nodes can reverse. 795 int size = mNodes.size(); 796 for (int i = 0; i < size; i++) { 797 Node node = mNodes.get(i); 798 if (!node.mAnimation.canReverse() || node.mAnimation.getStartDelay() > 0) { 799 return false; 800 } 801 } 802 return true; 803 } 804 805 /** 806 * @hide 807 */ 808 @Override 809 public void reverse() { 810 if (canReverse()) { 811 int size = mNodes.size(); 812 for (int i = 0; i < size; i++) { 813 Node node = mNodes.get(i); 814 node.mAnimation.reverse(); 815 } 816 } 817 } 818 819 @Override 820 public String toString() { 821 String returnVal = "AnimatorSet@" + Integer.toHexString(hashCode()) + "{"; 822 int size = mNodes.size(); 823 for (int i = 0; i < size; i++) { 824 Node node = mNodes.get(i); 825 returnVal += "\n " + node.mAnimation.toString(); 826 } 827 return returnVal + "\n}"; 828 } 829 830 private void printChildCount() { 831 // Print out the child count through a level traverse. 832 ArrayList<Node> list = new ArrayList<>(mNodes.size()); 833 list.add(mRootNode); 834 Log.d(TAG, "Current tree: "); 835 int index = 0; 836 while (index < list.size()) { 837 int listSize = list.size(); 838 StringBuilder builder = new StringBuilder(); 839 for (; index < listSize; index++) { 840 Node node = list.get(index); 841 int num = 0; 842 if (node.mChildNodes != null) { 843 for (int i = 0; i < node.mChildNodes.size(); i++) { 844 Node child = node.mChildNodes.get(i); 845 if (child.mLatestParent == node) { 846 num++; 847 list.add(child); 848 } 849 } 850 } 851 builder.append(" "); 852 builder.append(num); 853 } 854 Log.d(TAG, builder.toString()); 855 } 856 } 857 858 private void createDependencyGraph() { 859 if (!mDependencyDirty) { 860 return; 861 } 862 863 // TODO: In addition to checking the dirty flag, we should also cache the duration for 864 // each animator, so that when the animator's duration is changed, we can detect that and 865 // update the dependency graph. 866 867 mDependencyDirty = false; 868 // Traverse all the siblings and make sure they have all the parents 869 int size = mNodes.size(); 870 for (int i = 0; i < size; i++) { 871 mNodes.get(i).mParentsAdded = false; 872 } 873 for (int i = 0; i < size; i++) { 874 Node node = mNodes.get(i); 875 if (node.mParentsAdded) { 876 continue; 877 } 878 879 node.mParentsAdded = true; 880 if (node.mSiblings == null) { 881 continue; 882 } 883 884 // Find all the siblings 885 findSiblings(node, node.mSiblings); 886 node.mSiblings.remove(node); 887 888 // Get parents from all siblings 889 int siblingSize = node.mSiblings.size(); 890 for (int j = 0; j < siblingSize; j++) { 891 node.addParents(node.mSiblings.get(j).mParents); 892 } 893 894 // Now make sure all siblings share the same set of parents 895 for (int j = 0; j < siblingSize; j++) { 896 Node sibling = node.mSiblings.get(j); 897 sibling.addParents(node.mParents); 898 sibling.mParentsAdded = true; 899 } 900 } 901 902 for (int i = 0; i < size; i++) { 903 Node node = mNodes.get(i); 904 if (node != mRootNode && node.mParents == null) { 905 node.addParent(mRootNode); 906 } 907 } 908 909 // Do a DFS on the tree 910 ArrayList<Node> visited = new ArrayList<Node>(mNodes.size()); 911 // Assign start/end time 912 mRootNode.mStartTime = 0; 913 mRootNode.mEndTime = mDelayAnim.getDuration(); 914 updatePlayTime(mRootNode, visited); 915 916 long maxEndTime = 0; 917 for (int i = 0; i < size; i++) { 918 Node node = mNodes.get(i); 919 if (node.mEndTime == DURATION_INFINITE) { 920 maxEndTime = DURATION_INFINITE; 921 break; 922 } else { 923 maxEndTime = node.mEndTime > maxEndTime ? node.mEndTime : maxEndTime; 924 } 925 } 926 mTotalDuration = maxEndTime; 927 } 928 929 /** 930 * Based on parent's start/end time, calculate children's start/end time. If cycle exists in 931 * the graph, all the nodes on the cycle will be marked to start at {@link #DURATION_INFINITE}, 932 * meaning they will ever play. 933 */ 934 private void updatePlayTime(Node parent, ArrayList<Node> visited) { 935 if (parent.mChildNodes == null) { 936 return; 937 } 938 939 visited.add(parent); 940 int childrenSize = parent.mChildNodes.size(); 941 for (int i = 0; i < childrenSize; i++) { 942 Node child = parent.mChildNodes.get(i); 943 int index = visited.indexOf(child); 944 if (index >= 0) { 945 // Child has been visited, cycle found. Mark all the nodes in the cycle. 946 for (int j = index; j < visited.size(); i++) { 947 visited.get(j).mLatestParent = null; 948 visited.get(j).mStartTime = DURATION_INFINITE; 949 visited.get(j).mEndTime = DURATION_INFINITE; 950 } 951 child.mStartTime = DURATION_INFINITE; 952 child.mEndTime = DURATION_INFINITE; 953 child.mLatestParent = null; 954 Log.w(TAG, "Cycle found in AnimatorSet: " + this); 955 continue; 956 } 957 958 if (child.mStartTime != DURATION_INFINITE) { 959 if (parent.mEndTime == DURATION_INFINITE) { 960 child.mLatestParent = parent; 961 child.mStartTime = DURATION_INFINITE; 962 child.mEndTime = DURATION_INFINITE; 963 } else { 964 if (parent.mEndTime >= child.mStartTime) { 965 child.mLatestParent = parent; 966 child.mStartTime = parent.mEndTime; 967 } 968 969 long duration = child.mAnimation.getTotalDuration(); 970 child.mEndTime = duration == DURATION_INFINITE ? 971 DURATION_INFINITE : child.mStartTime + duration; 972 } 973 } 974 updatePlayTime(child, visited); 975 } 976 visited.remove(parent); 977 } 978 979 // Recursively find all the siblings 980 private void findSiblings(Node node, ArrayList<Node> siblings) { 981 if (!siblings.contains(node)) { 982 siblings.add(node); 983 if (node.mSiblings == null) { 984 return; 985 } 986 for (int i = 0; i < node.mSiblings.size(); i++) { 987 findSiblings(node.mSiblings.get(i), siblings); 988 } 989 } 990 } 991 992 /** 993 * @hide 994 */ 995 @Override 996 public long getTotalDuration() { 997 updateAnimatorsDuration(); 998 createDependencyGraph(); 999 return mTotalDuration; 1000 } 1001 1002 private Node getNodeForAnimation(Animator anim) { 1003 Node node = mNodeMap.get(anim); 1004 if (node == null) { 1005 node = new Node(anim); 1006 mNodeMap.put(anim, node); 1007 mNodes.add(node); 1008 } 1009 return node; 1010 } 1011 1012 /** 1013 * A Node is an embodiment of both the Animator that it wraps as well as 1014 * any dependencies that are associated with that Animation. This includes 1015 * both dependencies upon other nodes (in the dependencies list) as 1016 * well as dependencies of other nodes upon this (in the nodeDependents list). 1017 */ 1018 private static class Node implements Cloneable { 1019 Animator mAnimation; 1020 1021 /** 1022 * Child nodes are the nodes associated with animations that will be played immediately 1023 * after current node. 1024 */ 1025 ArrayList<Node> mChildNodes = null; 1026 1027 /** 1028 * Temporary field to hold the clone in AnimatorSet#clone. Cleaned after clone is complete 1029 */ 1030 private Node mTmpClone = null; 1031 1032 /** 1033 * Flag indicating whether the animation in this node is finished. This flag 1034 * is used by AnimatorSet to check, as each animation ends, whether all child animations 1035 * are mEnded and it's time to send out an end event for the entire AnimatorSet. 1036 */ 1037 boolean mEnded = false; 1038 1039 /** 1040 * Nodes with animations that are defined to play simultaneously with the animation 1041 * associated with this current node. 1042 */ 1043 ArrayList<Node> mSiblings; 1044 1045 /** 1046 * Parent nodes are the nodes with animations preceding current node's animation. Parent 1047 * nodes here are derived from user defined animation sequence. 1048 */ 1049 ArrayList<Node> mParents; 1050 1051 /** 1052 * Latest parent is the parent node associated with a animation that finishes after all 1053 * the other parents' animations. 1054 */ 1055 Node mLatestParent = null; 1056 1057 boolean mParentsAdded = false; 1058 long mStartTime = 0; 1059 long mEndTime = 0; 1060 1061 /** 1062 * Constructs the Node with the animation that it encapsulates. A Node has no 1063 * dependencies by default; dependencies are added via the addDependency() 1064 * method. 1065 * 1066 * @param animation The animation that the Node encapsulates. 1067 */ 1068 public Node(Animator animation) { 1069 this.mAnimation = animation; 1070 } 1071 1072 @Override 1073 public Node clone() { 1074 try { 1075 Node node = (Node) super.clone(); 1076 node.mAnimation = mAnimation.clone(); 1077 if (mChildNodes != null) { 1078 node.mChildNodes = new ArrayList<>(mChildNodes); 1079 } 1080 if (mSiblings != null) { 1081 node.mSiblings = new ArrayList<>(mSiblings); 1082 } 1083 if (mParents != null) { 1084 node.mParents = new ArrayList<>(mParents); 1085 } 1086 node.mEnded = false; 1087 return node; 1088 } catch (CloneNotSupportedException e) { 1089 throw new AssertionError(); 1090 } 1091 } 1092 1093 void addChild(Node node) { 1094 if (mChildNodes == null) { 1095 mChildNodes = new ArrayList<>(); 1096 } 1097 if (!mChildNodes.contains(node)) { 1098 mChildNodes.add(node); 1099 node.addParent(this); 1100 } 1101 } 1102 1103 public void addSibling(Node node) { 1104 if (mSiblings == null) { 1105 mSiblings = new ArrayList<Node>(); 1106 } 1107 if (!mSiblings.contains(node)) { 1108 mSiblings.add(node); 1109 node.addSibling(this); 1110 } 1111 } 1112 1113 public void addParent(Node node) { 1114 if (mParents == null) { 1115 mParents = new ArrayList<Node>(); 1116 } 1117 if (!mParents.contains(node)) { 1118 mParents.add(node); 1119 node.addChild(this); 1120 } 1121 } 1122 1123 public void addParents(ArrayList<Node> parents) { 1124 if (parents == null) { 1125 return; 1126 } 1127 int size = parents.size(); 1128 for (int i = 0; i < size; i++) { 1129 addParent(parents.get(i)); 1130 } 1131 } 1132 } 1133 1134 /** 1135 * The <code>Builder</code> object is a utility class to facilitate adding animations to a 1136 * <code>AnimatorSet</code> along with the relationships between the various animations. The 1137 * intention of the <code>Builder</code> methods, along with the {@link 1138 * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible 1139 * to express the dependency relationships of animations in a natural way. Developers can also 1140 * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link 1141 * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need, 1142 * but it might be easier in some situations to express the AnimatorSet of animations in pairs. 1143 * <p/> 1144 * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed 1145 * internally via a call to {@link AnimatorSet#play(Animator)}.</p> 1146 * <p/> 1147 * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to 1148 * play when anim2 finishes, and anim4 to play when anim3 finishes:</p> 1149 * <pre> 1150 * AnimatorSet s = new AnimatorSet(); 1151 * s.play(anim1).with(anim2); 1152 * s.play(anim2).before(anim3); 1153 * s.play(anim4).after(anim3); 1154 * </pre> 1155 * <p/> 1156 * <p>Note in the example that both {@link Builder#before(Animator)} and {@link 1157 * Builder#after(Animator)} are used. These are just different ways of expressing the same 1158 * relationship and are provided to make it easier to say things in a way that is more natural, 1159 * depending on the situation.</p> 1160 * <p/> 1161 * <p>It is possible to make several calls into the same <code>Builder</code> object to express 1162 * multiple relationships. However, note that it is only the animation passed into the initial 1163 * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive 1164 * calls to the <code>Builder</code> object. For example, the following code starts both anim2 1165 * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and 1166 * anim3: 1167 * <pre> 1168 * AnimatorSet s = new AnimatorSet(); 1169 * s.play(anim1).before(anim2).before(anim3); 1170 * </pre> 1171 * If the desired result is to play anim1 then anim2 then anim3, this code expresses the 1172 * relationship correctly:</p> 1173 * <pre> 1174 * AnimatorSet s = new AnimatorSet(); 1175 * s.play(anim1).before(anim2); 1176 * s.play(anim2).before(anim3); 1177 * </pre> 1178 * <p/> 1179 * <p>Note that it is possible to express relationships that cannot be resolved and will not 1180 * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no 1181 * sense. In general, circular dependencies like this one (or more indirect ones where a depends 1182 * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets 1183 * that can boil down to a simple, one-way relationship of animations starting with, before, and 1184 * after other, different, animations.</p> 1185 */ 1186 public class Builder { 1187 1188 /** 1189 * This tracks the current node being processed. It is supplied to the play() method 1190 * of AnimatorSet and passed into the constructor of Builder. 1191 */ 1192 private Node mCurrentNode; 1193 1194 /** 1195 * package-private constructor. Builders are only constructed by AnimatorSet, when the 1196 * play() method is called. 1197 * 1198 * @param anim The animation that is the dependency for the other animations passed into 1199 * the other methods of this Builder object. 1200 */ 1201 Builder(Animator anim) { 1202 mDependencyDirty = true; 1203 mCurrentNode = getNodeForAnimation(anim); 1204 } 1205 1206 /** 1207 * Sets up the given animation to play at the same time as the animation supplied in the 1208 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object. 1209 * 1210 * @param anim The animation that will play when the animation supplied to the 1211 * {@link AnimatorSet#play(Animator)} method starts. 1212 */ 1213 public Builder with(Animator anim) { 1214 Node node = getNodeForAnimation(anim); 1215 mCurrentNode.addSibling(node); 1216 return this; 1217 } 1218 1219 /** 1220 * Sets up the given animation to play when the animation supplied in the 1221 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 1222 * ends. 1223 * 1224 * @param anim The animation that will play when the animation supplied to the 1225 * {@link AnimatorSet#play(Animator)} method ends. 1226 */ 1227 public Builder before(Animator anim) { 1228 mReversible = false; 1229 Node node = getNodeForAnimation(anim); 1230 mCurrentNode.addChild(node); 1231 return this; 1232 } 1233 1234 /** 1235 * Sets up the given animation to play when the animation supplied in the 1236 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 1237 * to start when the animation supplied in this method call ends. 1238 * 1239 * @param anim The animation whose end will cause the animation supplied to the 1240 * {@link AnimatorSet#play(Animator)} method to play. 1241 */ 1242 public Builder after(Animator anim) { 1243 mReversible = false; 1244 Node node = getNodeForAnimation(anim); 1245 mCurrentNode.addParent(node); 1246 return this; 1247 } 1248 1249 /** 1250 * Sets up the animation supplied in the 1251 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 1252 * to play when the given amount of time elapses. 1253 * 1254 * @param delay The number of milliseconds that should elapse before the 1255 * animation starts. 1256 */ 1257 public Builder after(long delay) { 1258 // setup dummy ValueAnimator just to run the clock 1259 ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); 1260 anim.setDuration(delay); 1261 after(anim); 1262 return this; 1263 } 1264 1265 } 1266 1267} 1268