• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.HashMap;
22 import java.util.List;
23 
24 /**
25  * This class plays a set of {@link Animator} objects in the specified order. Animations
26  * can be set up to play together, in sequence, or after a specified delay.
27  *
28  * <p>There are two different approaches to adding animations to a <code>AnimatorSet</code>:
29  * either the {@link AnimatorSet#playTogether(Animator[]) playTogether()} or
30  * {@link AnimatorSet#playSequentially(Animator[]) playSequentially()} methods can be called to add
31  * a set of animations all at once, or the {@link AnimatorSet#play(Animator)} can be
32  * used in conjunction with methods in the {@link AnimatorSet.Builder Builder}
33  * class to add animations
34  * one by one.</p>
35  *
36  * <p>It is possible to set up a <code>AnimatorSet</code> with circular dependencies between
37  * its animations. For example, an animation a1 could be set up to start before animation a2, a2
38  * before a3, and a3 before a1. The results of this configuration are undefined, but will typically
39  * result in none of the affected animations being played. Because of this (and because
40  * circular dependencies do not make logical sense anyway), circular dependencies
41  * should be avoided, and the dependency flow of animations should only be in one direction.
42  *
43  * <div class="special reference">
44  * <h3>Developer Guides</h3>
45  * <p>For more information about animating with {@code AnimatorSet}, read the
46  * <a href="{@docRoot}guide/topics/graphics/prop-animation.html#choreography">Property
47  * Animation</a> developer guide.</p>
48  * </div>
49  */
50 public final class AnimatorSet extends Animator {
51 
52     /**
53      * Internal variables
54      * NOTE: This object implements the clone() method, making a deep copy of any referenced
55      * objects. As other non-trivial fields are added to this class, make sure to add logic
56      * to clone() to make deep copies of them.
57      */
58 
59     /**
60      * Tracks animations currently being played, so that we know what to
61      * cancel or end when cancel() or end() is called on this AnimatorSet
62      */
63     private ArrayList<Animator> mPlayingSet = new ArrayList<Animator>();
64 
65     /**
66      * Contains all nodes, mapped to their respective Animators. When new
67      * dependency information is added for an Animator, we want to add it
68      * to a single node representing that Animator, not create a new Node
69      * if one already exists.
70      */
71     private HashMap<Animator, Node> mNodeMap = new HashMap<Animator, Node>();
72 
73     /**
74      * Set of all nodes created for this AnimatorSet. This list is used upon
75      * starting the set, and the nodes are placed in sorted order into the
76      * sortedNodes collection.
77      */
78     private ArrayList<Node> mNodes = new ArrayList<Node>();
79 
80     /**
81      * The sorted list of nodes. This is the order in which the animations will
82      * be played. The details about when exactly they will be played depend
83      * on the dependency relationships of the nodes.
84      */
85     private ArrayList<Node> mSortedNodes = new ArrayList<Node>();
86 
87     /**
88      * Flag indicating whether the nodes should be sorted prior to playing. This
89      * flag allows us to cache the previous sorted nodes so that if the sequence
90      * is replayed with no changes, it does not have to re-sort the nodes again.
91      */
92     private boolean mNeedsSort = true;
93 
94     private AnimatorSetListener mSetListener = null;
95 
96     /**
97      * Flag indicating that the AnimatorSet has been manually
98      * terminated (by calling cancel() or end()).
99      * This flag is used to avoid starting other animations when currently-playing
100      * child animations of this AnimatorSet end. It also determines whether cancel/end
101      * notifications are sent out via the normal AnimatorSetListener mechanism.
102      */
103     boolean mTerminated = false;
104 
105     /**
106      * Indicates whether an AnimatorSet has been start()'d, whether or
107      * not there is a nonzero startDelay.
108      */
109     private boolean mStarted = false;
110 
111     // The amount of time in ms to delay starting the animation after start() is called
112     private long mStartDelay = 0;
113 
114     // Animator used for a nonzero startDelay
115     private ValueAnimator mDelayAnim = null;
116 
117 
118     // How long the child animations should last in ms. The default value is negative, which
119     // simply means that there is no duration set on the AnimatorSet. When a real duration is
120     // set, it is passed along to the child animations.
121     private long mDuration = -1;
122 
123 
124     /**
125      * Sets up this AnimatorSet to play all of the supplied animations at the same time.
126      *
127      * @param items The animations that will be started simultaneously.
128      */
playTogether(Animator... items)129     public void playTogether(Animator... items) {
130         if (items != null) {
131             mNeedsSort = true;
132             Builder builder = play(items[0]);
133             for (int i = 1; i < items.length; ++i) {
134                 builder.with(items[i]);
135             }
136         }
137     }
138 
139     /**
140      * Sets up this AnimatorSet to play all of the supplied animations at the same time.
141      *
142      * @param items The animations that will be started simultaneously.
143      */
playTogether(Collection<Animator> items)144     public void playTogether(Collection<Animator> items) {
145         if (items != null && items.size() > 0) {
146             mNeedsSort = true;
147             Builder builder = null;
148             for (Animator anim : items) {
149                 if (builder == null) {
150                     builder = play(anim);
151                 } else {
152                     builder.with(anim);
153                 }
154             }
155         }
156     }
157 
158     /**
159      * Sets up this AnimatorSet to play each of the supplied animations when the
160      * previous animation ends.
161      *
162      * @param items The animations that will be started one after another.
163      */
playSequentially(Animator... items)164     public void playSequentially(Animator... items) {
165         if (items != null) {
166             mNeedsSort = true;
167             if (items.length == 1) {
168                 play(items[0]);
169             } else {
170                 for (int i = 0; i < items.length - 1; ++i) {
171                     play(items[i]).before(items[i+1]);
172                 }
173             }
174         }
175     }
176 
177     /**
178      * Sets up this AnimatorSet to play each of the supplied animations when the
179      * previous animation ends.
180      *
181      * @param items The animations that will be started one after another.
182      */
playSequentially(List<Animator> items)183     public void playSequentially(List<Animator> items) {
184         if (items != null && items.size() > 0) {
185             mNeedsSort = true;
186             if (items.size() == 1) {
187                 play(items.get(0));
188             } else {
189                 for (int i = 0; i < items.size() - 1; ++i) {
190                     play(items.get(i)).before(items.get(i+1));
191                 }
192             }
193         }
194     }
195 
196     /**
197      * Returns the current list of child Animator objects controlled by this
198      * AnimatorSet. This is a copy of the internal list; modifications to the returned list
199      * will not affect the AnimatorSet, although changes to the underlying Animator objects
200      * will affect those objects being managed by the AnimatorSet.
201      *
202      * @return ArrayList<Animator> The list of child animations of this AnimatorSet.
203      */
getChildAnimations()204     public ArrayList<Animator> getChildAnimations() {
205         ArrayList<Animator> childList = new ArrayList<Animator>();
206         for (Node node : mNodes) {
207             childList.add(node.animation);
208         }
209         return childList;
210     }
211 
212     /**
213      * Sets the target object for all current {@link #getChildAnimations() child animations}
214      * of this AnimatorSet that take targets ({@link ObjectAnimator} and
215      * AnimatorSet).
216      *
217      * @param target The object being animated
218      */
219     @Override
setTarget(Object target)220     public void setTarget(Object target) {
221         for (Node node : mNodes) {
222             Animator animation = node.animation;
223             if (animation instanceof AnimatorSet) {
224                 ((AnimatorSet)animation).setTarget(target);
225             } else if (animation instanceof ObjectAnimator) {
226                 ((ObjectAnimator)animation).setTarget(target);
227             }
228         }
229     }
230 
231     /**
232      * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations}
233      * of this AnimatorSet.
234      *
235      * @param interpolator the interpolator to be used by each child animation of this AnimatorSet
236      */
237     @Override
setInterpolator(TimeInterpolator interpolator)238     public void setInterpolator(TimeInterpolator interpolator) {
239         for (Node node : mNodes) {
240             node.animation.setInterpolator(interpolator);
241         }
242     }
243 
244     /**
245      * This method creates a <code>Builder</code> object, which is used to
246      * set up playing constraints. This initial <code>play()</code> method
247      * tells the <code>Builder</code> the animation that is the dependency for
248      * the succeeding commands to the <code>Builder</code>. For example,
249      * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play
250      * <code>a1</code> and <code>a2</code> at the same time,
251      * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play
252      * <code>a1</code> first, followed by <code>a2</code>, and
253      * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play
254      * <code>a2</code> first, followed by <code>a1</code>.
255      *
256      * <p>Note that <code>play()</code> is the only way to tell the
257      * <code>Builder</code> the animation upon which the dependency is created,
258      * so successive calls to the various functions in <code>Builder</code>
259      * will all refer to the initial parameter supplied in <code>play()</code>
260      * as the dependency of the other animations. For example, calling
261      * <code>play(a1).before(a2).before(a3)</code> will play both <code>a2</code>
262      * and <code>a3</code> when a1 ends; it does not set up a dependency between
263      * <code>a2</code> and <code>a3</code>.</p>
264      *
265      * @param anim The animation that is the dependency used in later calls to the
266      * methods in the returned <code>Builder</code> object. A null parameter will result
267      * in a null <code>Builder</code> return value.
268      * @return Builder The object that constructs the AnimatorSet based on the dependencies
269      * outlined in the calls to <code>play</code> and the other methods in the
270      * <code>Builder</code object.
271      */
play(Animator anim)272     public Builder play(Animator anim) {
273         if (anim != null) {
274             mNeedsSort = true;
275             return new Builder(anim);
276         }
277         return null;
278     }
279 
280     /**
281      * {@inheritDoc}
282      *
283      * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it
284      * is responsible for.</p>
285      */
286     @SuppressWarnings("unchecked")
287     @Override
cancel()288     public void cancel() {
289         mTerminated = true;
290         if (isStarted()) {
291             ArrayList<AnimatorListener> tmpListeners = null;
292             if (mListeners != null) {
293                 tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
294                 for (AnimatorListener listener : tmpListeners) {
295                     listener.onAnimationCancel(this);
296                 }
297             }
298             if (mDelayAnim != null && mDelayAnim.isRunning()) {
299                 // If we're currently in the startDelay period, just cancel that animator and
300                 // send out the end event to all listeners
301                 mDelayAnim.cancel();
302             } else  if (mSortedNodes.size() > 0) {
303                 for (Node node : mSortedNodes) {
304                     node.animation.cancel();
305                 }
306             }
307             if (tmpListeners != null) {
308                 for (AnimatorListener listener : tmpListeners) {
309                     listener.onAnimationEnd(this);
310                 }
311             }
312             mStarted = false;
313         }
314     }
315 
316     /**
317      * {@inheritDoc}
318      *
319      * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is
320      * responsible for.</p>
321      */
322     @Override
end()323     public void end() {
324         mTerminated = true;
325         if (isStarted()) {
326             if (mSortedNodes.size() != mNodes.size()) {
327                 // hasn't been started yet - sort the nodes now, then end them
328                 sortNodes();
329                 for (Node node : mSortedNodes) {
330                     if (mSetListener == null) {
331                         mSetListener = new AnimatorSetListener(this);
332                     }
333                     node.animation.addListener(mSetListener);
334                 }
335             }
336             if (mDelayAnim != null) {
337                 mDelayAnim.cancel();
338             }
339             if (mSortedNodes.size() > 0) {
340                 for (Node node : mSortedNodes) {
341                     node.animation.end();
342                 }
343             }
344             if (mListeners != null) {
345                 ArrayList<AnimatorListener> tmpListeners =
346                         (ArrayList<AnimatorListener>) mListeners.clone();
347                 for (AnimatorListener listener : tmpListeners) {
348                     listener.onAnimationEnd(this);
349                 }
350             }
351             mStarted = false;
352         }
353     }
354 
355     /**
356      * Returns true if any of the child animations of this AnimatorSet have been started and have
357      * not yet ended.
358      * @return Whether this AnimatorSet has been started and has not yet ended.
359      */
360     @Override
isRunning()361     public boolean isRunning() {
362         for (Node node : mNodes) {
363             if (node.animation.isRunning()) {
364                 return true;
365             }
366         }
367         return false;
368     }
369 
370     @Override
isStarted()371     public boolean isStarted() {
372         return mStarted;
373     }
374 
375     /**
376      * The amount of time, in milliseconds, to delay starting the animation after
377      * {@link #start()} is called.
378      *
379      * @return the number of milliseconds to delay running the animation
380      */
381     @Override
getStartDelay()382     public long getStartDelay() {
383         return mStartDelay;
384     }
385 
386     /**
387      * The amount of time, in milliseconds, to delay starting the animation after
388      * {@link #start()} is called.
389 
390      * @param startDelay The amount of the delay, in milliseconds
391      */
392     @Override
setStartDelay(long startDelay)393     public void setStartDelay(long startDelay) {
394         mStartDelay = startDelay;
395     }
396 
397     /**
398      * Gets the length of each of the child animations of this AnimatorSet. This value may
399      * be less than 0, which indicates that no duration has been set on this AnimatorSet
400      * and each of the child animations will use their own duration.
401      *
402      * @return The length of the animation, in milliseconds, of each of the child
403      * animations of this AnimatorSet.
404      */
405     @Override
getDuration()406     public long getDuration() {
407         return mDuration;
408     }
409 
410     /**
411      * Sets the length of each of the current child animations of this AnimatorSet. By default,
412      * each child animation will use its own duration. If the duration is set on the AnimatorSet,
413      * then each child animation inherits this duration.
414      *
415      * @param duration The length of the animation, in milliseconds, of each of the child
416      * animations of this AnimatorSet.
417      */
418     @Override
setDuration(long duration)419     public AnimatorSet setDuration(long duration) {
420         if (duration < 0) {
421             throw new IllegalArgumentException("duration must be a value of zero or greater");
422         }
423         // Just record the value for now - it will be used later when the AnimatorSet starts
424         mDuration = duration;
425         return this;
426     }
427 
428     @Override
setupStartValues()429     public void setupStartValues() {
430         for (Node node : mNodes) {
431             node.animation.setupStartValues();
432         }
433     }
434 
435     @Override
setupEndValues()436     public void setupEndValues() {
437         for (Node node : mNodes) {
438             node.animation.setupEndValues();
439         }
440     }
441 
442     /**
443      * {@inheritDoc}
444      *
445      * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which
446      * it is responsible. The details of when exactly those animations are started depends on
447      * the dependency relationships that have been set up between the animations.
448      */
449     @SuppressWarnings("unchecked")
450     @Override
start()451     public void start() {
452         mTerminated = false;
453         mStarted = true;
454 
455         if (mDuration >= 0) {
456             // If the duration was set on this AnimatorSet, pass it along to all child animations
457             for (Node node : mNodes) {
458                 // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
459                 // insert "play-after" delays
460                 node.animation.setDuration(mDuration);
461             }
462         }
463         // First, sort the nodes (if necessary). This will ensure that sortedNodes
464         // contains the animation nodes in the correct order.
465         sortNodes();
466 
467         int numSortedNodes = mSortedNodes.size();
468         for (int i = 0; i < numSortedNodes; ++i) {
469             Node node = mSortedNodes.get(i);
470             // First, clear out the old listeners
471             ArrayList<AnimatorListener> oldListeners = node.animation.getListeners();
472             if (oldListeners != null && oldListeners.size() > 0) {
473                 final ArrayList<AnimatorListener> clonedListeners = new
474                         ArrayList<AnimatorListener>(oldListeners);
475 
476                 for (AnimatorListener listener : clonedListeners) {
477                     if (listener instanceof DependencyListener ||
478                             listener instanceof AnimatorSetListener) {
479                         node.animation.removeListener(listener);
480                     }
481                 }
482             }
483         }
484 
485         // nodesToStart holds the list of nodes to be started immediately. We don't want to
486         // start the animations in the loop directly because we first need to set up
487         // dependencies on all of the nodes. For example, we don't want to start an animation
488         // when some other animation also wants to start when the first animation begins.
489         final ArrayList<Node> nodesToStart = new ArrayList<Node>();
490         for (int i = 0; i < numSortedNodes; ++i) {
491             Node node = mSortedNodes.get(i);
492             if (mSetListener == null) {
493                 mSetListener = new AnimatorSetListener(this);
494             }
495             if (node.dependencies == null || node.dependencies.size() == 0) {
496                 nodesToStart.add(node);
497             } else {
498                 int numDependencies = node.dependencies.size();
499                 for (int j = 0; j < numDependencies; ++j) {
500                     Dependency dependency = node.dependencies.get(j);
501                     dependency.node.animation.addListener(
502                             new DependencyListener(this, node, dependency.rule));
503                 }
504                 node.tmpDependencies = (ArrayList<Dependency>) node.dependencies.clone();
505             }
506             node.animation.addListener(mSetListener);
507         }
508         // Now that all dependencies are set up, start the animations that should be started.
509         if (mStartDelay <= 0) {
510             for (Node node : nodesToStart) {
511                 node.animation.start();
512                 mPlayingSet.add(node.animation);
513             }
514         } else {
515             mDelayAnim = ValueAnimator.ofFloat(0f, 1f);
516             mDelayAnim.setDuration(mStartDelay);
517             mDelayAnim.addListener(new AnimatorListenerAdapter() {
518                 boolean canceled = false;
519                 public void onAnimationCancel(Animator anim) {
520                     canceled = true;
521                 }
522                 public void onAnimationEnd(Animator anim) {
523                     if (!canceled) {
524                         int numNodes = nodesToStart.size();
525                         for (int i = 0; i < numNodes; ++i) {
526                             Node node = nodesToStart.get(i);
527                             node.animation.start();
528                             mPlayingSet.add(node.animation);
529                         }
530                     }
531                 }
532             });
533             mDelayAnim.start();
534         }
535         if (mListeners != null) {
536             ArrayList<AnimatorListener> tmpListeners =
537                     (ArrayList<AnimatorListener>) mListeners.clone();
538             int numListeners = tmpListeners.size();
539             for (int i = 0; i < numListeners; ++i) {
540                 tmpListeners.get(i).onAnimationStart(this);
541             }
542         }
543         if (mNodes.size() == 0 && mStartDelay == 0) {
544             // Handle unusual case where empty AnimatorSet is started - should send out
545             // end event immediately since the event will not be sent out at all otherwise
546             mStarted = false;
547             if (mListeners != null) {
548                 ArrayList<AnimatorListener> tmpListeners =
549                         (ArrayList<AnimatorListener>) mListeners.clone();
550                 int numListeners = tmpListeners.size();
551                 for (int i = 0; i < numListeners; ++i) {
552                     tmpListeners.get(i).onAnimationEnd(this);
553                 }
554             }
555         }
556     }
557 
558     @Override
clone()559     public AnimatorSet clone() {
560         final AnimatorSet anim = (AnimatorSet) super.clone();
561         /*
562          * The basic clone() operation copies all items. This doesn't work very well for
563          * AnimatorSet, because it will copy references that need to be recreated and state
564          * that may not apply. What we need to do now is put the clone in an uninitialized
565          * state, with fresh, empty data structures. Then we will build up the nodes list
566          * manually, as we clone each Node (and its animation). The clone will then be sorted,
567          * and will populate any appropriate lists, when it is started.
568          */
569         anim.mNeedsSort = true;
570         anim.mTerminated = false;
571         anim.mStarted = false;
572         anim.mPlayingSet = new ArrayList<Animator>();
573         anim.mNodeMap = new HashMap<Animator, Node>();
574         anim.mNodes = new ArrayList<Node>();
575         anim.mSortedNodes = new ArrayList<Node>();
576 
577         // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
578         // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
579         // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
580         HashMap<Node, Node> nodeCloneMap = new HashMap<Node, Node>(); // <old, new>
581         for (Node node : mNodes) {
582             Node nodeClone = node.clone();
583             nodeCloneMap.put(node, nodeClone);
584             anim.mNodes.add(nodeClone);
585             anim.mNodeMap.put(nodeClone.animation, nodeClone);
586             // Clear out the dependencies in the clone; we'll set these up manually later
587             nodeClone.dependencies = null;
588             nodeClone.tmpDependencies = null;
589             nodeClone.nodeDependents = null;
590             nodeClone.nodeDependencies = null;
591             // clear out any listeners that were set up by the AnimatorSet; these will
592             // be set up when the clone's nodes are sorted
593             ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners();
594             if (cloneListeners != null) {
595                 ArrayList<AnimatorListener> listenersToRemove = null;
596                 for (AnimatorListener listener : cloneListeners) {
597                     if (listener instanceof AnimatorSetListener) {
598                         if (listenersToRemove == null) {
599                             listenersToRemove = new ArrayList<AnimatorListener>();
600                         }
601                         listenersToRemove.add(listener);
602                     }
603                 }
604                 if (listenersToRemove != null) {
605                     for (AnimatorListener listener : listenersToRemove) {
606                         cloneListeners.remove(listener);
607                     }
608                 }
609             }
610         }
611         // Now that we've cloned all of the nodes, we're ready to walk through their
612         // dependencies, mapping the old dependencies to the new nodes
613         for (Node node : mNodes) {
614             Node nodeClone = nodeCloneMap.get(node);
615             if (node.dependencies != null) {
616                 for (Dependency dependency : node.dependencies) {
617                     Node clonedDependencyNode = nodeCloneMap.get(dependency.node);
618                     Dependency cloneDependency = new Dependency(clonedDependencyNode,
619                             dependency.rule);
620                     nodeClone.addDependency(cloneDependency);
621                 }
622             }
623         }
624 
625         return anim;
626     }
627 
628     /**
629      * This class is the mechanism by which animations are started based on events in other
630      * animations. If an animation has multiple dependencies on other animations, then
631      * all dependencies must be satisfied before the animation is started.
632      */
633     private static class DependencyListener implements AnimatorListener {
634 
635         private AnimatorSet mAnimatorSet;
636 
637         // The node upon which the dependency is based.
638         private Node mNode;
639 
640         // The Dependency rule (WITH or AFTER) that the listener should wait for on
641         // the node
642         private int mRule;
643 
DependencyListener(AnimatorSet animatorSet, Node node, int rule)644         public DependencyListener(AnimatorSet animatorSet, Node node, int rule) {
645             this.mAnimatorSet = animatorSet;
646             this.mNode = node;
647             this.mRule = rule;
648         }
649 
650         /**
651          * Ignore cancel events for now. We may want to handle this eventually,
652          * to prevent follow-on animations from running when some dependency
653          * animation is canceled.
654          */
onAnimationCancel(Animator animation)655         public void onAnimationCancel(Animator animation) {
656         }
657 
658         /**
659          * An end event is received - see if this is an event we are listening for
660          */
onAnimationEnd(Animator animation)661         public void onAnimationEnd(Animator animation) {
662             if (mRule == Dependency.AFTER) {
663                 startIfReady(animation);
664             }
665         }
666 
667         /**
668          * Ignore repeat events for now
669          */
onAnimationRepeat(Animator animation)670         public void onAnimationRepeat(Animator animation) {
671         }
672 
673         /**
674          * A start event is received - see if this is an event we are listening for
675          */
onAnimationStart(Animator animation)676         public void onAnimationStart(Animator animation) {
677             if (mRule == Dependency.WITH) {
678                 startIfReady(animation);
679             }
680         }
681 
682         /**
683          * Check whether the event received is one that the node was waiting for.
684          * If so, mark it as complete and see whether it's time to start
685          * the animation.
686          * @param dependencyAnimation the animation that sent the event.
687          */
startIfReady(Animator dependencyAnimation)688         private void startIfReady(Animator dependencyAnimation) {
689             if (mAnimatorSet.mTerminated) {
690                 // if the parent AnimatorSet was canceled, then don't start any dependent anims
691                 return;
692             }
693             Dependency dependencyToRemove = null;
694             int numDependencies = mNode.tmpDependencies.size();
695             for (int i = 0; i < numDependencies; ++i) {
696                 Dependency dependency = mNode.tmpDependencies.get(i);
697                 if (dependency.rule == mRule &&
698                         dependency.node.animation == dependencyAnimation) {
699                     // rule fired - remove the dependency and listener and check to
700                     // see whether it's time to start the animation
701                     dependencyToRemove = dependency;
702                     dependencyAnimation.removeListener(this);
703                     break;
704                 }
705             }
706             mNode.tmpDependencies.remove(dependencyToRemove);
707             if (mNode.tmpDependencies.size() == 0) {
708                 // all dependencies satisfied: start the animation
709                 mNode.animation.start();
710                 mAnimatorSet.mPlayingSet.add(mNode.animation);
711             }
712         }
713 
714     }
715 
716     private class AnimatorSetListener implements AnimatorListener {
717 
718         private AnimatorSet mAnimatorSet;
719 
AnimatorSetListener(AnimatorSet animatorSet)720         AnimatorSetListener(AnimatorSet animatorSet) {
721             mAnimatorSet = animatorSet;
722         }
723 
onAnimationCancel(Animator animation)724         public void onAnimationCancel(Animator animation) {
725             if (!mTerminated) {
726                 // Listeners are already notified of the AnimatorSet canceling in cancel().
727                 // The logic below only kicks in when animations end normally
728                 if (mPlayingSet.size() == 0) {
729                     if (mListeners != null) {
730                         int numListeners = mListeners.size();
731                         for (int i = 0; i < numListeners; ++i) {
732                             mListeners.get(i).onAnimationCancel(mAnimatorSet);
733                         }
734                     }
735                 }
736             }
737         }
738 
739         @SuppressWarnings("unchecked")
onAnimationEnd(Animator animation)740         public void onAnimationEnd(Animator animation) {
741             animation.removeListener(this);
742             mPlayingSet.remove(animation);
743             Node animNode = mAnimatorSet.mNodeMap.get(animation);
744             animNode.done = true;
745             if (!mTerminated) {
746                 // Listeners are already notified of the AnimatorSet ending in cancel() or
747                 // end(); the logic below only kicks in when animations end normally
748                 ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes;
749                 boolean allDone = true;
750                 int numSortedNodes = sortedNodes.size();
751                 for (int i = 0; i < numSortedNodes; ++i) {
752                     if (!sortedNodes.get(i).done) {
753                         allDone = false;
754                         break;
755                     }
756                 }
757                 if (allDone) {
758                     // If this was the last child animation to end, then notify listeners that this
759                     // AnimatorSet has ended
760                     if (mListeners != null) {
761                         ArrayList<AnimatorListener> tmpListeners =
762                                 (ArrayList<AnimatorListener>) mListeners.clone();
763                         int numListeners = tmpListeners.size();
764                         for (int i = 0; i < numListeners; ++i) {
765                             tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
766                         }
767                     }
768                     mAnimatorSet.mStarted = false;
769                 }
770             }
771         }
772 
773         // Nothing to do
onAnimationRepeat(Animator animation)774         public void onAnimationRepeat(Animator animation) {
775         }
776 
777         // Nothing to do
onAnimationStart(Animator animation)778         public void onAnimationStart(Animator animation) {
779         }
780 
781     }
782 
783     /**
784      * This method sorts the current set of nodes, if needed. The sort is a simple
785      * DependencyGraph sort, which goes like this:
786      * - All nodes without dependencies become 'roots'
787      * - while roots list is not null
788      * -   for each root r
789      * -     add r to sorted list
790      * -     remove r as a dependency from any other node
791      * -   any nodes with no dependencies are added to the roots list
792      */
sortNodes()793     private void sortNodes() {
794         if (mNeedsSort) {
795             mSortedNodes.clear();
796             ArrayList<Node> roots = new ArrayList<Node>();
797             int numNodes = mNodes.size();
798             for (int i = 0; i < numNodes; ++i) {
799                 Node node = mNodes.get(i);
800                 if (node.dependencies == null || node.dependencies.size() == 0) {
801                     roots.add(node);
802                 }
803             }
804             ArrayList<Node> tmpRoots = new ArrayList<Node>();
805             while (roots.size() > 0) {
806                 int numRoots = roots.size();
807                 for (int i = 0; i < numRoots; ++i) {
808                     Node root = roots.get(i);
809                     mSortedNodes.add(root);
810                     if (root.nodeDependents != null) {
811                         int numDependents = root.nodeDependents.size();
812                         for (int j = 0; j < numDependents; ++j) {
813                             Node node = root.nodeDependents.get(j);
814                             node.nodeDependencies.remove(root);
815                             if (node.nodeDependencies.size() == 0) {
816                                 tmpRoots.add(node);
817                             }
818                         }
819                     }
820                 }
821                 roots.clear();
822                 roots.addAll(tmpRoots);
823                 tmpRoots.clear();
824             }
825             mNeedsSort = false;
826             if (mSortedNodes.size() != mNodes.size()) {
827                 throw new IllegalStateException("Circular dependencies cannot exist"
828                         + " in AnimatorSet");
829             }
830         } else {
831             // Doesn't need sorting, but still need to add in the nodeDependencies list
832             // because these get removed as the event listeners fire and the dependencies
833             // are satisfied
834             int numNodes = mNodes.size();
835             for (int i = 0; i < numNodes; ++i) {
836                 Node node = mNodes.get(i);
837                 if (node.dependencies != null && node.dependencies.size() > 0) {
838                     int numDependencies = node.dependencies.size();
839                     for (int j = 0; j < numDependencies; ++j) {
840                         Dependency dependency = node.dependencies.get(j);
841                         if (node.nodeDependencies == null) {
842                             node.nodeDependencies = new ArrayList<Node>();
843                         }
844                         if (!node.nodeDependencies.contains(dependency.node)) {
845                             node.nodeDependencies.add(dependency.node);
846                         }
847                     }
848                 }
849                 // nodes are 'done' by default; they become un-done when started, and done
850                 // again when ended
851                 node.done = false;
852             }
853         }
854     }
855 
856     /**
857      * Dependency holds information about the node that some other node is
858      * dependent upon and the nature of that dependency.
859      *
860      */
861     private static class Dependency {
862         static final int WITH = 0; // dependent node must start with this dependency node
863         static final int AFTER = 1; // dependent node must start when this dependency node finishes
864 
865         // The node that the other node with this Dependency is dependent upon
866         public Node node;
867 
868         // The nature of the dependency (WITH or AFTER)
869         public int rule;
870 
Dependency(Node node, int rule)871         public Dependency(Node node, int rule) {
872             this.node = node;
873             this.rule = rule;
874         }
875     }
876 
877     /**
878      * A Node is an embodiment of both the Animator that it wraps as well as
879      * any dependencies that are associated with that Animation. This includes
880      * both dependencies upon other nodes (in the dependencies list) as
881      * well as dependencies of other nodes upon this (in the nodeDependents list).
882      */
883     private static class Node implements Cloneable {
884         public Animator animation;
885 
886         /**
887          *  These are the dependencies that this node's animation has on other
888          *  nodes. For example, if this node's animation should begin with some
889          *  other animation ends, then there will be an item in this node's
890          *  dependencies list for that other animation's node.
891          */
892         public ArrayList<Dependency> dependencies = null;
893 
894         /**
895          * tmpDependencies is a runtime detail. We use the dependencies list for sorting.
896          * But we also use the list to keep track of when multiple dependencies are satisfied,
897          * but removing each dependency as it is satisfied. We do not want to remove
898          * the dependency itself from the list, because we need to retain that information
899          * if the AnimatorSet is launched in the future. So we create a copy of the dependency
900          * list when the AnimatorSet starts and use this tmpDependencies list to track the
901          * list of satisfied dependencies.
902          */
903         public ArrayList<Dependency> tmpDependencies = null;
904 
905         /**
906          * nodeDependencies is just a list of the nodes that this Node is dependent upon.
907          * This information is used in sortNodes(), to determine when a node is a root.
908          */
909         public ArrayList<Node> nodeDependencies = null;
910 
911         /**
912          * nodeDepdendents is the list of nodes that have this node as a dependency. This
913          * is a utility field used in sortNodes to facilitate removing this node as a
914          * dependency when it is a root node.
915          */
916         public ArrayList<Node> nodeDependents = null;
917 
918         /**
919          * Flag indicating whether the animation in this node is finished. This flag
920          * is used by AnimatorSet to check, as each animation ends, whether all child animations
921          * are done and it's time to send out an end event for the entire AnimatorSet.
922          */
923         public boolean done = false;
924 
925         /**
926          * Constructs the Node with the animation that it encapsulates. A Node has no
927          * dependencies by default; dependencies are added via the addDependency()
928          * method.
929          *
930          * @param animation The animation that the Node encapsulates.
931          */
Node(Animator animation)932         public Node(Animator animation) {
933             this.animation = animation;
934         }
935 
936         /**
937          * Add a dependency to this Node. The dependency includes information about the
938          * node that this node is dependency upon and the nature of the dependency.
939          * @param dependency
940          */
addDependency(Dependency dependency)941         public void addDependency(Dependency dependency) {
942             if (dependencies == null) {
943                 dependencies = new ArrayList<Dependency>();
944                 nodeDependencies = new ArrayList<Node>();
945             }
946             dependencies.add(dependency);
947             if (!nodeDependencies.contains(dependency.node)) {
948                 nodeDependencies.add(dependency.node);
949             }
950             Node dependencyNode = dependency.node;
951             if (dependencyNode.nodeDependents == null) {
952                 dependencyNode.nodeDependents = new ArrayList<Node>();
953             }
954             dependencyNode.nodeDependents.add(this);
955         }
956 
957         @Override
clone()958         public Node clone() {
959             try {
960                 Node node = (Node) super.clone();
961                 node.animation = (Animator) animation.clone();
962                 return node;
963             } catch (CloneNotSupportedException e) {
964                throw new AssertionError();
965             }
966         }
967     }
968 
969     /**
970      * The <code>Builder</code> object is a utility class to facilitate adding animations to a
971      * <code>AnimatorSet</code> along with the relationships between the various animations. The
972      * intention of the <code>Builder</code> methods, along with the {@link
973      * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible
974      * to express the dependency relationships of animations in a natural way. Developers can also
975      * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
976      * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
977      * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
978      * <p/>
979      * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
980      * internally via a call to {@link AnimatorSet#play(Animator)}.</p>
981      * <p/>
982      * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to
983      * play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
984      * <pre>
985      *     AnimatorSet s = new AnimatorSet();
986      *     s.play(anim1).with(anim2);
987      *     s.play(anim2).before(anim3);
988      *     s.play(anim4).after(anim3);
989      * </pre>
990      * <p/>
991      * <p>Note in the example that both {@link Builder#before(Animator)} and {@link
992      * Builder#after(Animator)} are used. These are just different ways of expressing the same
993      * relationship and are provided to make it easier to say things in a way that is more natural,
994      * depending on the situation.</p>
995      * <p/>
996      * <p>It is possible to make several calls into the same <code>Builder</code> object to express
997      * multiple relationships. However, note that it is only the animation passed into the initial
998      * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
999      * calls to the <code>Builder</code> object. For example, the following code starts both anim2
1000      * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
1001      * anim3:
1002      * <pre>
1003      *   AnimatorSet s = new AnimatorSet();
1004      *   s.play(anim1).before(anim2).before(anim3);
1005      * </pre>
1006      * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
1007      * relationship correctly:</p>
1008      * <pre>
1009      *   AnimatorSet s = new AnimatorSet();
1010      *   s.play(anim1).before(anim2);
1011      *   s.play(anim2).before(anim3);
1012      * </pre>
1013      * <p/>
1014      * <p>Note that it is possible to express relationships that cannot be resolved and will not
1015      * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
1016      * sense. In general, circular dependencies like this one (or more indirect ones where a depends
1017      * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
1018      * that can boil down to a simple, one-way relationship of animations starting with, before, and
1019      * after other, different, animations.</p>
1020      */
1021     public class Builder {
1022 
1023         /**
1024          * This tracks the current node being processed. It is supplied to the play() method
1025          * of AnimatorSet and passed into the constructor of Builder.
1026          */
1027         private Node mCurrentNode;
1028 
1029         /**
1030          * package-private constructor. Builders are only constructed by AnimatorSet, when the
1031          * play() method is called.
1032          *
1033          * @param anim The animation that is the dependency for the other animations passed into
1034          * the other methods of this Builder object.
1035          */
Builder(Animator anim)1036         Builder(Animator anim) {
1037             mCurrentNode = mNodeMap.get(anim);
1038             if (mCurrentNode == null) {
1039                 mCurrentNode = new Node(anim);
1040                 mNodeMap.put(anim, mCurrentNode);
1041                 mNodes.add(mCurrentNode);
1042             }
1043         }
1044 
1045         /**
1046          * Sets up the given animation to play at the same time as the animation supplied in the
1047          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
1048          *
1049          * @param anim The animation that will play when the animation supplied to the
1050          * {@link AnimatorSet#play(Animator)} method starts.
1051          */
with(Animator anim)1052         public Builder with(Animator anim) {
1053             Node node = mNodeMap.get(anim);
1054             if (node == null) {
1055                 node = new Node(anim);
1056                 mNodeMap.put(anim, node);
1057                 mNodes.add(node);
1058             }
1059             Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH);
1060             node.addDependency(dependency);
1061             return this;
1062         }
1063 
1064         /**
1065          * Sets up the given animation to play when the animation supplied in the
1066          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
1067          * ends.
1068          *
1069          * @param anim The animation that will play when the animation supplied to the
1070          * {@link AnimatorSet#play(Animator)} method ends.
1071          */
before(Animator anim)1072         public Builder before(Animator anim) {
1073             Node node = mNodeMap.get(anim);
1074             if (node == null) {
1075                 node = new Node(anim);
1076                 mNodeMap.put(anim, node);
1077                 mNodes.add(node);
1078             }
1079             Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER);
1080             node.addDependency(dependency);
1081             return this;
1082         }
1083 
1084         /**
1085          * Sets up the given animation to play when the animation supplied in the
1086          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
1087          * to start when the animation supplied in this method call ends.
1088          *
1089          * @param anim The animation whose end will cause the animation supplied to the
1090          * {@link AnimatorSet#play(Animator)} method to play.
1091          */
after(Animator anim)1092         public Builder after(Animator anim) {
1093             Node node = mNodeMap.get(anim);
1094             if (node == null) {
1095                 node = new Node(anim);
1096                 mNodeMap.put(anim, node);
1097                 mNodes.add(node);
1098             }
1099             Dependency dependency = new Dependency(node, Dependency.AFTER);
1100             mCurrentNode.addDependency(dependency);
1101             return this;
1102         }
1103 
1104         /**
1105          * Sets up the animation supplied in the
1106          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
1107          * to play when the given amount of time elapses.
1108          *
1109          * @param delay The number of milliseconds that should elapse before the
1110          * animation starts.
1111          */
after(long delay)1112         public Builder after(long delay) {
1113             // setup dummy ValueAnimator just to run the clock
1114             ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
1115             anim.setDuration(delay);
1116             after(anim);
1117             return this;
1118         }
1119 
1120     }
1121 
1122 }
1123