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