• 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     @Override
pause()459     public void pause() {
460         boolean previouslyPaused = mPaused;
461         super.pause();
462         if (!previouslyPaused && mPaused) {
463             if (mDelayAnim != null) {
464                 mDelayAnim.pause();
465             } else {
466                 for (Node node : mNodes) {
467                     node.animation.pause();
468                 }
469             }
470         }
471     }
472 
473     @Override
resume()474     public void resume() {
475         boolean previouslyPaused = mPaused;
476         super.resume();
477         if (previouslyPaused && !mPaused) {
478             if (mDelayAnim != null) {
479                 mDelayAnim.resume();
480             } else {
481                 for (Node node : mNodes) {
482                     node.animation.resume();
483                 }
484             }
485         }
486     }
487 
488     /**
489      * {@inheritDoc}
490      *
491      * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which
492      * it is responsible. The details of when exactly those animations are started depends on
493      * the dependency relationships that have been set up between the animations.
494      */
495     @SuppressWarnings("unchecked")
496     @Override
start()497     public void start() {
498         mTerminated = false;
499         mStarted = true;
500         mPaused = false;
501 
502         if (mDuration >= 0) {
503             // If the duration was set on this AnimatorSet, pass it along to all child animations
504             for (Node node : mNodes) {
505                 // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
506                 // insert "play-after" delays
507                 node.animation.setDuration(mDuration);
508             }
509         }
510         if (mInterpolator != null) {
511             for (Node node : mNodes) {
512                 node.animation.setInterpolator(mInterpolator);
513             }
514         }
515             // First, sort the nodes (if necessary). This will ensure that sortedNodes
516         // contains the animation nodes in the correct order.
517         sortNodes();
518 
519         int numSortedNodes = mSortedNodes.size();
520         for (int i = 0; i < numSortedNodes; ++i) {
521             Node node = mSortedNodes.get(i);
522             // First, clear out the old listeners
523             ArrayList<AnimatorListener> oldListeners = node.animation.getListeners();
524             if (oldListeners != null && oldListeners.size() > 0) {
525                 final ArrayList<AnimatorListener> clonedListeners = new
526                         ArrayList<AnimatorListener>(oldListeners);
527 
528                 for (AnimatorListener listener : clonedListeners) {
529                     if (listener instanceof DependencyListener ||
530                             listener instanceof AnimatorSetListener) {
531                         node.animation.removeListener(listener);
532                     }
533                 }
534             }
535         }
536 
537         // nodesToStart holds the list of nodes to be started immediately. We don't want to
538         // start the animations in the loop directly because we first need to set up
539         // dependencies on all of the nodes. For example, we don't want to start an animation
540         // when some other animation also wants to start when the first animation begins.
541         final ArrayList<Node> nodesToStart = new ArrayList<Node>();
542         for (int i = 0; i < numSortedNodes; ++i) {
543             Node node = mSortedNodes.get(i);
544             if (mSetListener == null) {
545                 mSetListener = new AnimatorSetListener(this);
546             }
547             if (node.dependencies == null || node.dependencies.size() == 0) {
548                 nodesToStart.add(node);
549             } else {
550                 int numDependencies = node.dependencies.size();
551                 for (int j = 0; j < numDependencies; ++j) {
552                     Dependency dependency = node.dependencies.get(j);
553                     dependency.node.animation.addListener(
554                             new DependencyListener(this, node, dependency.rule));
555                 }
556                 node.tmpDependencies = (ArrayList<Dependency>) node.dependencies.clone();
557             }
558             node.animation.addListener(mSetListener);
559         }
560         // Now that all dependencies are set up, start the animations that should be started.
561         if (mStartDelay <= 0) {
562             for (Node node : nodesToStart) {
563                 node.animation.start();
564                 mPlayingSet.add(node.animation);
565             }
566         } else {
567             mDelayAnim = ValueAnimator.ofFloat(0f, 1f);
568             mDelayAnim.setDuration(mStartDelay);
569             mDelayAnim.addListener(new AnimatorListenerAdapter() {
570                 boolean canceled = false;
571                 public void onAnimationCancel(Animator anim) {
572                     canceled = true;
573                 }
574                 public void onAnimationEnd(Animator anim) {
575                     if (!canceled) {
576                         int numNodes = nodesToStart.size();
577                         for (int i = 0; i < numNodes; ++i) {
578                             Node node = nodesToStart.get(i);
579                             node.animation.start();
580                             mPlayingSet.add(node.animation);
581                         }
582                     }
583                     mDelayAnim = null;
584                 }
585             });
586             mDelayAnim.start();
587         }
588         if (mListeners != null) {
589             ArrayList<AnimatorListener> tmpListeners =
590                     (ArrayList<AnimatorListener>) mListeners.clone();
591             int numListeners = tmpListeners.size();
592             for (int i = 0; i < numListeners; ++i) {
593                 tmpListeners.get(i).onAnimationStart(this);
594             }
595         }
596         if (mNodes.size() == 0 && mStartDelay == 0) {
597             // Handle unusual case where empty AnimatorSet is started - should send out
598             // end event immediately since the event will not be sent out at all otherwise
599             mStarted = false;
600             if (mListeners != null) {
601                 ArrayList<AnimatorListener> tmpListeners =
602                         (ArrayList<AnimatorListener>) mListeners.clone();
603                 int numListeners = tmpListeners.size();
604                 for (int i = 0; i < numListeners; ++i) {
605                     tmpListeners.get(i).onAnimationEnd(this);
606                 }
607             }
608         }
609     }
610 
611     @Override
clone()612     public AnimatorSet clone() {
613         final AnimatorSet anim = (AnimatorSet) super.clone();
614         /*
615          * The basic clone() operation copies all items. This doesn't work very well for
616          * AnimatorSet, because it will copy references that need to be recreated and state
617          * that may not apply. What we need to do now is put the clone in an uninitialized
618          * state, with fresh, empty data structures. Then we will build up the nodes list
619          * manually, as we clone each Node (and its animation). The clone will then be sorted,
620          * and will populate any appropriate lists, when it is started.
621          */
622         anim.mNeedsSort = true;
623         anim.mTerminated = false;
624         anim.mStarted = false;
625         anim.mPlayingSet = new ArrayList<Animator>();
626         anim.mNodeMap = new HashMap<Animator, Node>();
627         anim.mNodes = new ArrayList<Node>();
628         anim.mSortedNodes = new ArrayList<Node>();
629 
630         // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
631         // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
632         // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
633         HashMap<Node, Node> nodeCloneMap = new HashMap<Node, Node>(); // <old, new>
634         for (Node node : mNodes) {
635             Node nodeClone = node.clone();
636             nodeCloneMap.put(node, nodeClone);
637             anim.mNodes.add(nodeClone);
638             anim.mNodeMap.put(nodeClone.animation, nodeClone);
639             // Clear out the dependencies in the clone; we'll set these up manually later
640             nodeClone.dependencies = null;
641             nodeClone.tmpDependencies = null;
642             nodeClone.nodeDependents = null;
643             nodeClone.nodeDependencies = null;
644             // clear out any listeners that were set up by the AnimatorSet; these will
645             // be set up when the clone's nodes are sorted
646             ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners();
647             if (cloneListeners != null) {
648                 ArrayList<AnimatorListener> listenersToRemove = null;
649                 for (AnimatorListener listener : cloneListeners) {
650                     if (listener instanceof AnimatorSetListener) {
651                         if (listenersToRemove == null) {
652                             listenersToRemove = new ArrayList<AnimatorListener>();
653                         }
654                         listenersToRemove.add(listener);
655                     }
656                 }
657                 if (listenersToRemove != null) {
658                     for (AnimatorListener listener : listenersToRemove) {
659                         cloneListeners.remove(listener);
660                     }
661                 }
662             }
663         }
664         // Now that we've cloned all of the nodes, we're ready to walk through their
665         // dependencies, mapping the old dependencies to the new nodes
666         for (Node node : mNodes) {
667             Node nodeClone = nodeCloneMap.get(node);
668             if (node.dependencies != null) {
669                 for (Dependency dependency : node.dependencies) {
670                     Node clonedDependencyNode = nodeCloneMap.get(dependency.node);
671                     Dependency cloneDependency = new Dependency(clonedDependencyNode,
672                             dependency.rule);
673                     nodeClone.addDependency(cloneDependency);
674                 }
675             }
676         }
677 
678         return anim;
679     }
680 
681     /**
682      * This class is the mechanism by which animations are started based on events in other
683      * animations. If an animation has multiple dependencies on other animations, then
684      * all dependencies must be satisfied before the animation is started.
685      */
686     private static class DependencyListener implements AnimatorListener {
687 
688         private AnimatorSet mAnimatorSet;
689 
690         // The node upon which the dependency is based.
691         private Node mNode;
692 
693         // The Dependency rule (WITH or AFTER) that the listener should wait for on
694         // the node
695         private int mRule;
696 
DependencyListener(AnimatorSet animatorSet, Node node, int rule)697         public DependencyListener(AnimatorSet animatorSet, Node node, int rule) {
698             this.mAnimatorSet = animatorSet;
699             this.mNode = node;
700             this.mRule = rule;
701         }
702 
703         /**
704          * Ignore cancel events for now. We may want to handle this eventually,
705          * to prevent follow-on animations from running when some dependency
706          * animation is canceled.
707          */
onAnimationCancel(Animator animation)708         public void onAnimationCancel(Animator animation) {
709         }
710 
711         /**
712          * An end event is received - see if this is an event we are listening for
713          */
onAnimationEnd(Animator animation)714         public void onAnimationEnd(Animator animation) {
715             if (mRule == Dependency.AFTER) {
716                 startIfReady(animation);
717             }
718         }
719 
720         /**
721          * Ignore repeat events for now
722          */
onAnimationRepeat(Animator animation)723         public void onAnimationRepeat(Animator animation) {
724         }
725 
726         /**
727          * A start event is received - see if this is an event we are listening for
728          */
onAnimationStart(Animator animation)729         public void onAnimationStart(Animator animation) {
730             if (mRule == Dependency.WITH) {
731                 startIfReady(animation);
732             }
733         }
734 
735         /**
736          * Check whether the event received is one that the node was waiting for.
737          * If so, mark it as complete and see whether it's time to start
738          * the animation.
739          * @param dependencyAnimation the animation that sent the event.
740          */
startIfReady(Animator dependencyAnimation)741         private void startIfReady(Animator dependencyAnimation) {
742             if (mAnimatorSet.mTerminated) {
743                 // if the parent AnimatorSet was canceled, then don't start any dependent anims
744                 return;
745             }
746             Dependency dependencyToRemove = null;
747             int numDependencies = mNode.tmpDependencies.size();
748             for (int i = 0; i < numDependencies; ++i) {
749                 Dependency dependency = mNode.tmpDependencies.get(i);
750                 if (dependency.rule == mRule &&
751                         dependency.node.animation == dependencyAnimation) {
752                     // rule fired - remove the dependency and listener and check to
753                     // see whether it's time to start the animation
754                     dependencyToRemove = dependency;
755                     dependencyAnimation.removeListener(this);
756                     break;
757                 }
758             }
759             mNode.tmpDependencies.remove(dependencyToRemove);
760             if (mNode.tmpDependencies.size() == 0) {
761                 // all dependencies satisfied: start the animation
762                 mNode.animation.start();
763                 mAnimatorSet.mPlayingSet.add(mNode.animation);
764             }
765         }
766 
767     }
768 
769     private class AnimatorSetListener implements AnimatorListener {
770 
771         private AnimatorSet mAnimatorSet;
772 
AnimatorSetListener(AnimatorSet animatorSet)773         AnimatorSetListener(AnimatorSet animatorSet) {
774             mAnimatorSet = animatorSet;
775         }
776 
onAnimationCancel(Animator animation)777         public void onAnimationCancel(Animator animation) {
778             if (!mTerminated) {
779                 // Listeners are already notified of the AnimatorSet canceling in cancel().
780                 // The logic below only kicks in when animations end normally
781                 if (mPlayingSet.size() == 0) {
782                     if (mListeners != null) {
783                         int numListeners = mListeners.size();
784                         for (int i = 0; i < numListeners; ++i) {
785                             mListeners.get(i).onAnimationCancel(mAnimatorSet);
786                         }
787                     }
788                 }
789             }
790         }
791 
792         @SuppressWarnings("unchecked")
onAnimationEnd(Animator animation)793         public void onAnimationEnd(Animator animation) {
794             animation.removeListener(this);
795             mPlayingSet.remove(animation);
796             Node animNode = mAnimatorSet.mNodeMap.get(animation);
797             animNode.done = true;
798             if (!mTerminated) {
799                 // Listeners are already notified of the AnimatorSet ending in cancel() or
800                 // end(); the logic below only kicks in when animations end normally
801                 ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes;
802                 boolean allDone = true;
803                 int numSortedNodes = sortedNodes.size();
804                 for (int i = 0; i < numSortedNodes; ++i) {
805                     if (!sortedNodes.get(i).done) {
806                         allDone = false;
807                         break;
808                     }
809                 }
810                 if (allDone) {
811                     // If this was the last child animation to end, then notify listeners that this
812                     // AnimatorSet has ended
813                     if (mListeners != null) {
814                         ArrayList<AnimatorListener> tmpListeners =
815                                 (ArrayList<AnimatorListener>) mListeners.clone();
816                         int numListeners = tmpListeners.size();
817                         for (int i = 0; i < numListeners; ++i) {
818                             tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
819                         }
820                     }
821                     mAnimatorSet.mStarted = false;
822                     mAnimatorSet.mPaused = false;
823                 }
824             }
825         }
826 
827         // Nothing to do
onAnimationRepeat(Animator animation)828         public void onAnimationRepeat(Animator animation) {
829         }
830 
831         // Nothing to do
onAnimationStart(Animator animation)832         public void onAnimationStart(Animator animation) {
833         }
834 
835     }
836 
837     /**
838      * This method sorts the current set of nodes, if needed. The sort is a simple
839      * DependencyGraph sort, which goes like this:
840      * - All nodes without dependencies become 'roots'
841      * - while roots list is not null
842      * -   for each root r
843      * -     add r to sorted list
844      * -     remove r as a dependency from any other node
845      * -   any nodes with no dependencies are added to the roots list
846      */
sortNodes()847     private void sortNodes() {
848         if (mNeedsSort) {
849             mSortedNodes.clear();
850             ArrayList<Node> roots = new ArrayList<Node>();
851             int numNodes = mNodes.size();
852             for (int i = 0; i < numNodes; ++i) {
853                 Node node = mNodes.get(i);
854                 if (node.dependencies == null || node.dependencies.size() == 0) {
855                     roots.add(node);
856                 }
857             }
858             ArrayList<Node> tmpRoots = new ArrayList<Node>();
859             while (roots.size() > 0) {
860                 int numRoots = roots.size();
861                 for (int i = 0; i < numRoots; ++i) {
862                     Node root = roots.get(i);
863                     mSortedNodes.add(root);
864                     if (root.nodeDependents != null) {
865                         int numDependents = root.nodeDependents.size();
866                         for (int j = 0; j < numDependents; ++j) {
867                             Node node = root.nodeDependents.get(j);
868                             node.nodeDependencies.remove(root);
869                             if (node.nodeDependencies.size() == 0) {
870                                 tmpRoots.add(node);
871                             }
872                         }
873                     }
874                 }
875                 roots.clear();
876                 roots.addAll(tmpRoots);
877                 tmpRoots.clear();
878             }
879             mNeedsSort = false;
880             if (mSortedNodes.size() != mNodes.size()) {
881                 throw new IllegalStateException("Circular dependencies cannot exist"
882                         + " in AnimatorSet");
883             }
884         } else {
885             // Doesn't need sorting, but still need to add in the nodeDependencies list
886             // because these get removed as the event listeners fire and the dependencies
887             // are satisfied
888             int numNodes = mNodes.size();
889             for (int i = 0; i < numNodes; ++i) {
890                 Node node = mNodes.get(i);
891                 if (node.dependencies != null && node.dependencies.size() > 0) {
892                     int numDependencies = node.dependencies.size();
893                     for (int j = 0; j < numDependencies; ++j) {
894                         Dependency dependency = node.dependencies.get(j);
895                         if (node.nodeDependencies == null) {
896                             node.nodeDependencies = new ArrayList<Node>();
897                         }
898                         if (!node.nodeDependencies.contains(dependency.node)) {
899                             node.nodeDependencies.add(dependency.node);
900                         }
901                     }
902                 }
903                 // nodes are 'done' by default; they become un-done when started, and done
904                 // again when ended
905                 node.done = false;
906             }
907         }
908     }
909 
910     /**
911      * Dependency holds information about the node that some other node is
912      * dependent upon and the nature of that dependency.
913      *
914      */
915     private static class Dependency {
916         static final int WITH = 0; // dependent node must start with this dependency node
917         static final int AFTER = 1; // dependent node must start when this dependency node finishes
918 
919         // The node that the other node with this Dependency is dependent upon
920         public Node node;
921 
922         // The nature of the dependency (WITH or AFTER)
923         public int rule;
924 
Dependency(Node node, int rule)925         public Dependency(Node node, int rule) {
926             this.node = node;
927             this.rule = rule;
928         }
929     }
930 
931     /**
932      * A Node is an embodiment of both the Animator that it wraps as well as
933      * any dependencies that are associated with that Animation. This includes
934      * both dependencies upon other nodes (in the dependencies list) as
935      * well as dependencies of other nodes upon this (in the nodeDependents list).
936      */
937     private static class Node implements Cloneable {
938         public Animator animation;
939 
940         /**
941          *  These are the dependencies that this node's animation has on other
942          *  nodes. For example, if this node's animation should begin with some
943          *  other animation ends, then there will be an item in this node's
944          *  dependencies list for that other animation's node.
945          */
946         public ArrayList<Dependency> dependencies = null;
947 
948         /**
949          * tmpDependencies is a runtime detail. We use the dependencies list for sorting.
950          * But we also use the list to keep track of when multiple dependencies are satisfied,
951          * but removing each dependency as it is satisfied. We do not want to remove
952          * the dependency itself from the list, because we need to retain that information
953          * if the AnimatorSet is launched in the future. So we create a copy of the dependency
954          * list when the AnimatorSet starts and use this tmpDependencies list to track the
955          * list of satisfied dependencies.
956          */
957         public ArrayList<Dependency> tmpDependencies = null;
958 
959         /**
960          * nodeDependencies is just a list of the nodes that this Node is dependent upon.
961          * This information is used in sortNodes(), to determine when a node is a root.
962          */
963         public ArrayList<Node> nodeDependencies = null;
964 
965         /**
966          * nodeDepdendents is the list of nodes that have this node as a dependency. This
967          * is a utility field used in sortNodes to facilitate removing this node as a
968          * dependency when it is a root node.
969          */
970         public ArrayList<Node> nodeDependents = null;
971 
972         /**
973          * Flag indicating whether the animation in this node is finished. This flag
974          * is used by AnimatorSet to check, as each animation ends, whether all child animations
975          * are done and it's time to send out an end event for the entire AnimatorSet.
976          */
977         public boolean done = false;
978 
979         /**
980          * Constructs the Node with the animation that it encapsulates. A Node has no
981          * dependencies by default; dependencies are added via the addDependency()
982          * method.
983          *
984          * @param animation The animation that the Node encapsulates.
985          */
Node(Animator animation)986         public Node(Animator animation) {
987             this.animation = animation;
988         }
989 
990         /**
991          * Add a dependency to this Node. The dependency includes information about the
992          * node that this node is dependency upon and the nature of the dependency.
993          * @param dependency
994          */
addDependency(Dependency dependency)995         public void addDependency(Dependency dependency) {
996             if (dependencies == null) {
997                 dependencies = new ArrayList<Dependency>();
998                 nodeDependencies = new ArrayList<Node>();
999             }
1000             dependencies.add(dependency);
1001             if (!nodeDependencies.contains(dependency.node)) {
1002                 nodeDependencies.add(dependency.node);
1003             }
1004             Node dependencyNode = dependency.node;
1005             if (dependencyNode.nodeDependents == null) {
1006                 dependencyNode.nodeDependents = new ArrayList<Node>();
1007             }
1008             dependencyNode.nodeDependents.add(this);
1009         }
1010 
1011         @Override
clone()1012         public Node clone() {
1013             try {
1014                 Node node = (Node) super.clone();
1015                 node.animation = (Animator) animation.clone();
1016                 return node;
1017             } catch (CloneNotSupportedException e) {
1018                throw new AssertionError();
1019             }
1020         }
1021     }
1022 
1023     /**
1024      * The <code>Builder</code> object is a utility class to facilitate adding animations to a
1025      * <code>AnimatorSet</code> along with the relationships between the various animations. The
1026      * intention of the <code>Builder</code> methods, along with the {@link
1027      * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible
1028      * to express the dependency relationships of animations in a natural way. Developers can also
1029      * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
1030      * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
1031      * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
1032      * <p/>
1033      * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
1034      * internally via a call to {@link AnimatorSet#play(Animator)}.</p>
1035      * <p/>
1036      * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to
1037      * play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
1038      * <pre>
1039      *     AnimatorSet s = new AnimatorSet();
1040      *     s.play(anim1).with(anim2);
1041      *     s.play(anim2).before(anim3);
1042      *     s.play(anim4).after(anim3);
1043      * </pre>
1044      * <p/>
1045      * <p>Note in the example that both {@link Builder#before(Animator)} and {@link
1046      * Builder#after(Animator)} are used. These are just different ways of expressing the same
1047      * relationship and are provided to make it easier to say things in a way that is more natural,
1048      * depending on the situation.</p>
1049      * <p/>
1050      * <p>It is possible to make several calls into the same <code>Builder</code> object to express
1051      * multiple relationships. However, note that it is only the animation passed into the initial
1052      * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
1053      * calls to the <code>Builder</code> object. For example, the following code starts both anim2
1054      * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
1055      * anim3:
1056      * <pre>
1057      *   AnimatorSet s = new AnimatorSet();
1058      *   s.play(anim1).before(anim2).before(anim3);
1059      * </pre>
1060      * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
1061      * relationship correctly:</p>
1062      * <pre>
1063      *   AnimatorSet s = new AnimatorSet();
1064      *   s.play(anim1).before(anim2);
1065      *   s.play(anim2).before(anim3);
1066      * </pre>
1067      * <p/>
1068      * <p>Note that it is possible to express relationships that cannot be resolved and will not
1069      * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
1070      * sense. In general, circular dependencies like this one (or more indirect ones where a depends
1071      * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
1072      * that can boil down to a simple, one-way relationship of animations starting with, before, and
1073      * after other, different, animations.</p>
1074      */
1075     public class Builder {
1076 
1077         /**
1078          * This tracks the current node being processed. It is supplied to the play() method
1079          * of AnimatorSet and passed into the constructor of Builder.
1080          */
1081         private Node mCurrentNode;
1082 
1083         /**
1084          * package-private constructor. Builders are only constructed by AnimatorSet, when the
1085          * play() method is called.
1086          *
1087          * @param anim The animation that is the dependency for the other animations passed into
1088          * the other methods of this Builder object.
1089          */
Builder(Animator anim)1090         Builder(Animator anim) {
1091             mCurrentNode = mNodeMap.get(anim);
1092             if (mCurrentNode == null) {
1093                 mCurrentNode = new Node(anim);
1094                 mNodeMap.put(anim, mCurrentNode);
1095                 mNodes.add(mCurrentNode);
1096             }
1097         }
1098 
1099         /**
1100          * Sets up the given animation to play at the same time as the animation supplied in the
1101          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
1102          *
1103          * @param anim The animation that will play when the animation supplied to the
1104          * {@link AnimatorSet#play(Animator)} method starts.
1105          */
with(Animator anim)1106         public Builder with(Animator anim) {
1107             Node node = mNodeMap.get(anim);
1108             if (node == null) {
1109                 node = new Node(anim);
1110                 mNodeMap.put(anim, node);
1111                 mNodes.add(node);
1112             }
1113             Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH);
1114             node.addDependency(dependency);
1115             return this;
1116         }
1117 
1118         /**
1119          * Sets up the given animation to play when the animation supplied in the
1120          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
1121          * ends.
1122          *
1123          * @param anim The animation that will play when the animation supplied to the
1124          * {@link AnimatorSet#play(Animator)} method ends.
1125          */
before(Animator anim)1126         public Builder before(Animator anim) {
1127             Node node = mNodeMap.get(anim);
1128             if (node == null) {
1129                 node = new Node(anim);
1130                 mNodeMap.put(anim, node);
1131                 mNodes.add(node);
1132             }
1133             Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER);
1134             node.addDependency(dependency);
1135             return this;
1136         }
1137 
1138         /**
1139          * Sets up the given animation to play when the animation supplied in the
1140          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
1141          * to start when the animation supplied in this method call ends.
1142          *
1143          * @param anim The animation whose end will cause the animation supplied to the
1144          * {@link AnimatorSet#play(Animator)} method to play.
1145          */
after(Animator anim)1146         public Builder after(Animator anim) {
1147             Node node = mNodeMap.get(anim);
1148             if (node == null) {
1149                 node = new Node(anim);
1150                 mNodeMap.put(anim, node);
1151                 mNodes.add(node);
1152             }
1153             Dependency dependency = new Dependency(node, Dependency.AFTER);
1154             mCurrentNode.addDependency(dependency);
1155             return this;
1156         }
1157 
1158         /**
1159          * Sets up the animation supplied in the
1160          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
1161          * to play when the given amount of time elapses.
1162          *
1163          * @param delay The number of milliseconds that should elapse before the
1164          * animation starts.
1165          */
after(long delay)1166         public Builder after(long delay) {
1167             // setup dummy ValueAnimator just to run the clock
1168             ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
1169             anim.setDuration(delay);
1170             after(anim);
1171             return this;
1172         }
1173 
1174     }
1175 
1176 }
1177