blob: c5a4171687a126784d59ab1d5f4dedfdadb743a4 [file] [log] [blame]
Chet Haase17fb4b02010-06-28 17:55:07 -07001/*
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
17package android.animation;
18
19import java.util.ArrayList;
Chet Haase37a7bec2010-11-30 15:55:39 -080020import java.util.Collection;
Chet Haase17fb4b02010-06-28 17:55:07 -070021import java.util.HashMap;
Chet Haase37a7bec2010-11-30 15:55:39 -080022import java.util.List;
Chet Haase17fb4b02010-06-28 17:55:07 -070023
24/**
Chet Haasea18a86b2010-09-07 13:20:00 -070025 * This class plays a set of {@link Animator} objects in the specified order. Animations
Chet Haase17fb4b02010-06-28 17:55:07 -070026 * can be set up to play together, in sequence, or after a specified delay.
27 *
Chet Haasea18a86b2010-09-07 13:20:00 -070028 * <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}
Chet Haase17fb4b02010-06-28 17:55:07 -070033 * class to add animations
34 * one by one.</p>
35 *
Chet Haasea18a86b2010-09-07 13:20:00 -070036 * <p>It is possible to set up a <code>AnimatorSet</code> with circular dependencies between
Chet Haase17fb4b02010-06-28 17:55:07 -070037 * 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.
Joe Fernandez3aef8e1d2011-12-20 10:38:34 -080042 *
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>
Chet Haase17fb4b02010-06-28 17:55:07 -070049 */
Chet Haasea18a86b2010-09-07 13:20:00 -070050public final class AnimatorSet extends Animator {
Chet Haase17fb4b02010-06-28 17:55:07 -070051
52 /**
Chet Haase49afa5b2010-08-23 11:39:53 -070053 * 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 /**
Chet Haase3b69b6f2010-07-29 09:09:05 -070060 * Tracks animations currently being played, so that we know what to
Chet Haasea18a86b2010-09-07 13:20:00 -070061 * cancel or end when cancel() or end() is called on this AnimatorSet
Chet Haase17fb4b02010-06-28 17:55:07 -070062 */
Chet Haasea18a86b2010-09-07 13:20:00 -070063 private ArrayList<Animator> mPlayingSet = new ArrayList<Animator>();
Chet Haase17fb4b02010-06-28 17:55:07 -070064
65 /**
Chet Haasea18a86b2010-09-07 13:20:00 -070066 * 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
Chet Haase17fb4b02010-06-28 17:55:07 -070069 * if one already exists.
70 */
Chet Haasea18a86b2010-09-07 13:20:00 -070071 private HashMap<Animator, Node> mNodeMap = new HashMap<Animator, Node>();
Chet Haase17fb4b02010-06-28 17:55:07 -070072
73 /**
Chet Haasea18a86b2010-09-07 13:20:00 -070074 * 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
Chet Haase17fb4b02010-06-28 17:55:07 -070076 * sortedNodes collection.
77 */
Chet Haase49afa5b2010-08-23 11:39:53 -070078 private ArrayList<Node> mNodes = new ArrayList<Node>();
Chet Haase17fb4b02010-06-28 17:55:07 -070079
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 */
Chet Haase49afa5b2010-08-23 11:39:53 -070085 private ArrayList<Node> mSortedNodes = new ArrayList<Node>();
Chet Haase17fb4b02010-06-28 17:55:07 -070086
87 /**
Chet Haase17fb4b02010-06-28 17:55:07 -070088 * 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
Chet Haasea18a86b2010-09-07 13:20:00 -070094 private AnimatorSetListener mSetListener = null;
Chet Haase17fb4b02010-06-28 17:55:07 -070095
96 /**
Chet Haase7dfacdb2011-07-11 17:01:56 -070097 * Flag indicating that the AnimatorSet has been manually
98 * terminated (by calling cancel() or end()).
Chet Haase010dbaa2010-07-19 17:29:49 -070099 * This flag is used to avoid starting other animations when currently-playing
Chet Haase7dfacdb2011-07-11 17:01:56 -0700100 * child animations of this AnimatorSet end. It also determines whether cancel/end
101 * notifications are sent out via the normal AnimatorSetListener mechanism.
Chet Haase010dbaa2010-07-19 17:29:49 -0700102 */
Chet Haase7dfacdb2011-07-11 17:01:56 -0700103 boolean mTerminated = false;
Chet Haase010dbaa2010-07-19 17:29:49 -0700104
Chet Haase8b699792011-08-05 15:20:19 -0700105 /**
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
Chet Haase21cd1382010-09-01 17:42:29 -0700111 // The amount of time in ms to delay starting the animation after start() is called
112 private long mStartDelay = 0;
113
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800114 // Animator used for a nonzero startDelay
115 private ValueAnimator mDelayAnim = null;
116
Chet Haase21cd1382010-09-01 17:42:29 -0700117
118 // How long the child animations should last in ms. The default value is negative, which
Chet Haasea18a86b2010-09-07 13:20:00 -0700119 // simply means that there is no duration set on the AnimatorSet. When a real duration is
Chet Haase21cd1382010-09-01 17:42:29 -0700120 // set, it is passed along to the child animations.
121 private long mDuration = -1;
122
123
Chet Haase010dbaa2010-07-19 17:29:49 -0700124 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700125 * Sets up this AnimatorSet to play all of the supplied animations at the same time.
Chet Haase17fb4b02010-06-28 17:55:07 -0700126 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700127 * @param items The animations that will be started simultaneously.
Chet Haase17fb4b02010-06-28 17:55:07 -0700128 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700129 public void playTogether(Animator... items) {
130 if (items != null) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700131 mNeedsSort = true;
Chet Haasea18a86b2010-09-07 13:20:00 -0700132 Builder builder = play(items[0]);
133 for (int i = 1; i < items.length; ++i) {
134 builder.with(items[i]);
Chet Haase17fb4b02010-06-28 17:55:07 -0700135 }
136 }
137 }
138
139 /**
Chet Haase37a7bec2010-11-30 15:55:39 -0800140 * Sets up this AnimatorSet to play all of the supplied animations at the same time.
141 *
142 * @param items The animations that will be started simultaneously.
143 */
144 public void playTogether(Collection<Animator> items) {
145 if (items != null && items.size() > 0) {
146 mNeedsSort = true;
147 Builder builder = null;
148 for (Animator anim : items) {
149 if (builder == null) {
150 builder = play(anim);
151 } else {
152 builder.with(anim);
153 }
154 }
155 }
156 }
157
158 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700159 * Sets up this AnimatorSet to play each of the supplied animations when the
Chet Haase17fb4b02010-06-28 17:55:07 -0700160 * previous animation ends.
161 *
Chet Haase37a7bec2010-11-30 15:55:39 -0800162 * @param items The animations that will be started one after another.
Chet Haase17fb4b02010-06-28 17:55:07 -0700163 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700164 public void playSequentially(Animator... items) {
165 if (items != null) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700166 mNeedsSort = true;
Chet Haasea18a86b2010-09-07 13:20:00 -0700167 if (items.length == 1) {
168 play(items[0]);
Chet Haase17fb4b02010-06-28 17:55:07 -0700169 } else {
Chet Haasea18a86b2010-09-07 13:20:00 -0700170 for (int i = 0; i < items.length - 1; ++i) {
171 play(items[i]).before(items[i+1]);
Chet Haase17fb4b02010-06-28 17:55:07 -0700172 }
173 }
174 }
175 }
176
177 /**
Chet Haase37a7bec2010-11-30 15:55:39 -0800178 * Sets up this AnimatorSet to play each of the supplied animations when the
179 * previous animation ends.
180 *
181 * @param items The animations that will be started one after another.
182 */
183 public void playSequentially(List<Animator> items) {
184 if (items != null && items.size() > 0) {
185 mNeedsSort = true;
186 if (items.size() == 1) {
187 play(items.get(0));
188 } else {
189 for (int i = 0; i < items.size() - 1; ++i) {
190 play(items.get(i)).before(items.get(i+1));
191 }
192 }
193 }
194 }
195
196 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700197 * Returns the current list of child Animator objects controlled by this
198 * AnimatorSet. This is a copy of the internal list; modifications to the returned list
199 * will not affect the AnimatorSet, although changes to the underlying Animator objects
200 * will affect those objects being managed by the AnimatorSet.
Chet Haasef54a8d72010-07-22 14:44:59 -0700201 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700202 * @return ArrayList<Animator> The list of child animations of this AnimatorSet.
Chet Haasef54a8d72010-07-22 14:44:59 -0700203 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700204 public ArrayList<Animator> getChildAnimations() {
205 ArrayList<Animator> childList = new ArrayList<Animator>();
Chet Haasef54a8d72010-07-22 14:44:59 -0700206 for (Node node : mNodes) {
207 childList.add(node.animation);
208 }
209 return childList;
210 }
211
212 /**
Chet Haase811ed1062010-08-06 10:38:15 -0700213 * Sets the target object for all current {@link #getChildAnimations() child animations}
Chet Haasea18a86b2010-09-07 13:20:00 -0700214 * of this AnimatorSet that take targets ({@link ObjectAnimator} and
215 * AnimatorSet).
Chet Haase811ed1062010-08-06 10:38:15 -0700216 *
217 * @param target The object being animated
218 */
Chet Haase21cd1382010-09-01 17:42:29 -0700219 @Override
Chet Haase811ed1062010-08-06 10:38:15 -0700220 public void setTarget(Object target) {
221 for (Node node : mNodes) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700222 Animator animation = node.animation;
223 if (animation instanceof AnimatorSet) {
224 ((AnimatorSet)animation).setTarget(target);
225 } else if (animation instanceof ObjectAnimator) {
226 ((ObjectAnimator)animation).setTarget(target);
Chet Haase811ed1062010-08-06 10:38:15 -0700227 }
228 }
229 }
230
231 /**
Chet Haasee0ee2e92010-10-07 09:06:18 -0700232 * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations}
Chet Haasea18a86b2010-09-07 13:20:00 -0700233 * of this AnimatorSet.
Chet Haase21cd1382010-09-01 17:42:29 -0700234 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700235 * @param interpolator the interpolator to be used by each child animation of this AnimatorSet
Chet Haase21cd1382010-09-01 17:42:29 -0700236 */
237 @Override
Chet Haasee0ee2e92010-10-07 09:06:18 -0700238 public void setInterpolator(TimeInterpolator interpolator) {
Chet Haase21cd1382010-09-01 17:42:29 -0700239 for (Node node : mNodes) {
240 node.animation.setInterpolator(interpolator);
241 }
242 }
243
244 /**
Chet Haase17fb4b02010-06-28 17:55:07 -0700245 * This method creates a <code>Builder</code> object, which is used to
246 * set up playing constraints. This initial <code>play()</code> method
247 * tells the <code>Builder</code> the animation that is the dependency for
248 * the succeeding commands to the <code>Builder</code>. For example,
Chet Haasea18a86b2010-09-07 13:20:00 -0700249 * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play
Chet Haase17fb4b02010-06-28 17:55:07 -0700250 * <code>a1</code> and <code>a2</code> at the same time,
Chet Haasea18a86b2010-09-07 13:20:00 -0700251 * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play
Chet Haase17fb4b02010-06-28 17:55:07 -0700252 * <code>a1</code> first, followed by <code>a2</code>, and
Chet Haasea18a86b2010-09-07 13:20:00 -0700253 * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play
Chet Haase17fb4b02010-06-28 17:55:07 -0700254 * <code>a2</code> first, followed by <code>a1</code>.
255 *
256 * <p>Note that <code>play()</code> is the only way to tell the
257 * <code>Builder</code> the animation upon which the dependency is created,
258 * so successive calls to the various functions in <code>Builder</code>
259 * will all refer to the initial parameter supplied in <code>play()</code>
260 * as the dependency of the other animations. For example, calling
261 * <code>play(a1).before(a2).before(a3)</code> will play both <code>a2</code>
262 * and <code>a3</code> when a1 ends; it does not set up a dependency between
263 * <code>a2</code> and <code>a3</code>.</p>
264 *
265 * @param anim The animation that is the dependency used in later calls to the
266 * methods in the returned <code>Builder</code> object. A null parameter will result
267 * in a null <code>Builder</code> return value.
Chet Haasea18a86b2010-09-07 13:20:00 -0700268 * @return Builder The object that constructs the AnimatorSet based on the dependencies
Chet Haase17fb4b02010-06-28 17:55:07 -0700269 * outlined in the calls to <code>play</code> and the other methods in the
270 * <code>Builder</code object.
271 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700272 public Builder play(Animator anim) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700273 if (anim != null) {
274 mNeedsSort = true;
275 return new Builder(anim);
276 }
277 return null;
278 }
279
280 /**
281 * {@inheritDoc}
282 *
Chet Haase8b699792011-08-05 15:20:19 -0700283 * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it
284 * is responsible for.</p>
Chet Haase17fb4b02010-06-28 17:55:07 -0700285 */
286 @SuppressWarnings("unchecked")
287 @Override
288 public void cancel() {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700289 mTerminated = true;
Chet Haase8b699792011-08-05 15:20:19 -0700290 if (isStarted()) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700291 ArrayList<AnimatorListener> tmpListeners = null;
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800292 if (mListeners != null) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700293 tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
294 for (AnimatorListener listener : tmpListeners) {
295 listener.onAnimationCancel(this);
296 }
297 }
298 if (mDelayAnim != null && mDelayAnim.isRunning()) {
299 // If we're currently in the startDelay period, just cancel that animator and
300 // send out the end event to all listeners
301 mDelayAnim.cancel();
302 } else if (mSortedNodes.size() > 0) {
303 for (Node node : mSortedNodes) {
304 node.animation.cancel();
305 }
306 }
307 if (tmpListeners != null) {
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800308 for (AnimatorListener listener : tmpListeners) {
309 listener.onAnimationEnd(this);
310 }
311 }
Chet Haase8b699792011-08-05 15:20:19 -0700312 mStarted = false;
Chet Haase17fb4b02010-06-28 17:55:07 -0700313 }
314 }
315
316 /**
317 * {@inheritDoc}
318 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700319 * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is
Chet Haase17fb4b02010-06-28 17:55:07 -0700320 * responsible for.</p>
321 */
322 @Override
323 public void end() {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700324 mTerminated = true;
Chet Haase8b699792011-08-05 15:20:19 -0700325 if (isStarted()) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700326 if (mSortedNodes.size() != mNodes.size()) {
327 // hasn't been started yet - sort the nodes now, then end them
328 sortNodes();
329 for (Node node : mSortedNodes) {
330 if (mSetListener == null) {
331 mSetListener = new AnimatorSetListener(this);
332 }
333 node.animation.addListener(mSetListener);
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700334 }
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700335 }
Chet Haase7dfacdb2011-07-11 17:01:56 -0700336 if (mDelayAnim != null) {
337 mDelayAnim.cancel();
338 }
339 if (mSortedNodes.size() > 0) {
340 for (Node node : mSortedNodes) {
341 node.animation.end();
342 }
343 }
344 if (mListeners != null) {
345 ArrayList<AnimatorListener> tmpListeners =
346 (ArrayList<AnimatorListener>) mListeners.clone();
347 for (AnimatorListener listener : tmpListeners) {
348 listener.onAnimationEnd(this);
349 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700350 }
Chet Haase8b699792011-08-05 15:20:19 -0700351 mStarted = false;
Chet Haase17fb4b02010-06-28 17:55:07 -0700352 }
353 }
354
355 /**
Chet Haase8b699792011-08-05 15:20:19 -0700356 * Returns true if any of the child animations of this AnimatorSet have been started and have
357 * not yet ended.
Chet Haasea18a86b2010-09-07 13:20:00 -0700358 * @return Whether this AnimatorSet has been started and has not yet ended.
Chet Haase673e42f2010-08-25 16:32:37 -0700359 */
360 @Override
361 public boolean isRunning() {
362 for (Node node : mNodes) {
363 if (node.animation.isRunning()) {
364 return true;
365 }
366 }
Chet Haase8b699792011-08-05 15:20:19 -0700367 return false;
368 }
369
370 @Override
371 public boolean isStarted() {
372 return mStarted;
Chet Haase673e42f2010-08-25 16:32:37 -0700373 }
374
375 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700376 * The amount of time, in milliseconds, to delay starting the animation after
377 * {@link #start()} is called.
378 *
379 * @return the number of milliseconds to delay running the animation
380 */
381 @Override
382 public long getStartDelay() {
383 return mStartDelay;
384 }
385
386 /**
387 * The amount of time, in milliseconds, to delay starting the animation after
388 * {@link #start()} is called.
389
390 * @param startDelay The amount of the delay, in milliseconds
391 */
392 @Override
393 public void setStartDelay(long startDelay) {
394 mStartDelay = startDelay;
395 }
396
397 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700398 * Gets the length of each of the child animations of this AnimatorSet. This value may
399 * be less than 0, which indicates that no duration has been set on this AnimatorSet
Chet Haase21cd1382010-09-01 17:42:29 -0700400 * and each of the child animations will use their own duration.
401 *
402 * @return The length of the animation, in milliseconds, of each of the child
Chet Haasea18a86b2010-09-07 13:20:00 -0700403 * animations of this AnimatorSet.
Chet Haase21cd1382010-09-01 17:42:29 -0700404 */
405 @Override
406 public long getDuration() {
407 return mDuration;
408 }
409
410 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700411 * Sets the length of each of the current child animations of this AnimatorSet. By default,
412 * each child animation will use its own duration. If the duration is set on the AnimatorSet,
Chet Haase21cd1382010-09-01 17:42:29 -0700413 * then each child animation inherits this duration.
414 *
415 * @param duration The length of the animation, in milliseconds, of each of the child
Chet Haasea18a86b2010-09-07 13:20:00 -0700416 * animations of this AnimatorSet.
Chet Haase21cd1382010-09-01 17:42:29 -0700417 */
418 @Override
Chet Haase2794eb32010-10-12 16:29:28 -0700419 public AnimatorSet setDuration(long duration) {
Chet Haase21cd1382010-09-01 17:42:29 -0700420 if (duration < 0) {
421 throw new IllegalArgumentException("duration must be a value of zero or greater");
422 }
423 for (Node node : mNodes) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700424 // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
Chet Haase21cd1382010-09-01 17:42:29 -0700425 // insert "play-after" delays
426 node.animation.setDuration(duration);
427 }
428 mDuration = duration;
Chet Haase2794eb32010-10-12 16:29:28 -0700429 return this;
Chet Haase21cd1382010-09-01 17:42:29 -0700430 }
431
Chet Haase2970c492010-11-09 13:58:04 -0800432 @Override
433 public void setupStartValues() {
434 for (Node node : mNodes) {
435 node.animation.setupStartValues();
436 }
437 }
438
439 @Override
440 public void setupEndValues() {
441 for (Node node : mNodes) {
442 node.animation.setupEndValues();
443 }
444 }
445
Chet Haase21cd1382010-09-01 17:42:29 -0700446 /**
Chet Haase17fb4b02010-06-28 17:55:07 -0700447 * {@inheritDoc}
448 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700449 * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which
Chet Haase17fb4b02010-06-28 17:55:07 -0700450 * it is responsible. The details of when exactly those animations are started depends on
451 * the dependency relationships that have been set up between the animations.
452 */
453 @SuppressWarnings("unchecked")
454 @Override
455 public void start() {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700456 mTerminated = false;
Chet Haase8b699792011-08-05 15:20:19 -0700457 mStarted = true;
Chet Haase010dbaa2010-07-19 17:29:49 -0700458
Chet Haase17fb4b02010-06-28 17:55:07 -0700459 // First, sort the nodes (if necessary). This will ensure that sortedNodes
460 // contains the animation nodes in the correct order.
461 sortNodes();
462
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800463 int numSortedNodes = mSortedNodes.size();
464 for (int i = 0; i < numSortedNodes; ++i) {
465 Node node = mSortedNodes.get(i);
466 // First, clear out the old listeners
467 ArrayList<AnimatorListener> oldListeners = node.animation.getListeners();
468 if (oldListeners != null && oldListeners.size() > 0) {
Martijn Coenend45204b2011-07-29 15:16:19 -0500469 final ArrayList<AnimatorListener> clonedListeners = new
470 ArrayList<AnimatorListener>(oldListeners);
471
472 for (AnimatorListener listener : clonedListeners) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700473 if (listener instanceof DependencyListener ||
474 listener instanceof AnimatorSetListener) {
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800475 node.animation.removeListener(listener);
476 }
477 }
478 }
479 }
480
Chet Haase17fb4b02010-06-28 17:55:07 -0700481 // nodesToStart holds the list of nodes to be started immediately. We don't want to
482 // start the animations in the loop directly because we first need to set up
483 // dependencies on all of the nodes. For example, we don't want to start an animation
484 // when some other animation also wants to start when the first animation begins.
Chet Haase21cd1382010-09-01 17:42:29 -0700485 final ArrayList<Node> nodesToStart = new ArrayList<Node>();
Chet Haase7c608f22010-10-22 17:54:04 -0700486 for (int i = 0; i < numSortedNodes; ++i) {
487 Node node = mSortedNodes.get(i);
Chet Haasea18a86b2010-09-07 13:20:00 -0700488 if (mSetListener == null) {
489 mSetListener = new AnimatorSetListener(this);
Chet Haase17fb4b02010-06-28 17:55:07 -0700490 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700491 if (node.dependencies == null || node.dependencies.size() == 0) {
492 nodesToStart.add(node);
493 } else {
Chet Haase7c608f22010-10-22 17:54:04 -0700494 int numDependencies = node.dependencies.size();
495 for (int j = 0; j < numDependencies; ++j) {
496 Dependency dependency = node.dependencies.get(j);
Chet Haase17fb4b02010-06-28 17:55:07 -0700497 dependency.node.animation.addListener(
Chet Haase010dbaa2010-07-19 17:29:49 -0700498 new DependencyListener(this, node, dependency.rule));
Chet Haase17fb4b02010-06-28 17:55:07 -0700499 }
500 node.tmpDependencies = (ArrayList<Dependency>) node.dependencies.clone();
501 }
Chet Haasea18a86b2010-09-07 13:20:00 -0700502 node.animation.addListener(mSetListener);
Chet Haase17fb4b02010-06-28 17:55:07 -0700503 }
504 // Now that all dependencies are set up, start the animations that should be started.
Chet Haase21cd1382010-09-01 17:42:29 -0700505 if (mStartDelay <= 0) {
506 for (Node node : nodesToStart) {
507 node.animation.start();
508 mPlayingSet.add(node.animation);
509 }
510 } else {
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800511 mDelayAnim = ValueAnimator.ofFloat(0f, 1f);
512 mDelayAnim.setDuration(mStartDelay);
513 mDelayAnim.addListener(new AnimatorListenerAdapter() {
514 boolean canceled = false;
515 public void onAnimationCancel(Animator anim) {
516 canceled = true;
517 }
Chet Haasea18a86b2010-09-07 13:20:00 -0700518 public void onAnimationEnd(Animator anim) {
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800519 if (!canceled) {
520 int numNodes = nodesToStart.size();
521 for (int i = 0; i < numNodes; ++i) {
522 Node node = nodesToStart.get(i);
523 node.animation.start();
524 mPlayingSet.add(node.animation);
525 }
Chet Haase21cd1382010-09-01 17:42:29 -0700526 }
527 }
528 });
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800529 mDelayAnim.start();
Chet Haase17fb4b02010-06-28 17:55:07 -0700530 }
531 if (mListeners != null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700532 ArrayList<AnimatorListener> tmpListeners =
533 (ArrayList<AnimatorListener>) mListeners.clone();
Chet Haase7c608f22010-10-22 17:54:04 -0700534 int numListeners = tmpListeners.size();
535 for (int i = 0; i < numListeners; ++i) {
536 tmpListeners.get(i).onAnimationStart(this);
Chet Haase8b699792011-08-05 15:20:19 -0700537 }
538 }
539 if (mNodes.size() == 0 && mStartDelay == 0) {
540 // Handle unusual case where empty AnimatorSet is started - should send out
541 // end event immediately since the event will not be sent out at all otherwise
542 mStarted = false;
543 if (mListeners != null) {
544 ArrayList<AnimatorListener> tmpListeners =
545 (ArrayList<AnimatorListener>) mListeners.clone();
546 int numListeners = tmpListeners.size();
547 for (int i = 0; i < numListeners; ++i) {
Chet Haase2970c492010-11-09 13:58:04 -0800548 tmpListeners.get(i).onAnimationEnd(this);
549 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700550 }
551 }
552 }
553
Chet Haase49afa5b2010-08-23 11:39:53 -0700554 @Override
Chet Haasea18a86b2010-09-07 13:20:00 -0700555 public AnimatorSet clone() {
556 final AnimatorSet anim = (AnimatorSet) super.clone();
Chet Haase49afa5b2010-08-23 11:39:53 -0700557 /*
558 * The basic clone() operation copies all items. This doesn't work very well for
Chet Haasea18a86b2010-09-07 13:20:00 -0700559 * AnimatorSet, because it will copy references that need to be recreated and state
Chet Haase49afa5b2010-08-23 11:39:53 -0700560 * that may not apply. What we need to do now is put the clone in an uninitialized
561 * state, with fresh, empty data structures. Then we will build up the nodes list
562 * manually, as we clone each Node (and its animation). The clone will then be sorted,
563 * and will populate any appropriate lists, when it is started.
564 */
565 anim.mNeedsSort = true;
Chet Haase7dfacdb2011-07-11 17:01:56 -0700566 anim.mTerminated = false;
Chet Haase8b699792011-08-05 15:20:19 -0700567 anim.mStarted = false;
Chet Haasea18a86b2010-09-07 13:20:00 -0700568 anim.mPlayingSet = new ArrayList<Animator>();
569 anim.mNodeMap = new HashMap<Animator, Node>();
Chet Haase49afa5b2010-08-23 11:39:53 -0700570 anim.mNodes = new ArrayList<Node>();
571 anim.mSortedNodes = new ArrayList<Node>();
572
573 // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
Chet Haasea18a86b2010-09-07 13:20:00 -0700574 // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
Chet Haase49afa5b2010-08-23 11:39:53 -0700575 // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
576 HashMap<Node, Node> nodeCloneMap = new HashMap<Node, Node>(); // <old, new>
577 for (Node node : mNodes) {
578 Node nodeClone = node.clone();
579 nodeCloneMap.put(node, nodeClone);
580 anim.mNodes.add(nodeClone);
581 anim.mNodeMap.put(nodeClone.animation, nodeClone);
582 // Clear out the dependencies in the clone; we'll set these up manually later
583 nodeClone.dependencies = null;
584 nodeClone.tmpDependencies = null;
585 nodeClone.nodeDependents = null;
586 nodeClone.nodeDependencies = null;
Chet Haasea18a86b2010-09-07 13:20:00 -0700587 // clear out any listeners that were set up by the AnimatorSet; these will
Chet Haase49afa5b2010-08-23 11:39:53 -0700588 // be set up when the clone's nodes are sorted
Chet Haasea18a86b2010-09-07 13:20:00 -0700589 ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners();
Chet Haase49afa5b2010-08-23 11:39:53 -0700590 if (cloneListeners != null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700591 ArrayList<AnimatorListener> listenersToRemove = null;
592 for (AnimatorListener listener : cloneListeners) {
593 if (listener instanceof AnimatorSetListener) {
Chet Haase49afa5b2010-08-23 11:39:53 -0700594 if (listenersToRemove == null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700595 listenersToRemove = new ArrayList<AnimatorListener>();
Chet Haase49afa5b2010-08-23 11:39:53 -0700596 }
597 listenersToRemove.add(listener);
598 }
599 }
600 if (listenersToRemove != null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700601 for (AnimatorListener listener : listenersToRemove) {
Chet Haase49afa5b2010-08-23 11:39:53 -0700602 cloneListeners.remove(listener);
603 }
604 }
605 }
606 }
607 // Now that we've cloned all of the nodes, we're ready to walk through their
608 // dependencies, mapping the old dependencies to the new nodes
609 for (Node node : mNodes) {
610 Node nodeClone = nodeCloneMap.get(node);
611 if (node.dependencies != null) {
612 for (Dependency dependency : node.dependencies) {
613 Node clonedDependencyNode = nodeCloneMap.get(dependency.node);
614 Dependency cloneDependency = new Dependency(clonedDependencyNode,
615 dependency.rule);
616 nodeClone.addDependency(cloneDependency);
617 }
618 }
619 }
620
621 return anim;
622 }
623
Chet Haase17fb4b02010-06-28 17:55:07 -0700624 /**
625 * This class is the mechanism by which animations are started based on events in other
626 * animations. If an animation has multiple dependencies on other animations, then
627 * all dependencies must be satisfied before the animation is started.
628 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700629 private static class DependencyListener implements AnimatorListener {
Chet Haase17fb4b02010-06-28 17:55:07 -0700630
Chet Haasea18a86b2010-09-07 13:20:00 -0700631 private AnimatorSet mAnimatorSet;
Chet Haase010dbaa2010-07-19 17:29:49 -0700632
Chet Haase17fb4b02010-06-28 17:55:07 -0700633 // The node upon which the dependency is based.
634 private Node mNode;
635
636 // The Dependency rule (WITH or AFTER) that the listener should wait for on
637 // the node
638 private int mRule;
639
Chet Haasea18a86b2010-09-07 13:20:00 -0700640 public DependencyListener(AnimatorSet animatorSet, Node node, int rule) {
641 this.mAnimatorSet = animatorSet;
Chet Haase17fb4b02010-06-28 17:55:07 -0700642 this.mNode = node;
643 this.mRule = rule;
644 }
645
646 /**
Chet Haase010dbaa2010-07-19 17:29:49 -0700647 * Ignore cancel events for now. We may want to handle this eventually,
648 * to prevent follow-on animations from running when some dependency
649 * animation is canceled.
Chet Haase17fb4b02010-06-28 17:55:07 -0700650 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700651 public void onAnimationCancel(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700652 }
653
654 /**
655 * An end event is received - see if this is an event we are listening for
656 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700657 public void onAnimationEnd(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700658 if (mRule == Dependency.AFTER) {
659 startIfReady(animation);
660 }
661 }
662
663 /**
664 * Ignore repeat events for now
665 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700666 public void onAnimationRepeat(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700667 }
668
669 /**
670 * A start event is received - see if this is an event we are listening for
671 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700672 public void onAnimationStart(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700673 if (mRule == Dependency.WITH) {
674 startIfReady(animation);
675 }
676 }
677
678 /**
679 * Check whether the event received is one that the node was waiting for.
680 * If so, mark it as complete and see whether it's time to start
681 * the animation.
682 * @param dependencyAnimation the animation that sent the event.
683 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700684 private void startIfReady(Animator dependencyAnimation) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700685 if (mAnimatorSet.mTerminated) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700686 // if the parent AnimatorSet was canceled, then don't start any dependent anims
Chet Haase010dbaa2010-07-19 17:29:49 -0700687 return;
688 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700689 Dependency dependencyToRemove = null;
Chet Haase7c608f22010-10-22 17:54:04 -0700690 int numDependencies = mNode.tmpDependencies.size();
691 for (int i = 0; i < numDependencies; ++i) {
692 Dependency dependency = mNode.tmpDependencies.get(i);
Chet Haase17fb4b02010-06-28 17:55:07 -0700693 if (dependency.rule == mRule &&
694 dependency.node.animation == dependencyAnimation) {
695 // rule fired - remove the dependency and listener and check to
696 // see whether it's time to start the animation
697 dependencyToRemove = dependency;
698 dependencyAnimation.removeListener(this);
699 break;
700 }
701 }
702 mNode.tmpDependencies.remove(dependencyToRemove);
703 if (mNode.tmpDependencies.size() == 0) {
704 // all dependencies satisfied: start the animation
705 mNode.animation.start();
Chet Haasea18a86b2010-09-07 13:20:00 -0700706 mAnimatorSet.mPlayingSet.add(mNode.animation);
Chet Haase17fb4b02010-06-28 17:55:07 -0700707 }
708 }
709
710 }
711
Chet Haasea18a86b2010-09-07 13:20:00 -0700712 private class AnimatorSetListener implements AnimatorListener {
Chet Haase17fb4b02010-06-28 17:55:07 -0700713
Chet Haasea18a86b2010-09-07 13:20:00 -0700714 private AnimatorSet mAnimatorSet;
Chet Haase17fb4b02010-06-28 17:55:07 -0700715
Chet Haasea18a86b2010-09-07 13:20:00 -0700716 AnimatorSetListener(AnimatorSet animatorSet) {
717 mAnimatorSet = animatorSet;
Chet Haase17fb4b02010-06-28 17:55:07 -0700718 }
719
Chet Haasea18a86b2010-09-07 13:20:00 -0700720 public void onAnimationCancel(Animator animation) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700721 if (!mTerminated) {
722 // Listeners are already notified of the AnimatorSet canceling in cancel().
723 // The logic below only kicks in when animations end normally
724 if (mPlayingSet.size() == 0) {
725 if (mListeners != null) {
726 int numListeners = mListeners.size();
727 for (int i = 0; i < numListeners; ++i) {
728 mListeners.get(i).onAnimationCancel(mAnimatorSet);
729 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700730 }
731 }
732 }
733 }
734
735 @SuppressWarnings("unchecked")
Chet Haasea18a86b2010-09-07 13:20:00 -0700736 public void onAnimationEnd(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700737 animation.removeListener(this);
738 mPlayingSet.remove(animation);
Chet Haasea18a86b2010-09-07 13:20:00 -0700739 Node animNode = mAnimatorSet.mNodeMap.get(animation);
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700740 animNode.done = true;
Chet Haase7dfacdb2011-07-11 17:01:56 -0700741 if (!mTerminated) {
742 // Listeners are already notified of the AnimatorSet ending in cancel() or
743 // end(); the logic below only kicks in when animations end normally
744 ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes;
745 boolean allDone = true;
746 int numSortedNodes = sortedNodes.size();
747 for (int i = 0; i < numSortedNodes; ++i) {
748 if (!sortedNodes.get(i).done) {
749 allDone = false;
750 break;
751 }
Chet Haasea6e4a1752010-07-23 11:08:47 -0700752 }
Chet Haase7dfacdb2011-07-11 17:01:56 -0700753 if (allDone) {
754 // If this was the last child animation to end, then notify listeners that this
755 // AnimatorSet has ended
756 if (mListeners != null) {
757 ArrayList<AnimatorListener> tmpListeners =
758 (ArrayList<AnimatorListener>) mListeners.clone();
759 int numListeners = tmpListeners.size();
760 for (int i = 0; i < numListeners; ++i) {
761 tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
762 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700763 }
Chet Haase8b699792011-08-05 15:20:19 -0700764 mAnimatorSet.mStarted = false;
Chet Haase17fb4b02010-06-28 17:55:07 -0700765 }
766 }
767 }
768
769 // Nothing to do
Chet Haasea18a86b2010-09-07 13:20:00 -0700770 public void onAnimationRepeat(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700771 }
772
773 // Nothing to do
Chet Haasea18a86b2010-09-07 13:20:00 -0700774 public void onAnimationStart(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700775 }
776
777 }
778
779 /**
780 * This method sorts the current set of nodes, if needed. The sort is a simple
781 * DependencyGraph sort, which goes like this:
782 * - All nodes without dependencies become 'roots'
783 * - while roots list is not null
784 * - for each root r
785 * - add r to sorted list
786 * - remove r as a dependency from any other node
787 * - any nodes with no dependencies are added to the roots list
788 */
789 private void sortNodes() {
790 if (mNeedsSort) {
791 mSortedNodes.clear();
792 ArrayList<Node> roots = new ArrayList<Node>();
Chet Haase7c608f22010-10-22 17:54:04 -0700793 int numNodes = mNodes.size();
794 for (int i = 0; i < numNodes; ++i) {
795 Node node = mNodes.get(i);
Chet Haase17fb4b02010-06-28 17:55:07 -0700796 if (node.dependencies == null || node.dependencies.size() == 0) {
797 roots.add(node);
798 }
799 }
800 ArrayList<Node> tmpRoots = new ArrayList<Node>();
801 while (roots.size() > 0) {
Chet Haase7c608f22010-10-22 17:54:04 -0700802 int numRoots = roots.size();
803 for (int i = 0; i < numRoots; ++i) {
804 Node root = roots.get(i);
Chet Haase17fb4b02010-06-28 17:55:07 -0700805 mSortedNodes.add(root);
806 if (root.nodeDependents != null) {
Chet Haase7c608f22010-10-22 17:54:04 -0700807 int numDependents = root.nodeDependents.size();
808 for (int j = 0; j < numDependents; ++j) {
809 Node node = root.nodeDependents.get(j);
Chet Haase17fb4b02010-06-28 17:55:07 -0700810 node.nodeDependencies.remove(root);
811 if (node.nodeDependencies.size() == 0) {
812 tmpRoots.add(node);
813 }
814 }
815 }
816 }
Chet Haase010dbaa2010-07-19 17:29:49 -0700817 roots.clear();
Chet Haase17fb4b02010-06-28 17:55:07 -0700818 roots.addAll(tmpRoots);
819 tmpRoots.clear();
820 }
821 mNeedsSort = false;
822 if (mSortedNodes.size() != mNodes.size()) {
823 throw new IllegalStateException("Circular dependencies cannot exist"
Chet Haasea18a86b2010-09-07 13:20:00 -0700824 + " in AnimatorSet");
Chet Haase17fb4b02010-06-28 17:55:07 -0700825 }
826 } else {
827 // Doesn't need sorting, but still need to add in the nodeDependencies list
828 // because these get removed as the event listeners fire and the dependencies
829 // are satisfied
Chet Haase7c608f22010-10-22 17:54:04 -0700830 int numNodes = mNodes.size();
831 for (int i = 0; i < numNodes; ++i) {
832 Node node = mNodes.get(i);
Chet Haase17fb4b02010-06-28 17:55:07 -0700833 if (node.dependencies != null && node.dependencies.size() > 0) {
Chet Haase7c608f22010-10-22 17:54:04 -0700834 int numDependencies = node.dependencies.size();
835 for (int j = 0; j < numDependencies; ++j) {
836 Dependency dependency = node.dependencies.get(j);
Chet Haase17fb4b02010-06-28 17:55:07 -0700837 if (node.nodeDependencies == null) {
838 node.nodeDependencies = new ArrayList<Node>();
839 }
840 if (!node.nodeDependencies.contains(dependency.node)) {
841 node.nodeDependencies.add(dependency.node);
842 }
843 }
844 }
Chet Haase7dfacdb2011-07-11 17:01:56 -0700845 // nodes are 'done' by default; they become un-done when started, and done
846 // again when ended
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700847 node.done = false;
Chet Haase17fb4b02010-06-28 17:55:07 -0700848 }
849 }
850 }
851
852 /**
853 * Dependency holds information about the node that some other node is
854 * dependent upon and the nature of that dependency.
855 *
856 */
857 private static class Dependency {
858 static final int WITH = 0; // dependent node must start with this dependency node
859 static final int AFTER = 1; // dependent node must start when this dependency node finishes
860
861 // The node that the other node with this Dependency is dependent upon
862 public Node node;
863
864 // The nature of the dependency (WITH or AFTER)
865 public int rule;
866
867 public Dependency(Node node, int rule) {
868 this.node = node;
869 this.rule = rule;
870 }
871 }
872
873 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700874 * A Node is an embodiment of both the Animator that it wraps as well as
Chet Haase17fb4b02010-06-28 17:55:07 -0700875 * any dependencies that are associated with that Animation. This includes
876 * both dependencies upon other nodes (in the dependencies list) as
877 * well as dependencies of other nodes upon this (in the nodeDependents list).
878 */
Chet Haase49afa5b2010-08-23 11:39:53 -0700879 private static class Node implements Cloneable {
Chet Haasea18a86b2010-09-07 13:20:00 -0700880 public Animator animation;
Chet Haase17fb4b02010-06-28 17:55:07 -0700881
882 /**
883 * These are the dependencies that this node's animation has on other
884 * nodes. For example, if this node's animation should begin with some
885 * other animation ends, then there will be an item in this node's
886 * dependencies list for that other animation's node.
887 */
888 public ArrayList<Dependency> dependencies = null;
889
890 /**
891 * tmpDependencies is a runtime detail. We use the dependencies list for sorting.
892 * But we also use the list to keep track of when multiple dependencies are satisfied,
893 * but removing each dependency as it is satisfied. We do not want to remove
894 * the dependency itself from the list, because we need to retain that information
Chet Haasea18a86b2010-09-07 13:20:00 -0700895 * if the AnimatorSet is launched in the future. So we create a copy of the dependency
896 * list when the AnimatorSet starts and use this tmpDependencies list to track the
Chet Haase17fb4b02010-06-28 17:55:07 -0700897 * list of satisfied dependencies.
898 */
899 public ArrayList<Dependency> tmpDependencies = null;
900
901 /**
902 * nodeDependencies is just a list of the nodes that this Node is dependent upon.
903 * This information is used in sortNodes(), to determine when a node is a root.
904 */
905 public ArrayList<Node> nodeDependencies = null;
906
907 /**
908 * nodeDepdendents is the list of nodes that have this node as a dependency. This
909 * is a utility field used in sortNodes to facilitate removing this node as a
910 * dependency when it is a root node.
911 */
912 public ArrayList<Node> nodeDependents = null;
913
914 /**
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700915 * Flag indicating whether the animation in this node is finished. This flag
Chet Haasea18a86b2010-09-07 13:20:00 -0700916 * is used by AnimatorSet to check, as each animation ends, whether all child animations
917 * are done and it's time to send out an end event for the entire AnimatorSet.
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700918 */
919 public boolean done = false;
920
921 /**
Chet Haase17fb4b02010-06-28 17:55:07 -0700922 * Constructs the Node with the animation that it encapsulates. A Node has no
923 * dependencies by default; dependencies are added via the addDependency()
924 * method.
925 *
926 * @param animation The animation that the Node encapsulates.
927 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700928 public Node(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700929 this.animation = animation;
930 }
931
932 /**
933 * Add a dependency to this Node. The dependency includes information about the
934 * node that this node is dependency upon and the nature of the dependency.
935 * @param dependency
936 */
937 public void addDependency(Dependency dependency) {
938 if (dependencies == null) {
939 dependencies = new ArrayList<Dependency>();
940 nodeDependencies = new ArrayList<Node>();
941 }
942 dependencies.add(dependency);
943 if (!nodeDependencies.contains(dependency.node)) {
944 nodeDependencies.add(dependency.node);
945 }
946 Node dependencyNode = dependency.node;
947 if (dependencyNode.nodeDependents == null) {
948 dependencyNode.nodeDependents = new ArrayList<Node>();
949 }
950 dependencyNode.nodeDependents.add(this);
951 }
Chet Haase49afa5b2010-08-23 11:39:53 -0700952
953 @Override
Chet Haase21cd1382010-09-01 17:42:29 -0700954 public Node clone() {
955 try {
956 Node node = (Node) super.clone();
Chet Haasea18a86b2010-09-07 13:20:00 -0700957 node.animation = (Animator) animation.clone();
Chet Haase21cd1382010-09-01 17:42:29 -0700958 return node;
959 } catch (CloneNotSupportedException e) {
960 throw new AssertionError();
961 }
Chet Haase49afa5b2010-08-23 11:39:53 -0700962 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700963 }
964
965 /**
966 * The <code>Builder</code> object is a utility class to facilitate adding animations to a
Chet Haasea18a86b2010-09-07 13:20:00 -0700967 * <code>AnimatorSet</code> along with the relationships between the various animations. The
Chet Haase17fb4b02010-06-28 17:55:07 -0700968 * intention of the <code>Builder</code> methods, along with the {@link
Chet Haase8b699792011-08-05 15:20:19 -0700969 * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible
970 * to express the dependency relationships of animations in a natural way. Developers can also
971 * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
Chet Haasea18a86b2010-09-07 13:20:00 -0700972 * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
973 * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
Chet Haase17fb4b02010-06-28 17:55:07 -0700974 * <p/>
975 * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
Chet Haasea18a86b2010-09-07 13:20:00 -0700976 * internally via a call to {@link AnimatorSet#play(Animator)}.</p>
Chet Haase17fb4b02010-06-28 17:55:07 -0700977 * <p/>
Chet Haasea18a86b2010-09-07 13:20:00 -0700978 * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to
Chet Haase17fb4b02010-06-28 17:55:07 -0700979 * play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
980 * <pre>
Chet Haasea18a86b2010-09-07 13:20:00 -0700981 * AnimatorSet s = new AnimatorSet();
Chet Haase17fb4b02010-06-28 17:55:07 -0700982 * s.play(anim1).with(anim2);
983 * s.play(anim2).before(anim3);
984 * s.play(anim4).after(anim3);
985 * </pre>
986 * <p/>
Chet Haasea18a86b2010-09-07 13:20:00 -0700987 * <p>Note in the example that both {@link Builder#before(Animator)} and {@link
988 * Builder#after(Animator)} are used. These are just different ways of expressing the same
Chet Haase17fb4b02010-06-28 17:55:07 -0700989 * relationship and are provided to make it easier to say things in a way that is more natural,
990 * depending on the situation.</p>
991 * <p/>
992 * <p>It is possible to make several calls into the same <code>Builder</code> object to express
993 * multiple relationships. However, note that it is only the animation passed into the initial
Chet Haasea18a86b2010-09-07 13:20:00 -0700994 * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
Chet Haase17fb4b02010-06-28 17:55:07 -0700995 * calls to the <code>Builder</code> object. For example, the following code starts both anim2
996 * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
997 * anim3:
998 * <pre>
Chet Haasea18a86b2010-09-07 13:20:00 -0700999 * AnimatorSet s = new AnimatorSet();
Chet Haase17fb4b02010-06-28 17:55:07 -07001000 * s.play(anim1).before(anim2).before(anim3);
1001 * </pre>
1002 * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
1003 * relationship correctly:</p>
1004 * <pre>
Chet Haasea18a86b2010-09-07 13:20:00 -07001005 * AnimatorSet s = new AnimatorSet();
Chet Haase17fb4b02010-06-28 17:55:07 -07001006 * s.play(anim1).before(anim2);
1007 * s.play(anim2).before(anim3);
1008 * </pre>
1009 * <p/>
1010 * <p>Note that it is possible to express relationships that cannot be resolved and will not
1011 * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
1012 * sense. In general, circular dependencies like this one (or more indirect ones where a depends
Chet Haasea18a86b2010-09-07 13:20:00 -07001013 * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
1014 * that can boil down to a simple, one-way relationship of animations starting with, before, and
Chet Haase17fb4b02010-06-28 17:55:07 -07001015 * after other, different, animations.</p>
1016 */
1017 public class Builder {
1018
1019 /**
1020 * This tracks the current node being processed. It is supplied to the play() method
Chet Haasea18a86b2010-09-07 13:20:00 -07001021 * of AnimatorSet and passed into the constructor of Builder.
Chet Haase17fb4b02010-06-28 17:55:07 -07001022 */
1023 private Node mCurrentNode;
1024
1025 /**
Chet Haasea18a86b2010-09-07 13:20:00 -07001026 * package-private constructor. Builders are only constructed by AnimatorSet, when the
Chet Haase17fb4b02010-06-28 17:55:07 -07001027 * play() method is called.
1028 *
1029 * @param anim The animation that is the dependency for the other animations passed into
1030 * the other methods of this Builder object.
1031 */
Chet Haasea18a86b2010-09-07 13:20:00 -07001032 Builder(Animator anim) {
Chet Haase17fb4b02010-06-28 17:55:07 -07001033 mCurrentNode = mNodeMap.get(anim);
1034 if (mCurrentNode == null) {
1035 mCurrentNode = new Node(anim);
1036 mNodeMap.put(anim, mCurrentNode);
1037 mNodes.add(mCurrentNode);
1038 }
1039 }
1040
1041 /**
1042 * Sets up the given animation to play at the same time as the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001043 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
Chet Haase17fb4b02010-06-28 17:55:07 -07001044 *
1045 * @param anim The animation that will play when the animation supplied to the
Chet Haasea18a86b2010-09-07 13:20:00 -07001046 * {@link AnimatorSet#play(Animator)} method starts.
Chet Haase17fb4b02010-06-28 17:55:07 -07001047 */
Chet Haase2970c492010-11-09 13:58:04 -08001048 public Builder with(Animator anim) {
Chet Haase17fb4b02010-06-28 17:55:07 -07001049 Node node = mNodeMap.get(anim);
1050 if (node == null) {
1051 node = new Node(anim);
1052 mNodeMap.put(anim, node);
1053 mNodes.add(node);
1054 }
1055 Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH);
1056 node.addDependency(dependency);
Chet Haase2970c492010-11-09 13:58:04 -08001057 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001058 }
1059
1060 /**
1061 * Sets up the given animation to play when the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001062 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
Chet Haase17fb4b02010-06-28 17:55:07 -07001063 * ends.
1064 *
1065 * @param anim The animation that will play when the animation supplied to the
Chet Haasea18a86b2010-09-07 13:20:00 -07001066 * {@link AnimatorSet#play(Animator)} method ends.
Chet Haase17fb4b02010-06-28 17:55:07 -07001067 */
Chet Haase2970c492010-11-09 13:58:04 -08001068 public Builder before(Animator anim) {
Chet Haase17fb4b02010-06-28 17:55:07 -07001069 Node node = mNodeMap.get(anim);
1070 if (node == null) {
1071 node = new Node(anim);
1072 mNodeMap.put(anim, node);
1073 mNodes.add(node);
1074 }
1075 Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER);
1076 node.addDependency(dependency);
Chet Haase2970c492010-11-09 13:58:04 -08001077 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001078 }
1079
1080 /**
1081 * Sets up the given animation to play when the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001082 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
Chet Haase17fb4b02010-06-28 17:55:07 -07001083 * to start when the animation supplied in this method call ends.
1084 *
1085 * @param anim The animation whose end will cause the animation supplied to the
Chet Haasea18a86b2010-09-07 13:20:00 -07001086 * {@link AnimatorSet#play(Animator)} method to play.
Chet Haase17fb4b02010-06-28 17:55:07 -07001087 */
Chet Haase2970c492010-11-09 13:58:04 -08001088 public Builder after(Animator anim) {
Chet Haase17fb4b02010-06-28 17:55:07 -07001089 Node node = mNodeMap.get(anim);
1090 if (node == null) {
1091 node = new Node(anim);
1092 mNodeMap.put(anim, node);
1093 mNodes.add(node);
1094 }
1095 Dependency dependency = new Dependency(node, Dependency.AFTER);
1096 mCurrentNode.addDependency(dependency);
Chet Haase2970c492010-11-09 13:58:04 -08001097 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001098 }
1099
1100 /**
1101 * Sets up the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001102 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
Chet Haase17fb4b02010-06-28 17:55:07 -07001103 * to play when the given amount of time elapses.
1104 *
1105 * @param delay The number of milliseconds that should elapse before the
1106 * animation starts.
1107 */
Chet Haase2970c492010-11-09 13:58:04 -08001108 public Builder after(long delay) {
Chet Haasea18a86b2010-09-07 13:20:00 -07001109 // setup dummy ValueAnimator just to run the clock
Chet Haase2794eb32010-10-12 16:29:28 -07001110 ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
1111 anim.setDuration(delay);
1112 after(anim);
Chet Haase2970c492010-11-09 13:58:04 -08001113 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001114 }
1115
1116 }
1117
1118}