2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com
.actionbarsherlock
.internal
.nineoldandroids
.animation
;
19 import java
.util
.ArrayList
;
20 import java
.util
.Collection
;
21 import java
.util
.HashMap
;
22 import java
.util
.List
;
24 import android
.view
.animation
.Interpolator
;
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.
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
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.
45 @SuppressWarnings("unchecked")
46 public final class AnimatorSet
extends Animator
{
50 * NOTE: This object implements the clone() method, making a deep copy of any referenced
51 * objects. As other non-trivial fields are added to this class, make sure to add logic
52 * to clone() to make deep copies of them.
56 * Tracks animations currently being played, so that we know what to
57 * cancel or end when cancel() or end() is called on this AnimatorSet
59 private ArrayList
<Animator
> mPlayingSet
= new ArrayList
<Animator
>();
62 * Contains all nodes, mapped to their respective Animators. When new
63 * dependency information is added for an Animator, we want to add it
64 * to a single node representing that Animator, not create a new Node
65 * if one already exists.
67 private HashMap
<Animator
, Node
> mNodeMap
= new HashMap
<Animator
, Node
>();
70 * Set of all nodes created for this AnimatorSet. This list is used upon
71 * starting the set, and the nodes are placed in sorted order into the
72 * sortedNodes collection.
74 private ArrayList
<Node
> mNodes
= new ArrayList
<Node
>();
77 * The sorted list of nodes. This is the order in which the animations will
78 * be played. The details about when exactly they will be played depend
79 * on the dependency relationships of the nodes.
81 private ArrayList
<Node
> mSortedNodes
= new ArrayList
<Node
>();
84 * Flag indicating whether the nodes should be sorted prior to playing. This
85 * flag allows us to cache the previous sorted nodes so that if the sequence
86 * is replayed with no changes, it does not have to re-sort the nodes again.
88 private boolean mNeedsSort
= true
;
90 private AnimatorSetListener mSetListener
= null
;
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.
99 boolean mTerminated
= false
;
102 * Indicates whether an AnimatorSet has been start()'d, whether or
103 * not there is a nonzero startDelay.
105 private boolean mStarted
= false
;
107 // The amount of time in ms to delay starting the animation after start() is called
108 private long mStartDelay
= 0;
110 // Animator used for a nonzero startDelay
111 private ValueAnimator mDelayAnim
= null
;
114 // How long the child animations should last in ms. The default value is negative, which
115 // simply means that there is no duration set on the AnimatorSet. When a real duration is
116 // set, it is passed along to the child animations.
117 private long mDuration
= -1;
121 * Sets up this AnimatorSet to play all of the supplied animations at the same time.
123 * @param items The animations that will be started simultaneously.
125 public void playTogether(Animator
... items
) {
128 Builder builder
= play(items
[0]);
129 for (int i
= 1; i
< items
.length
; ++i
) {
130 builder
.with(items
[i
]);
136 * Sets up this AnimatorSet to play all of the supplied animations at the same time.
138 * @param items The animations that will be started simultaneously.
140 public void playTogether(Collection
<Animator
> items
) {
141 if (items
!= null
&& items
.size() > 0) {
143 Builder builder
= null
;
144 for (Animator anim
: items
) {
145 if (builder
== null
) {
146 builder
= play(anim
);
155 * Sets up this AnimatorSet to play each of the supplied animations when the
156 * previous animation ends.
158 * @param items The animations that will be started one after another.
160 public void playSequentially(Animator
... items
) {
163 if (items
.length
== 1) {
166 for (int i
= 0; i
< items
.length
- 1; ++i
) {
167 play(items
[i
]).before(items
[i
+1]);
174 * Sets up this AnimatorSet to play each of the supplied animations when the
175 * previous animation ends.
177 * @param items The animations that will be started one after another.
179 public void playSequentially(List
<Animator
> items
) {
180 if (items
!= null
&& items
.size() > 0) {
182 if (items
.size() == 1) {
185 for (int i
= 0; i
< items
.size() - 1; ++i
) {
186 play(items
.get(i
)).before(items
.get(i
+1));
193 * Returns the current list of child Animator objects controlled by this
194 * AnimatorSet. This is a copy of the internal list; modifications to the returned list
195 * will not affect the AnimatorSet, although changes to the underlying Animator objects
196 * will affect those objects being managed by the AnimatorSet.
198 * @return ArrayList<Animator> The list of child animations of this AnimatorSet.
200 public ArrayList
<Animator
> getChildAnimations() {
201 ArrayList
<Animator
> childList
= new ArrayList
<Animator
>();
202 for (Node node
: mNodes
) {
203 childList
.add(node
.animation
);
209 * Sets the target object for all current {@link #getChildAnimations() child animations}
210 * of this AnimatorSet that take targets ({@link ObjectAnimator} and
213 * @param target The object being animated
216 public void setTarget(Object target
) {
217 for (Node node
: mNodes
) {
218 Animator animation
= node
.animation
;
219 if (animation
instanceof AnimatorSet
) {
220 ((AnimatorSet
)animation
).setTarget(target
);
221 } else if (animation
instanceof ObjectAnimator
) {
222 ((ObjectAnimator
)animation
).setTarget(target
);
228 * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations}
229 * of this AnimatorSet.
231 * @param interpolator the interpolator to be used by each child animation of this AnimatorSet
234 public void setInterpolator(/*Time*/Interpolator interpolator
) {
235 for (Node node
: mNodes
) {
236 node
.animation
.setInterpolator(interpolator
);
241 * This method creates a <code>Builder</code> object, which is used to
242 * set up playing constraints. This initial <code>play()</code> method
243 * tells the <code>Builder</code> the animation that is the dependency for
244 * the succeeding commands to the <code>Builder</code>. For example,
245 * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play
246 * <code>a1</code> and <code>a2</code> at the same time,
247 * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play
248 * <code>a1</code> first, followed by <code>a2</code>, and
249 * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play
250 * <code>a2</code> first, followed by <code>a1</code>.
252 * <p>Note that <code>play()</code> is the only way to tell the
253 * <code>Builder</code> the animation upon which the dependency is created,
254 * so successive calls to the various functions in <code>Builder</code>
255 * will all refer to the initial parameter supplied in <code>play()</code>
256 * as the dependency of the other animations. For example, calling
257 * <code>play(a1).before(a2).before(a3)</code> will play both <code>a2</code>
258 * and <code>a3</code> when a1 ends; it does not set up a dependency between
259 * <code>a2</code> and <code>a3</code>.</p>
261 * @param anim The animation that is the dependency used in later calls to the
262 * methods in the returned <code>Builder</code> object. A null parameter will result
263 * in a null <code>Builder</code> return value.
264 * @return Builder The object that constructs the AnimatorSet based on the dependencies
265 * outlined in the calls to <code>play</code> and the other methods in the
266 * <code>Builder</code object.
268 public Builder
play(Animator anim
) {
271 return new Builder(anim
);
279 * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it
280 * is responsible for.</p>
283 public void cancel() {
286 ArrayList
<AnimatorListener
> tmpListeners
= null
;
287 if (mListeners
!= null
) {
288 tmpListeners
= (ArrayList
<AnimatorListener
>) mListeners
.clone();
289 for (AnimatorListener listener
: tmpListeners
) {
290 listener
.onAnimationCancel(this);
293 if (mDelayAnim
!= null
&& mDelayAnim
.isRunning()) {
294 // If we're currently in the startDelay period, just cancel that animator and
295 // send out the end event to all listeners
297 } else if (mSortedNodes
.size() > 0) {
298 for (Node node
: mSortedNodes
) {
299 node
.animation
.cancel();
302 if (tmpListeners
!= null
) {
303 for (AnimatorListener listener
: tmpListeners
) {
304 listener
.onAnimationEnd(this);
314 * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is
315 * responsible for.</p>
321 if (mSortedNodes
.size() != mNodes
.size()) {
322 // hasn't been started yet - sort the nodes now, then end them
324 for (Node node
: mSortedNodes
) {
325 if (mSetListener
== null
) {
326 mSetListener
= new AnimatorSetListener(this);
328 node
.animation
.addListener(mSetListener
);
331 if (mDelayAnim
!= null
) {
334 if (mSortedNodes
.size() > 0) {
335 for (Node node
: mSortedNodes
) {
336 node
.animation
.end();
339 if (mListeners
!= null
) {
340 ArrayList
<AnimatorListener
> tmpListeners
=
341 (ArrayList
<AnimatorListener
>) mListeners
.clone();
342 for (AnimatorListener listener
: tmpListeners
) {
343 listener
.onAnimationEnd(this);
351 * Returns true if any of the child animations of this AnimatorSet have been started and have
353 * @return Whether this AnimatorSet has been started and has not yet ended.
356 public boolean isRunning() {
357 for (Node node
: mNodes
) {
358 if (node
.animation
.isRunning()) {
366 public boolean isStarted() {
371 * The amount of time, in milliseconds, to delay starting the animation after
372 * {@link #start()} is called.
374 * @return the number of milliseconds to delay running the animation
377 public long getStartDelay() {
382 * The amount of time, in milliseconds, to delay starting the animation after
383 * {@link #start()} is called.
385 * @param startDelay The amount of the delay, in milliseconds
388 public void setStartDelay(long startDelay
) {
389 mStartDelay
= startDelay
;
393 * Gets the length of each of the child animations of this AnimatorSet. This value may
394 * be less than 0, which indicates that no duration has been set on this AnimatorSet
395 * and each of the child animations will use their own duration.
397 * @return The length of the animation, in milliseconds, of each of the child
398 * animations of this AnimatorSet.
401 public long getDuration() {
406 * Sets the length of each of the current child animations of this AnimatorSet. By default,
407 * each child animation will use its own duration. If the duration is set on the AnimatorSet,
408 * then each child animation inherits this duration.
410 * @param duration The length of the animation, in milliseconds, of each of the child
411 * animations of this AnimatorSet.
414 public AnimatorSet
setDuration(long duration
) {
416 throw new IllegalArgumentException("duration must be a value of zero or greater");
418 for (Node node
: mNodes
) {
419 // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
420 // insert "play-after" delays
421 node
.animation
.setDuration(duration
);
423 mDuration
= duration
;
428 public void setupStartValues() {
429 for (Node node
: mNodes
) {
430 node
.animation
.setupStartValues();
435 public void setupEndValues() {
436 for (Node node
: mNodes
) {
437 node
.animation
.setupEndValues();
444 * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which
445 * it is responsible. The details of when exactly those animations are started depends on
446 * the dependency relationships that have been set up between the animations.
449 public void start() {
453 // First, sort the nodes (if necessary). This will ensure that sortedNodes
454 // contains the animation nodes in the correct order.
457 int numSortedNodes
= mSortedNodes
.size();
458 for (int i
= 0; i
< numSortedNodes
; ++i
) {
459 Node node
= mSortedNodes
.get(i
);
460 // First, clear out the old listeners
461 ArrayList
<AnimatorListener
> oldListeners
= node
.animation
.getListeners();
462 if (oldListeners
!= null
&& oldListeners
.size() > 0) {
463 final ArrayList
<AnimatorListener
> clonedListeners
= new
464 ArrayList
<AnimatorListener
>(oldListeners
);
466 for (AnimatorListener listener
: clonedListeners
) {
467 if (listener
instanceof DependencyListener
||
468 listener
instanceof AnimatorSetListener
) {
469 node
.animation
.removeListener(listener
);
475 // nodesToStart holds the list of nodes to be started immediately. We don't want to
476 // start the animations in the loop directly because we first need to set up
477 // dependencies on all of the nodes. For example, we don't want to start an animation
478 // when some other animation also wants to start when the first animation begins.
479 final ArrayList
<Node
> nodesToStart
= new ArrayList
<Node
>();
480 for (int i
= 0; i
< numSortedNodes
; ++i
) {
481 Node node
= mSortedNodes
.get(i
);
482 if (mSetListener
== null
) {
483 mSetListener
= new AnimatorSetListener(this);
485 if (node
.dependencies
== null
|| node
.dependencies
.size() == 0) {
486 nodesToStart
.add(node
);
488 int numDependencies
= node
.dependencies
.size();
489 for (int j
= 0; j
< numDependencies
; ++j
) {
490 Dependency dependency
= node
.dependencies
.get(j
);
491 dependency
.node
.animation
.addListener(
492 new DependencyListener(this, node
, dependency
.rule
));
494 node
.tmpDependencies
= (ArrayList
<Dependency
>) node
.dependencies
.clone();
496 node
.animation
.addListener(mSetListener
);
498 // Now that all dependencies are set up, start the animations that should be started.
499 if (mStartDelay
<= 0) {
500 for (Node node
: nodesToStart
) {
501 node
.animation
.start();
502 mPlayingSet
.add(node
.animation
);
505 mDelayAnim
= ValueAnimator
.ofFloat(0f
, 1f
);
506 mDelayAnim
.setDuration(mStartDelay
);
507 mDelayAnim
.addListener(new AnimatorListenerAdapter() {
508 boolean canceled
= false
;
509 public void onAnimationCancel(Animator anim
) {
512 public void onAnimationEnd(Animator anim
) {
514 int numNodes
= nodesToStart
.size();
515 for (int i
= 0; i
< numNodes
; ++i
) {
516 Node node
= nodesToStart
.get(i
);
517 node
.animation
.start();
518 mPlayingSet
.add(node
.animation
);
525 if (mListeners
!= null
) {
526 ArrayList
<AnimatorListener
> tmpListeners
=
527 (ArrayList
<AnimatorListener
>) mListeners
.clone();
528 int numListeners
= tmpListeners
.size();
529 for (int i
= 0; i
< numListeners
; ++i
) {
530 tmpListeners
.get(i
).onAnimationStart(this);
533 if (mNodes
.size() == 0 && mStartDelay
== 0) {
534 // Handle unusual case where empty AnimatorSet is started - should send out
535 // end event immediately since the event will not be sent out at all otherwise
537 if (mListeners
!= null
) {
538 ArrayList
<AnimatorListener
> tmpListeners
=
539 (ArrayList
<AnimatorListener
>) mListeners
.clone();
540 int numListeners
= tmpListeners
.size();
541 for (int i
= 0; i
< numListeners
; ++i
) {
542 tmpListeners
.get(i
).onAnimationEnd(this);
549 public AnimatorSet
clone() {
550 final AnimatorSet anim
= (AnimatorSet
) super.clone();
552 * The basic clone() operation copies all items. This doesn't work very well for
553 * AnimatorSet, because it will copy references that need to be recreated and state
554 * that may not apply. What we need to do now is put the clone in an uninitialized
555 * state, with fresh, empty data structures. Then we will build up the nodes list
556 * manually, as we clone each Node (and its animation). The clone will then be sorted,
557 * and will populate any appropriate lists, when it is started.
559 anim
.mNeedsSort
= true
;
560 anim
.mTerminated
= false
;
561 anim
.mStarted
= false
;
562 anim
.mPlayingSet
= new ArrayList
<Animator
>();
563 anim
.mNodeMap
= new HashMap
<Animator
, Node
>();
564 anim
.mNodes
= new ArrayList
<Node
>();
565 anim
.mSortedNodes
= new ArrayList
<Node
>();
567 // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
568 // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
569 // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
570 HashMap
<Node
, Node
> nodeCloneMap
= new HashMap
<Node
, Node
>(); // <old, new>
571 for (Node node
: mNodes
) {
572 Node nodeClone
= node
.clone();
573 nodeCloneMap
.put(node
, nodeClone
);
574 anim
.mNodes
.add(nodeClone
);
575 anim
.mNodeMap
.put(nodeClone
.animation
, nodeClone
);
576 // Clear out the dependencies in the clone; we'll set these up manually later
577 nodeClone
.dependencies
= null
;
578 nodeClone
.tmpDependencies
= null
;
579 nodeClone
.nodeDependents
= null
;
580 nodeClone
.nodeDependencies
= null
;
581 // clear out any listeners that were set up by the AnimatorSet; these will
582 // be set up when the clone's nodes are sorted
583 ArrayList
<AnimatorListener
> cloneListeners
= nodeClone
.animation
.getListeners();
584 if (cloneListeners
!= null
) {
585 ArrayList
<AnimatorListener
> listenersToRemove
= null
;
586 for (AnimatorListener listener
: cloneListeners
) {
587 if (listener
instanceof AnimatorSetListener
) {
588 if (listenersToRemove
== null
) {
589 listenersToRemove
= new ArrayList
<AnimatorListener
>();
591 listenersToRemove
.add(listener
);
594 if (listenersToRemove
!= null
) {
595 for (AnimatorListener listener
: listenersToRemove
) {
596 cloneListeners
.remove(listener
);
601 // Now that we've cloned all of the nodes, we're ready to walk through their
602 // dependencies, mapping the old dependencies to the new nodes
603 for (Node node
: mNodes
) {
604 Node nodeClone
= nodeCloneMap
.get(node
);
605 if (node
.dependencies
!= null
) {
606 for (Dependency dependency
: node
.dependencies
) {
607 Node clonedDependencyNode
= nodeCloneMap
.get(dependency
.node
);
608 Dependency cloneDependency
= new Dependency(clonedDependencyNode
,
610 nodeClone
.addDependency(cloneDependency
);
619 * This class is the mechanism by which animations are started based on events in other
620 * animations. If an animation has multiple dependencies on other animations, then
621 * all dependencies must be satisfied before the animation is started.
623 private static class DependencyListener
implements AnimatorListener
{
625 private AnimatorSet mAnimatorSet
;
627 // The node upon which the dependency is based.
630 // The Dependency rule (WITH or AFTER) that the listener should wait for on
634 public DependencyListener(AnimatorSet animatorSet
, Node node
, int rule
) {
635 this.mAnimatorSet
= animatorSet
;
641 * Ignore cancel events for now. We may want to handle this eventually,
642 * to prevent follow-on animations from running when some dependency
643 * animation is canceled.
645 public void onAnimationCancel(Animator animation
) {
649 * An end event is received - see if this is an event we are listening for
651 public void onAnimationEnd(Animator animation
) {
652 if (mRule
== Dependency
.AFTER
) {
653 startIfReady(animation
);
658 * Ignore repeat events for now
660 public void onAnimationRepeat(Animator animation
) {
664 * A start event is received - see if this is an event we are listening for
666 public void onAnimationStart(Animator animation
) {
667 if (mRule
== Dependency
.WITH
) {
668 startIfReady(animation
);
673 * Check whether the event received is one that the node was waiting for.
674 * If so, mark it as complete and see whether it's time to start
676 * @param dependencyAnimation the animation that sent the event.
678 private void startIfReady(Animator dependencyAnimation
) {
679 if (mAnimatorSet
.mTerminated
) {
680 // if the parent AnimatorSet was canceled, then don't start any dependent anims
683 Dependency dependencyToRemove
= null
;
684 int numDependencies
= mNode
.tmpDependencies
.size();
685 for (int i
= 0; i
< numDependencies
; ++i
) {
686 Dependency dependency
= mNode
.tmpDependencies
.get(i
);
687 if (dependency
.rule
== mRule
&&
688 dependency
.node
.animation
== dependencyAnimation
) {
689 // rule fired - remove the dependency and listener and check to
690 // see whether it's time to start the animation
691 dependencyToRemove
= dependency
;
692 dependencyAnimation
.removeListener(this);
696 mNode
.tmpDependencies
.remove(dependencyToRemove
);
697 if (mNode
.tmpDependencies
.size() == 0) {
698 // all dependencies satisfied: start the animation
699 mNode
.animation
.start();
700 mAnimatorSet
.mPlayingSet
.add(mNode
.animation
);
706 private class AnimatorSetListener
implements AnimatorListener
{
708 private AnimatorSet mAnimatorSet
;
710 AnimatorSetListener(AnimatorSet animatorSet
) {
711 mAnimatorSet
= animatorSet
;
714 public void onAnimationCancel(Animator animation
) {
716 // Listeners are already notified of the AnimatorSet canceling in cancel().
717 // The logic below only kicks in when animations end normally
718 if (mPlayingSet
.size() == 0) {
719 if (mListeners
!= null
) {
720 int numListeners
= mListeners
.size();
721 for (int i
= 0; i
< numListeners
; ++i
) {
722 mListeners
.get(i
).onAnimationCancel(mAnimatorSet
);
729 public void onAnimationEnd(Animator animation
) {
730 animation
.removeListener(this);
731 mPlayingSet
.remove(animation
);
732 Node animNode
= mAnimatorSet
.mNodeMap
.get(animation
);
733 animNode
.done
= true
;
735 // Listeners are already notified of the AnimatorSet ending in cancel() or
736 // end(); the logic below only kicks in when animations end normally
737 ArrayList
<Node
> sortedNodes
= mAnimatorSet
.mSortedNodes
;
738 boolean allDone
= true
;
739 int numSortedNodes
= sortedNodes
.size();
740 for (int i
= 0; i
< numSortedNodes
; ++i
) {
741 if (!sortedNodes
.get(i
).done
) {
747 // If this was the last child animation to end, then notify listeners that this
748 // AnimatorSet has ended
749 if (mListeners
!= null
) {
750 ArrayList
<AnimatorListener
> tmpListeners
=
751 (ArrayList
<AnimatorListener
>) mListeners
.clone();
752 int numListeners
= tmpListeners
.size();
753 for (int i
= 0; i
< numListeners
; ++i
) {
754 tmpListeners
.get(i
).onAnimationEnd(mAnimatorSet
);
757 mAnimatorSet
.mStarted
= false
;
763 public void onAnimationRepeat(Animator animation
) {
767 public void onAnimationStart(Animator animation
) {
773 * This method sorts the current set of nodes, if needed. The sort is a simple
774 * DependencyGraph sort, which goes like this:
775 * - All nodes without dependencies become 'roots'
776 * - while roots list is not null
778 * - add r to sorted list
779 * - remove r as a dependency from any other node
780 * - any nodes with no dependencies are added to the roots list
782 private void sortNodes() {
784 mSortedNodes
.clear();
785 ArrayList
<Node
> roots
= new ArrayList
<Node
>();
786 int numNodes
= mNodes
.size();
787 for (int i
= 0; i
< numNodes
; ++i
) {
788 Node node
= mNodes
.get(i
);
789 if (node
.dependencies
== null
|| node
.dependencies
.size() == 0) {
793 ArrayList
<Node
> tmpRoots
= new ArrayList
<Node
>();
794 while (roots
.size() > 0) {
795 int numRoots
= roots
.size();
796 for (int i
= 0; i
< numRoots
; ++i
) {
797 Node root
= roots
.get(i
);
798 mSortedNodes
.add(root
);
799 if (root
.nodeDependents
!= null
) {
800 int numDependents
= root
.nodeDependents
.size();
801 for (int j
= 0; j
< numDependents
; ++j
) {
802 Node node
= root
.nodeDependents
.get(j
);
803 node
.nodeDependencies
.remove(root
);
804 if (node
.nodeDependencies
.size() == 0) {
811 roots
.addAll(tmpRoots
);
815 if (mSortedNodes
.size() != mNodes
.size()) {
816 throw new IllegalStateException("Circular dependencies cannot exist"
817 + " in AnimatorSet");
820 // Doesn't need sorting, but still need to add in the nodeDependencies list
821 // because these get removed as the event listeners fire and the dependencies
823 int numNodes
= mNodes
.size();
824 for (int i
= 0; i
< numNodes
; ++i
) {
825 Node node
= mNodes
.get(i
);
826 if (node
.dependencies
!= null
&& node
.dependencies
.size() > 0) {
827 int numDependencies
= node
.dependencies
.size();
828 for (int j
= 0; j
< numDependencies
; ++j
) {
829 Dependency dependency
= node
.dependencies
.get(j
);
830 if (node
.nodeDependencies
== null
) {
831 node
.nodeDependencies
= new ArrayList
<Node
>();
833 if (!node
.nodeDependencies
.contains(dependency
.node
)) {
834 node
.nodeDependencies
.add(dependency
.node
);
838 // nodes are 'done' by default; they become un-done when started, and done
846 * Dependency holds information about the node that some other node is
847 * dependent upon and the nature of that dependency.
850 private static class Dependency
{
851 static final int WITH
= 0; // dependent node must start with this dependency node
852 static final int AFTER
= 1; // dependent node must start when this dependency node finishes
854 // The node that the other node with this Dependency is dependent upon
857 // The nature of the dependency (WITH or AFTER)
860 public Dependency(Node node
, int rule
) {
867 * A Node is an embodiment of both the Animator that it wraps as well as
868 * any dependencies that are associated with that Animation. This includes
869 * both dependencies upon other nodes (in the dependencies list) as
870 * well as dependencies of other nodes upon this (in the nodeDependents list).
872 private static class Node
implements Cloneable
{
873 public Animator animation
;
876 * These are the dependencies that this node's animation has on other
877 * nodes. For example, if this node's animation should begin with some
878 * other animation ends, then there will be an item in this node's
879 * dependencies list for that other animation's node.
881 public ArrayList
<Dependency
> dependencies
= null
;
884 * tmpDependencies is a runtime detail. We use the dependencies list for sorting.
885 * But we also use the list to keep track of when multiple dependencies are satisfied,
886 * but removing each dependency as it is satisfied. We do not want to remove
887 * the dependency itself from the list, because we need to retain that information
888 * if the AnimatorSet is launched in the future. So we create a copy of the dependency
889 * list when the AnimatorSet starts and use this tmpDependencies list to track the
890 * list of satisfied dependencies.
892 public ArrayList
<Dependency
> tmpDependencies
= null
;
895 * nodeDependencies is just a list of the nodes that this Node is dependent upon.
896 * This information is used in sortNodes(), to determine when a node is a root.
898 public ArrayList
<Node
> nodeDependencies
= null
;
901 * nodeDepdendents is the list of nodes that have this node as a dependency. This
902 * is a utility field used in sortNodes to facilitate removing this node as a
903 * dependency when it is a root node.
905 public ArrayList
<Node
> nodeDependents
= null
;
908 * Flag indicating whether the animation in this node is finished. This flag
909 * is used by AnimatorSet to check, as each animation ends, whether all child animations
910 * are done and it's time to send out an end event for the entire AnimatorSet.
912 public boolean done
= false
;
915 * Constructs the Node with the animation that it encapsulates. A Node has no
916 * dependencies by default; dependencies are added via the addDependency()
919 * @param animation The animation that the Node encapsulates.
921 public Node(Animator animation
) {
922 this.animation
= animation
;
926 * Add a dependency to this Node. The dependency includes information about the
927 * node that this node is dependency upon and the nature of the dependency.
930 public void addDependency(Dependency dependency
) {
931 if (dependencies
== null
) {
932 dependencies
= new ArrayList
<Dependency
>();
933 nodeDependencies
= new ArrayList
<Node
>();
935 dependencies
.add(dependency
);
936 if (!nodeDependencies
.contains(dependency
.node
)) {
937 nodeDependencies
.add(dependency
.node
);
939 Node dependencyNode
= dependency
.node
;
940 if (dependencyNode
.nodeDependents
== null
) {
941 dependencyNode
.nodeDependents
= new ArrayList
<Node
>();
943 dependencyNode
.nodeDependents
.add(this);
947 public Node
clone() {
949 Node node
= (Node
) super.clone();
950 node
.animation
= animation
.clone();
952 } catch (CloneNotSupportedException e
) {
953 throw new AssertionError();
959 * The <code>Builder</code> object is a utility class to facilitate adding animations to a
960 * <code>AnimatorSet</code> along with the relationships between the various animations. The
961 * intention of the <code>Builder</code> methods, along with the {@link
962 * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible
963 * to express the dependency relationships of animations in a natural way. Developers can also
964 * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
965 * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
966 * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
968 * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
969 * internally via a call to {@link AnimatorSet#play(Animator)}.</p>
971 * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to
972 * play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
974 * AnimatorSet s = new AnimatorSet();
975 * s.play(anim1).with(anim2);
976 * s.play(anim2).before(anim3);
977 * s.play(anim4).after(anim3);
980 * <p>Note in the example that both {@link Builder#before(Animator)} and {@link
981 * Builder#after(Animator)} are used. These are just different ways of expressing the same
982 * relationship and are provided to make it easier to say things in a way that is more natural,
983 * depending on the situation.</p>
985 * <p>It is possible to make several calls into the same <code>Builder</code> object to express
986 * multiple relationships. However, note that it is only the animation passed into the initial
987 * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
988 * calls to the <code>Builder</code> object. For example, the following code starts both anim2
989 * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
992 * AnimatorSet s = new AnimatorSet();
993 * s.play(anim1).before(anim2).before(anim3);
995 * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
996 * relationship correctly:</p>
998 * AnimatorSet s = new AnimatorSet();
999 * s.play(anim1).before(anim2);
1000 * s.play(anim2).before(anim3);
1003 * <p>Note that it is possible to express relationships that cannot be resolved and will not
1004 * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
1005 * sense. In general, circular dependencies like this one (or more indirect ones where a depends
1006 * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
1007 * that can boil down to a simple, one-way relationship of animations starting with, before, and
1008 * after other, different, animations.</p>
1010 public class Builder
{
1013 * This tracks the current node being processed. It is supplied to the play() method
1014 * of AnimatorSet and passed into the constructor of Builder.
1016 private Node mCurrentNode
;
1019 * package-private constructor. Builders are only constructed by AnimatorSet, when the
1020 * play() method is called.
1022 * @param anim The animation that is the dependency for the other animations passed into
1023 * the other methods of this Builder object.
1025 Builder(Animator anim
) {
1026 mCurrentNode
= mNodeMap
.get(anim
);
1027 if (mCurrentNode
== null
) {
1028 mCurrentNode
= new Node(anim
);
1029 mNodeMap
.put(anim
, mCurrentNode
);
1030 mNodes
.add(mCurrentNode
);
1035 * Sets up the given animation to play at the same time as the animation supplied in the
1036 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
1038 * @param anim The animation that will play when the animation supplied to the
1039 * {@link AnimatorSet#play(Animator)} method starts.
1041 public Builder
with(Animator anim
) {
1042 Node node
= mNodeMap
.get(anim
);
1044 node
= new Node(anim
);
1045 mNodeMap
.put(anim
, node
);
1048 Dependency dependency
= new Dependency(mCurrentNode
, Dependency
.WITH
);
1049 node
.addDependency(dependency
);
1054 * Sets up the given animation to play when the animation supplied in the
1055 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
1058 * @param anim The animation that will play when the animation supplied to the
1059 * {@link AnimatorSet#play(Animator)} method ends.
1061 public Builder
before(Animator anim
) {
1062 Node node
= mNodeMap
.get(anim
);
1064 node
= new Node(anim
);
1065 mNodeMap
.put(anim
, node
);
1068 Dependency dependency
= new Dependency(mCurrentNode
, Dependency
.AFTER
);
1069 node
.addDependency(dependency
);
1074 * Sets up the given animation to play when the animation supplied in the
1075 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
1076 * to start when the animation supplied in this method call ends.
1078 * @param anim The animation whose end will cause the animation supplied to the
1079 * {@link AnimatorSet#play(Animator)} method to play.
1081 public Builder
after(Animator anim
) {
1082 Node node
= mNodeMap
.get(anim
);
1084 node
= new Node(anim
);
1085 mNodeMap
.put(anim
, node
);
1088 Dependency dependency
= new Dependency(node
, Dependency
.AFTER
);
1089 mCurrentNode
.addDependency(dependency
);
1094 * Sets up the animation supplied in the
1095 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
1096 * to play when the given amount of time elapses.
1098 * @param delay The number of milliseconds that should elapse before the
1101 public Builder
after(long delay
) {
1102 // setup dummy ValueAnimator just to run the clock
1103 ValueAnimator anim
= ValueAnimator
.ofFloat(0f
, 1f
);
1104 anim
.setDuration(delay
);