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