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