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