blob: 0b68dd86b6c73d5afeb0477cdee80e7d2dd42614 [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.
42 */
Chet Haasea18a86b2010-09-07 13:20:00 -070043public final class AnimatorSet extends Animator {
Chet Haase17fb4b02010-06-28 17:55:07 -070044
45 /**
Chet Haase49afa5b2010-08-23 11:39:53 -070046 * Internal variables
47 * NOTE: This object implements the clone() method, making a deep copy of any referenced
48 * objects. As other non-trivial fields are added to this class, make sure to add logic
49 * to clone() to make deep copies of them.
50 */
51
52 /**
Chet Haase3b69b6f2010-07-29 09:09:05 -070053 * Tracks animations currently being played, so that we know what to
Chet Haasea18a86b2010-09-07 13:20:00 -070054 * cancel or end when cancel() or end() is called on this AnimatorSet
Chet Haase17fb4b02010-06-28 17:55:07 -070055 */
Chet Haasea18a86b2010-09-07 13:20:00 -070056 private ArrayList<Animator> mPlayingSet = new ArrayList<Animator>();
Chet Haase17fb4b02010-06-28 17:55:07 -070057
58 /**
Chet Haasea18a86b2010-09-07 13:20:00 -070059 * Contains all nodes, mapped to their respective Animators. When new
60 * dependency information is added for an Animator, we want to add it
61 * to a single node representing that Animator, not create a new Node
Chet Haase17fb4b02010-06-28 17:55:07 -070062 * if one already exists.
63 */
Chet Haasea18a86b2010-09-07 13:20:00 -070064 private HashMap<Animator, Node> mNodeMap = new HashMap<Animator, Node>();
Chet Haase17fb4b02010-06-28 17:55:07 -070065
66 /**
Chet Haasea18a86b2010-09-07 13:20:00 -070067 * Set of all nodes created for this AnimatorSet. This list is used upon
68 * starting the set, and the nodes are placed in sorted order into the
Chet Haase17fb4b02010-06-28 17:55:07 -070069 * sortedNodes collection.
70 */
Chet Haase49afa5b2010-08-23 11:39:53 -070071 private ArrayList<Node> mNodes = new ArrayList<Node>();
Chet Haase17fb4b02010-06-28 17:55:07 -070072
73 /**
74 * The sorted list of nodes. This is the order in which the animations will
75 * be played. The details about when exactly they will be played depend
76 * on the dependency relationships of the nodes.
77 */
Chet Haase49afa5b2010-08-23 11:39:53 -070078 private ArrayList<Node> mSortedNodes = new ArrayList<Node>();
Chet Haase17fb4b02010-06-28 17:55:07 -070079
80 /**
Chet Haase17fb4b02010-06-28 17:55:07 -070081 * Flag indicating whether the nodes should be sorted prior to playing. This
82 * flag allows us to cache the previous sorted nodes so that if the sequence
83 * is replayed with no changes, it does not have to re-sort the nodes again.
84 */
85 private boolean mNeedsSort = true;
86
Chet Haasea18a86b2010-09-07 13:20:00 -070087 private AnimatorSetListener mSetListener = null;
Chet Haase17fb4b02010-06-28 17:55:07 -070088
89 /**
Chet Haase7dfacdb2011-07-11 17:01:56 -070090 * Flag indicating that the AnimatorSet has been manually
91 * terminated (by calling cancel() or end()).
Chet Haase010dbaa2010-07-19 17:29:49 -070092 * This flag is used to avoid starting other animations when currently-playing
Chet Haase7dfacdb2011-07-11 17:01:56 -070093 * child animations of this AnimatorSet end. It also determines whether cancel/end
94 * notifications are sent out via the normal AnimatorSetListener mechanism.
Chet Haase010dbaa2010-07-19 17:29:49 -070095 */
Chet Haase7dfacdb2011-07-11 17:01:56 -070096 boolean mTerminated = false;
Chet Haase010dbaa2010-07-19 17:29:49 -070097
Chet Haase8b699792011-08-05 15:20:19 -070098 /**
99 * Indicates whether an AnimatorSet has been start()'d, whether or
100 * not there is a nonzero startDelay.
101 */
102 private boolean mStarted = false;
103
Chet Haase21cd1382010-09-01 17:42:29 -0700104 // The amount of time in ms to delay starting the animation after start() is called
105 private long mStartDelay = 0;
106
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800107 // Animator used for a nonzero startDelay
108 private ValueAnimator mDelayAnim = null;
109
Chet Haase21cd1382010-09-01 17:42:29 -0700110
111 // How long the child animations should last in ms. The default value is negative, which
Chet Haasea18a86b2010-09-07 13:20:00 -0700112 // simply means that there is no duration set on the AnimatorSet. When a real duration is
Chet Haase21cd1382010-09-01 17:42:29 -0700113 // set, it is passed along to the child animations.
114 private long mDuration = -1;
115
116
Chet Haase010dbaa2010-07-19 17:29:49 -0700117 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700118 * Sets up this AnimatorSet to play all of the supplied animations at the same time.
Chet Haase17fb4b02010-06-28 17:55:07 -0700119 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700120 * @param items The animations that will be started simultaneously.
Chet Haase17fb4b02010-06-28 17:55:07 -0700121 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700122 public void playTogether(Animator... items) {
123 if (items != null) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700124 mNeedsSort = true;
Chet Haasea18a86b2010-09-07 13:20:00 -0700125 Builder builder = play(items[0]);
126 for (int i = 1; i < items.length; ++i) {
127 builder.with(items[i]);
Chet Haase17fb4b02010-06-28 17:55:07 -0700128 }
129 }
130 }
131
132 /**
Chet Haase37a7bec2010-11-30 15:55:39 -0800133 * Sets up this AnimatorSet to play all of the supplied animations at the same time.
134 *
135 * @param items The animations that will be started simultaneously.
136 */
137 public void playTogether(Collection<Animator> items) {
138 if (items != null && items.size() > 0) {
139 mNeedsSort = true;
140 Builder builder = null;
141 for (Animator anim : items) {
142 if (builder == null) {
143 builder = play(anim);
144 } else {
145 builder.with(anim);
146 }
147 }
148 }
149 }
150
151 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700152 * Sets up this AnimatorSet to play each of the supplied animations when the
Chet Haase17fb4b02010-06-28 17:55:07 -0700153 * previous animation ends.
154 *
Chet Haase37a7bec2010-11-30 15:55:39 -0800155 * @param items The animations that will be started one after another.
Chet Haase17fb4b02010-06-28 17:55:07 -0700156 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700157 public void playSequentially(Animator... items) {
158 if (items != null) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700159 mNeedsSort = true;
Chet Haasea18a86b2010-09-07 13:20:00 -0700160 if (items.length == 1) {
161 play(items[0]);
Chet Haase17fb4b02010-06-28 17:55:07 -0700162 } else {
Chet Haasea18a86b2010-09-07 13:20:00 -0700163 for (int i = 0; i < items.length - 1; ++i) {
164 play(items[i]).before(items[i+1]);
Chet Haase17fb4b02010-06-28 17:55:07 -0700165 }
166 }
167 }
168 }
169
170 /**
Chet Haase37a7bec2010-11-30 15:55:39 -0800171 * Sets up this AnimatorSet to play each of the supplied animations when the
172 * previous animation ends.
173 *
174 * @param items The animations that will be started one after another.
175 */
176 public void playSequentially(List<Animator> items) {
177 if (items != null && items.size() > 0) {
178 mNeedsSort = true;
179 if (items.size() == 1) {
180 play(items.get(0));
181 } else {
182 for (int i = 0; i < items.size() - 1; ++i) {
183 play(items.get(i)).before(items.get(i+1));
184 }
185 }
186 }
187 }
188
189 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700190 * Returns the current list of child Animator objects controlled by this
191 * AnimatorSet. This is a copy of the internal list; modifications to the returned list
192 * will not affect the AnimatorSet, although changes to the underlying Animator objects
193 * will affect those objects being managed by the AnimatorSet.
Chet Haasef54a8d72010-07-22 14:44:59 -0700194 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700195 * @return ArrayList<Animator> The list of child animations of this AnimatorSet.
Chet Haasef54a8d72010-07-22 14:44:59 -0700196 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700197 public ArrayList<Animator> getChildAnimations() {
198 ArrayList<Animator> childList = new ArrayList<Animator>();
Chet Haasef54a8d72010-07-22 14:44:59 -0700199 for (Node node : mNodes) {
200 childList.add(node.animation);
201 }
202 return childList;
203 }
204
205 /**
Chet Haase811ed1062010-08-06 10:38:15 -0700206 * Sets the target object for all current {@link #getChildAnimations() child animations}
Chet Haasea18a86b2010-09-07 13:20:00 -0700207 * of this AnimatorSet that take targets ({@link ObjectAnimator} and
208 * AnimatorSet).
Chet Haase811ed1062010-08-06 10:38:15 -0700209 *
210 * @param target The object being animated
211 */
Chet Haase21cd1382010-09-01 17:42:29 -0700212 @Override
Chet Haase811ed1062010-08-06 10:38:15 -0700213 public void setTarget(Object target) {
214 for (Node node : mNodes) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700215 Animator animation = node.animation;
216 if (animation instanceof AnimatorSet) {
217 ((AnimatorSet)animation).setTarget(target);
218 } else if (animation instanceof ObjectAnimator) {
219 ((ObjectAnimator)animation).setTarget(target);
Chet Haase811ed1062010-08-06 10:38:15 -0700220 }
221 }
222 }
223
224 /**
Chet Haasee0ee2e92010-10-07 09:06:18 -0700225 * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations}
Chet Haasea18a86b2010-09-07 13:20:00 -0700226 * of this AnimatorSet.
Chet Haase21cd1382010-09-01 17:42:29 -0700227 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700228 * @param interpolator the interpolator to be used by each child animation of this AnimatorSet
Chet Haase21cd1382010-09-01 17:42:29 -0700229 */
230 @Override
Chet Haasee0ee2e92010-10-07 09:06:18 -0700231 public void setInterpolator(TimeInterpolator interpolator) {
Chet Haase21cd1382010-09-01 17:42:29 -0700232 for (Node node : mNodes) {
233 node.animation.setInterpolator(interpolator);
234 }
235 }
236
237 /**
Chet Haase17fb4b02010-06-28 17:55:07 -0700238 * This method creates a <code>Builder</code> object, which is used to
239 * set up playing constraints. This initial <code>play()</code> method
240 * tells the <code>Builder</code> the animation that is the dependency for
241 * the succeeding commands to the <code>Builder</code>. For example,
Chet Haasea18a86b2010-09-07 13:20:00 -0700242 * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play
Chet Haase17fb4b02010-06-28 17:55:07 -0700243 * <code>a1</code> and <code>a2</code> at the same time,
Chet Haasea18a86b2010-09-07 13:20:00 -0700244 * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play
Chet Haase17fb4b02010-06-28 17:55:07 -0700245 * <code>a1</code> first, followed by <code>a2</code>, and
Chet Haasea18a86b2010-09-07 13:20:00 -0700246 * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play
Chet Haase17fb4b02010-06-28 17:55:07 -0700247 * <code>a2</code> first, followed by <code>a1</code>.
248 *
249 * <p>Note that <code>play()</code> is the only way to tell the
250 * <code>Builder</code> the animation upon which the dependency is created,
251 * so successive calls to the various functions in <code>Builder</code>
252 * will all refer to the initial parameter supplied in <code>play()</code>
253 * as the dependency of the other animations. For example, calling
254 * <code>play(a1).before(a2).before(a3)</code> will play both <code>a2</code>
255 * and <code>a3</code> when a1 ends; it does not set up a dependency between
256 * <code>a2</code> and <code>a3</code>.</p>
257 *
258 * @param anim The animation that is the dependency used in later calls to the
259 * methods in the returned <code>Builder</code> object. A null parameter will result
260 * in a null <code>Builder</code> return value.
Chet Haasea18a86b2010-09-07 13:20:00 -0700261 * @return Builder The object that constructs the AnimatorSet based on the dependencies
Chet Haase17fb4b02010-06-28 17:55:07 -0700262 * outlined in the calls to <code>play</code> and the other methods in the
263 * <code>Builder</code object.
264 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700265 public Builder play(Animator anim) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700266 if (anim != null) {
267 mNeedsSort = true;
268 return new Builder(anim);
269 }
270 return null;
271 }
272
273 /**
274 * {@inheritDoc}
275 *
Chet Haase8b699792011-08-05 15:20:19 -0700276 * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it
277 * is responsible for.</p>
Chet Haase17fb4b02010-06-28 17:55:07 -0700278 */
279 @SuppressWarnings("unchecked")
280 @Override
281 public void cancel() {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700282 mTerminated = true;
Chet Haase8b699792011-08-05 15:20:19 -0700283 if (isStarted()) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700284 ArrayList<AnimatorListener> tmpListeners = null;
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800285 if (mListeners != null) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700286 tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
287 for (AnimatorListener listener : tmpListeners) {
288 listener.onAnimationCancel(this);
289 }
290 }
291 if (mDelayAnim != null && mDelayAnim.isRunning()) {
292 // If we're currently in the startDelay period, just cancel that animator and
293 // send out the end event to all listeners
294 mDelayAnim.cancel();
295 } else if (mSortedNodes.size() > 0) {
296 for (Node node : mSortedNodes) {
297 node.animation.cancel();
298 }
299 }
300 if (tmpListeners != null) {
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800301 for (AnimatorListener listener : tmpListeners) {
302 listener.onAnimationEnd(this);
303 }
304 }
Chet Haase8b699792011-08-05 15:20:19 -0700305 mStarted = false;
Chet Haase17fb4b02010-06-28 17:55:07 -0700306 }
307 }
308
309 /**
310 * {@inheritDoc}
311 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700312 * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is
Chet Haase17fb4b02010-06-28 17:55:07 -0700313 * responsible for.</p>
314 */
315 @Override
316 public void end() {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700317 mTerminated = true;
Chet Haase8b699792011-08-05 15:20:19 -0700318 if (isStarted()) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700319 if (mSortedNodes.size() != mNodes.size()) {
320 // hasn't been started yet - sort the nodes now, then end them
321 sortNodes();
322 for (Node node : mSortedNodes) {
323 if (mSetListener == null) {
324 mSetListener = new AnimatorSetListener(this);
325 }
326 node.animation.addListener(mSetListener);
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700327 }
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700328 }
Chet Haase7dfacdb2011-07-11 17:01:56 -0700329 if (mDelayAnim != null) {
330 mDelayAnim.cancel();
331 }
332 if (mSortedNodes.size() > 0) {
333 for (Node node : mSortedNodes) {
334 node.animation.end();
335 }
336 }
337 if (mListeners != null) {
338 ArrayList<AnimatorListener> tmpListeners =
339 (ArrayList<AnimatorListener>) mListeners.clone();
340 for (AnimatorListener listener : tmpListeners) {
341 listener.onAnimationEnd(this);
342 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700343 }
Chet Haase8b699792011-08-05 15:20:19 -0700344 mStarted = false;
Chet Haase17fb4b02010-06-28 17:55:07 -0700345 }
346 }
347
348 /**
Chet Haase8b699792011-08-05 15:20:19 -0700349 * Returns true if any of the child animations of this AnimatorSet have been started and have
350 * not yet ended.
Chet Haasea18a86b2010-09-07 13:20:00 -0700351 * @return Whether this AnimatorSet has been started and has not yet ended.
Chet Haase673e42f2010-08-25 16:32:37 -0700352 */
353 @Override
354 public boolean isRunning() {
355 for (Node node : mNodes) {
356 if (node.animation.isRunning()) {
357 return true;
358 }
359 }
Chet Haase8b699792011-08-05 15:20:19 -0700360 return false;
361 }
362
363 @Override
364 public boolean isStarted() {
365 return mStarted;
Chet Haase673e42f2010-08-25 16:32:37 -0700366 }
367
368 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700369 * The amount of time, in milliseconds, to delay starting the animation after
370 * {@link #start()} is called.
371 *
372 * @return the number of milliseconds to delay running the animation
373 */
374 @Override
375 public long getStartDelay() {
376 return mStartDelay;
377 }
378
379 /**
380 * The amount of time, in milliseconds, to delay starting the animation after
381 * {@link #start()} is called.
382
383 * @param startDelay The amount of the delay, in milliseconds
384 */
385 @Override
386 public void setStartDelay(long startDelay) {
387 mStartDelay = startDelay;
388 }
389
390 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700391 * Gets the length of each of the child animations of this AnimatorSet. This value may
392 * be less than 0, which indicates that no duration has been set on this AnimatorSet
Chet Haase21cd1382010-09-01 17:42:29 -0700393 * and each of the child animations will use their own duration.
394 *
395 * @return The length of the animation, in milliseconds, of each of the child
Chet Haasea18a86b2010-09-07 13:20:00 -0700396 * animations of this AnimatorSet.
Chet Haase21cd1382010-09-01 17:42:29 -0700397 */
398 @Override
399 public long getDuration() {
400 return mDuration;
401 }
402
403 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700404 * Sets the length of each of the current child animations of this AnimatorSet. By default,
405 * each child animation will use its own duration. If the duration is set on the AnimatorSet,
Chet Haase21cd1382010-09-01 17:42:29 -0700406 * then each child animation inherits this duration.
407 *
408 * @param duration The length of the animation, in milliseconds, of each of the child
Chet Haasea18a86b2010-09-07 13:20:00 -0700409 * animations of this AnimatorSet.
Chet Haase21cd1382010-09-01 17:42:29 -0700410 */
411 @Override
Chet Haase2794eb32010-10-12 16:29:28 -0700412 public AnimatorSet setDuration(long duration) {
Chet Haase21cd1382010-09-01 17:42:29 -0700413 if (duration < 0) {
414 throw new IllegalArgumentException("duration must be a value of zero or greater");
415 }
416 for (Node node : mNodes) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700417 // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
Chet Haase21cd1382010-09-01 17:42:29 -0700418 // insert "play-after" delays
419 node.animation.setDuration(duration);
420 }
421 mDuration = duration;
Chet Haase2794eb32010-10-12 16:29:28 -0700422 return this;
Chet Haase21cd1382010-09-01 17:42:29 -0700423 }
424
Chet Haase2970c492010-11-09 13:58:04 -0800425 @Override
426 public void setupStartValues() {
427 for (Node node : mNodes) {
428 node.animation.setupStartValues();
429 }
430 }
431
432 @Override
433 public void setupEndValues() {
434 for (Node node : mNodes) {
435 node.animation.setupEndValues();
436 }
437 }
438
Chet Haase21cd1382010-09-01 17:42:29 -0700439 /**
Chet Haase17fb4b02010-06-28 17:55:07 -0700440 * {@inheritDoc}
441 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700442 * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which
Chet Haase17fb4b02010-06-28 17:55:07 -0700443 * it is responsible. The details of when exactly those animations are started depends on
444 * the dependency relationships that have been set up between the animations.
445 */
446 @SuppressWarnings("unchecked")
447 @Override
448 public void start() {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700449 mTerminated = false;
Chet Haase8b699792011-08-05 15:20:19 -0700450 mStarted = true;
Chet Haase010dbaa2010-07-19 17:29:49 -0700451
Chet Haase17fb4b02010-06-28 17:55:07 -0700452 // First, sort the nodes (if necessary). This will ensure that sortedNodes
453 // contains the animation nodes in the correct order.
454 sortNodes();
455
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800456 int numSortedNodes = mSortedNodes.size();
457 for (int i = 0; i < numSortedNodes; ++i) {
458 Node node = mSortedNodes.get(i);
459 // First, clear out the old listeners
460 ArrayList<AnimatorListener> oldListeners = node.animation.getListeners();
461 if (oldListeners != null && oldListeners.size() > 0) {
Martijn Coenend45204b2011-07-29 15:16:19 -0500462 final ArrayList<AnimatorListener> clonedListeners = new
463 ArrayList<AnimatorListener>(oldListeners);
464
465 for (AnimatorListener listener : clonedListeners) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700466 if (listener instanceof DependencyListener ||
467 listener instanceof AnimatorSetListener) {
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800468 node.animation.removeListener(listener);
469 }
470 }
471 }
472 }
473
Chet Haase17fb4b02010-06-28 17:55:07 -0700474 // nodesToStart holds the list of nodes to be started immediately. We don't want to
475 // start the animations in the loop directly because we first need to set up
476 // dependencies on all of the nodes. For example, we don't want to start an animation
477 // when some other animation also wants to start when the first animation begins.
Chet Haase21cd1382010-09-01 17:42:29 -0700478 final ArrayList<Node> nodesToStart = new ArrayList<Node>();
Chet Haase7c608f22010-10-22 17:54:04 -0700479 for (int i = 0; i < numSortedNodes; ++i) {
480 Node node = mSortedNodes.get(i);
Chet Haasea18a86b2010-09-07 13:20:00 -0700481 if (mSetListener == null) {
482 mSetListener = new AnimatorSetListener(this);
Chet Haase17fb4b02010-06-28 17:55:07 -0700483 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700484 if (node.dependencies == null || node.dependencies.size() == 0) {
485 nodesToStart.add(node);
486 } else {
Chet Haase7c608f22010-10-22 17:54:04 -0700487 int numDependencies = node.dependencies.size();
488 for (int j = 0; j < numDependencies; ++j) {
489 Dependency dependency = node.dependencies.get(j);
Chet Haase17fb4b02010-06-28 17:55:07 -0700490 dependency.node.animation.addListener(
Chet Haase010dbaa2010-07-19 17:29:49 -0700491 new DependencyListener(this, node, dependency.rule));
Chet Haase17fb4b02010-06-28 17:55:07 -0700492 }
493 node.tmpDependencies = (ArrayList<Dependency>) node.dependencies.clone();
494 }
Chet Haasea18a86b2010-09-07 13:20:00 -0700495 node.animation.addListener(mSetListener);
Chet Haase17fb4b02010-06-28 17:55:07 -0700496 }
497 // Now that all dependencies are set up, start the animations that should be started.
Chet Haase21cd1382010-09-01 17:42:29 -0700498 if (mStartDelay <= 0) {
499 for (Node node : nodesToStart) {
500 node.animation.start();
501 mPlayingSet.add(node.animation);
502 }
503 } else {
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800504 mDelayAnim = ValueAnimator.ofFloat(0f, 1f);
505 mDelayAnim.setDuration(mStartDelay);
506 mDelayAnim.addListener(new AnimatorListenerAdapter() {
507 boolean canceled = false;
508 public void onAnimationCancel(Animator anim) {
509 canceled = true;
510 }
Chet Haasea18a86b2010-09-07 13:20:00 -0700511 public void onAnimationEnd(Animator anim) {
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800512 if (!canceled) {
513 int numNodes = nodesToStart.size();
514 for (int i = 0; i < numNodes; ++i) {
515 Node node = nodesToStart.get(i);
516 node.animation.start();
517 mPlayingSet.add(node.animation);
518 }
Chet Haase21cd1382010-09-01 17:42:29 -0700519 }
520 }
521 });
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800522 mDelayAnim.start();
Chet Haase17fb4b02010-06-28 17:55:07 -0700523 }
524 if (mListeners != null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700525 ArrayList<AnimatorListener> tmpListeners =
526 (ArrayList<AnimatorListener>) mListeners.clone();
Chet Haase7c608f22010-10-22 17:54:04 -0700527 int numListeners = tmpListeners.size();
528 for (int i = 0; i < numListeners; ++i) {
529 tmpListeners.get(i).onAnimationStart(this);
Chet Haase8b699792011-08-05 15:20:19 -0700530 }
531 }
532 if (mNodes.size() == 0 && mStartDelay == 0) {
533 // Handle unusual case where empty AnimatorSet is started - should send out
534 // end event immediately since the event will not be sent out at all otherwise
535 mStarted = false;
536 if (mListeners != null) {
537 ArrayList<AnimatorListener> tmpListeners =
538 (ArrayList<AnimatorListener>) mListeners.clone();
539 int numListeners = tmpListeners.size();
540 for (int i = 0; i < numListeners; ++i) {
Chet Haase2970c492010-11-09 13:58:04 -0800541 tmpListeners.get(i).onAnimationEnd(this);
542 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700543 }
544 }
545 }
546
Chet Haase49afa5b2010-08-23 11:39:53 -0700547 @Override
Chet Haasea18a86b2010-09-07 13:20:00 -0700548 public AnimatorSet clone() {
549 final AnimatorSet anim = (AnimatorSet) super.clone();
Chet Haase49afa5b2010-08-23 11:39:53 -0700550 /*
551 * The basic clone() operation copies all items. This doesn't work very well for
Chet Haasea18a86b2010-09-07 13:20:00 -0700552 * AnimatorSet, because it will copy references that need to be recreated and state
Chet Haase49afa5b2010-08-23 11:39:53 -0700553 * that may not apply. What we need to do now is put the clone in an uninitialized
554 * state, with fresh, empty data structures. Then we will build up the nodes list
555 * manually, as we clone each Node (and its animation). The clone will then be sorted,
556 * and will populate any appropriate lists, when it is started.
557 */
558 anim.mNeedsSort = true;
Chet Haase7dfacdb2011-07-11 17:01:56 -0700559 anim.mTerminated = false;
Chet Haase8b699792011-08-05 15:20:19 -0700560 anim.mStarted = false;
Chet Haasea18a86b2010-09-07 13:20:00 -0700561 anim.mPlayingSet = new ArrayList<Animator>();
562 anim.mNodeMap = new HashMap<Animator, Node>();
Chet Haase49afa5b2010-08-23 11:39:53 -0700563 anim.mNodes = new ArrayList<Node>();
564 anim.mSortedNodes = new ArrayList<Node>();
565
566 // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
Chet Haasea18a86b2010-09-07 13:20:00 -0700567 // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
Chet Haase49afa5b2010-08-23 11:39:53 -0700568 // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
569 HashMap<Node, Node> nodeCloneMap = new HashMap<Node, Node>(); // <old, new>
570 for (Node node : mNodes) {
571 Node nodeClone = node.clone();
572 nodeCloneMap.put(node, nodeClone);
573 anim.mNodes.add(nodeClone);
574 anim.mNodeMap.put(nodeClone.animation, nodeClone);
575 // Clear out the dependencies in the clone; we'll set these up manually later
576 nodeClone.dependencies = null;
577 nodeClone.tmpDependencies = null;
578 nodeClone.nodeDependents = null;
579 nodeClone.nodeDependencies = null;
Chet Haasea18a86b2010-09-07 13:20:00 -0700580 // clear out any listeners that were set up by the AnimatorSet; these will
Chet Haase49afa5b2010-08-23 11:39:53 -0700581 // be set up when the clone's nodes are sorted
Chet Haasea18a86b2010-09-07 13:20:00 -0700582 ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners();
Chet Haase49afa5b2010-08-23 11:39:53 -0700583 if (cloneListeners != null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700584 ArrayList<AnimatorListener> listenersToRemove = null;
585 for (AnimatorListener listener : cloneListeners) {
586 if (listener instanceof AnimatorSetListener) {
Chet Haase49afa5b2010-08-23 11:39:53 -0700587 if (listenersToRemove == null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700588 listenersToRemove = new ArrayList<AnimatorListener>();
Chet Haase49afa5b2010-08-23 11:39:53 -0700589 }
590 listenersToRemove.add(listener);
591 }
592 }
593 if (listenersToRemove != null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700594 for (AnimatorListener listener : listenersToRemove) {
Chet Haase49afa5b2010-08-23 11:39:53 -0700595 cloneListeners.remove(listener);
596 }
597 }
598 }
599 }
600 // Now that we've cloned all of the nodes, we're ready to walk through their
601 // dependencies, mapping the old dependencies to the new nodes
602 for (Node node : mNodes) {
603 Node nodeClone = nodeCloneMap.get(node);
604 if (node.dependencies != null) {
605 for (Dependency dependency : node.dependencies) {
606 Node clonedDependencyNode = nodeCloneMap.get(dependency.node);
607 Dependency cloneDependency = new Dependency(clonedDependencyNode,
608 dependency.rule);
609 nodeClone.addDependency(cloneDependency);
610 }
611 }
612 }
613
614 return anim;
615 }
616
Chet Haase17fb4b02010-06-28 17:55:07 -0700617 /**
618 * This class is the mechanism by which animations are started based on events in other
619 * animations. If an animation has multiple dependencies on other animations, then
620 * all dependencies must be satisfied before the animation is started.
621 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700622 private static class DependencyListener implements AnimatorListener {
Chet Haase17fb4b02010-06-28 17:55:07 -0700623
Chet Haasea18a86b2010-09-07 13:20:00 -0700624 private AnimatorSet mAnimatorSet;
Chet Haase010dbaa2010-07-19 17:29:49 -0700625
Chet Haase17fb4b02010-06-28 17:55:07 -0700626 // The node upon which the dependency is based.
627 private Node mNode;
628
629 // The Dependency rule (WITH or AFTER) that the listener should wait for on
630 // the node
631 private int mRule;
632
Chet Haasea18a86b2010-09-07 13:20:00 -0700633 public DependencyListener(AnimatorSet animatorSet, Node node, int rule) {
634 this.mAnimatorSet = animatorSet;
Chet Haase17fb4b02010-06-28 17:55:07 -0700635 this.mNode = node;
636 this.mRule = rule;
637 }
638
639 /**
Chet Haase010dbaa2010-07-19 17:29:49 -0700640 * Ignore cancel events for now. We may want to handle this eventually,
641 * to prevent follow-on animations from running when some dependency
642 * animation is canceled.
Chet Haase17fb4b02010-06-28 17:55:07 -0700643 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700644 public void onAnimationCancel(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700645 }
646
647 /**
648 * An end event is received - see if this is an event we are listening for
649 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700650 public void onAnimationEnd(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700651 if (mRule == Dependency.AFTER) {
652 startIfReady(animation);
653 }
654 }
655
656 /**
657 * Ignore repeat events for now
658 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700659 public void onAnimationRepeat(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700660 }
661
662 /**
663 * A start event is received - see if this is an event we are listening for
664 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700665 public void onAnimationStart(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700666 if (mRule == Dependency.WITH) {
667 startIfReady(animation);
668 }
669 }
670
671 /**
672 * Check whether the event received is one that the node was waiting for.
673 * If so, mark it as complete and see whether it's time to start
674 * the animation.
675 * @param dependencyAnimation the animation that sent the event.
676 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700677 private void startIfReady(Animator dependencyAnimation) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700678 if (mAnimatorSet.mTerminated) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700679 // if the parent AnimatorSet was canceled, then don't start any dependent anims
Chet Haase010dbaa2010-07-19 17:29:49 -0700680 return;
681 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700682 Dependency dependencyToRemove = null;
Chet Haase7c608f22010-10-22 17:54:04 -0700683 int numDependencies = mNode.tmpDependencies.size();
684 for (int i = 0; i < numDependencies; ++i) {
685 Dependency dependency = mNode.tmpDependencies.get(i);
Chet Haase17fb4b02010-06-28 17:55:07 -0700686 if (dependency.rule == mRule &&
687 dependency.node.animation == dependencyAnimation) {
688 // rule fired - remove the dependency and listener and check to
689 // see whether it's time to start the animation
690 dependencyToRemove = dependency;
691 dependencyAnimation.removeListener(this);
692 break;
693 }
694 }
695 mNode.tmpDependencies.remove(dependencyToRemove);
696 if (mNode.tmpDependencies.size() == 0) {
697 // all dependencies satisfied: start the animation
698 mNode.animation.start();
Chet Haasea18a86b2010-09-07 13:20:00 -0700699 mAnimatorSet.mPlayingSet.add(mNode.animation);
Chet Haase17fb4b02010-06-28 17:55:07 -0700700 }
701 }
702
703 }
704
Chet Haasea18a86b2010-09-07 13:20:00 -0700705 private class AnimatorSetListener implements AnimatorListener {
Chet Haase17fb4b02010-06-28 17:55:07 -0700706
Chet Haasea18a86b2010-09-07 13:20:00 -0700707 private AnimatorSet mAnimatorSet;
Chet Haase17fb4b02010-06-28 17:55:07 -0700708
Chet Haasea18a86b2010-09-07 13:20:00 -0700709 AnimatorSetListener(AnimatorSet animatorSet) {
710 mAnimatorSet = animatorSet;
Chet Haase17fb4b02010-06-28 17:55:07 -0700711 }
712
Chet Haasea18a86b2010-09-07 13:20:00 -0700713 public void onAnimationCancel(Animator animation) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700714 if (!mTerminated) {
715 // Listeners are already notified of the AnimatorSet canceling in cancel().
716 // The logic below only kicks in when animations end normally
717 if (mPlayingSet.size() == 0) {
718 if (mListeners != null) {
719 int numListeners = mListeners.size();
720 for (int i = 0; i < numListeners; ++i) {
721 mListeners.get(i).onAnimationCancel(mAnimatorSet);
722 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700723 }
724 }
725 }
726 }
727
728 @SuppressWarnings("unchecked")
Chet Haasea18a86b2010-09-07 13:20:00 -0700729 public void onAnimationEnd(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700730 animation.removeListener(this);
731 mPlayingSet.remove(animation);
Chet Haasea18a86b2010-09-07 13:20:00 -0700732 Node animNode = mAnimatorSet.mNodeMap.get(animation);
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700733 animNode.done = true;
Chet Haase7dfacdb2011-07-11 17:01:56 -0700734 if (!mTerminated) {
735 // Listeners are already notified of the AnimatorSet ending in cancel() or
736 // end(); the logic below only kicks in when animations end normally
737 ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes;
738 boolean allDone = true;
739 int numSortedNodes = sortedNodes.size();
740 for (int i = 0; i < numSortedNodes; ++i) {
741 if (!sortedNodes.get(i).done) {
742 allDone = false;
743 break;
744 }
Chet Haasea6e4a1752010-07-23 11:08:47 -0700745 }
Chet Haase7dfacdb2011-07-11 17:01:56 -0700746 if (allDone) {
747 // If this was the last child animation to end, then notify listeners that this
748 // AnimatorSet has ended
749 if (mListeners != null) {
750 ArrayList<AnimatorListener> tmpListeners =
751 (ArrayList<AnimatorListener>) mListeners.clone();
752 int numListeners = tmpListeners.size();
753 for (int i = 0; i < numListeners; ++i) {
754 tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
755 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700756 }
Chet Haase8b699792011-08-05 15:20:19 -0700757 mAnimatorSet.mStarted = false;
Chet Haase17fb4b02010-06-28 17:55:07 -0700758 }
759 }
760 }
761
762 // Nothing to do
Chet Haasea18a86b2010-09-07 13:20:00 -0700763 public void onAnimationRepeat(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700764 }
765
766 // Nothing to do
Chet Haasea18a86b2010-09-07 13:20:00 -0700767 public void onAnimationStart(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700768 }
769
770 }
771
772 /**
773 * This method sorts the current set of nodes, if needed. The sort is a simple
774 * DependencyGraph sort, which goes like this:
775 * - All nodes without dependencies become 'roots'
776 * - while roots list is not null
777 * - for each root r
778 * - add r to sorted list
779 * - remove r as a dependency from any other node
780 * - any nodes with no dependencies are added to the roots list
781 */
782 private void sortNodes() {
783 if (mNeedsSort) {
784 mSortedNodes.clear();
785 ArrayList<Node> roots = new ArrayList<Node>();
Chet Haase7c608f22010-10-22 17:54:04 -0700786 int numNodes = mNodes.size();
787 for (int i = 0; i < numNodes; ++i) {
788 Node node = mNodes.get(i);
Chet Haase17fb4b02010-06-28 17:55:07 -0700789 if (node.dependencies == null || node.dependencies.size() == 0) {
790 roots.add(node);
791 }
792 }
793 ArrayList<Node> tmpRoots = new ArrayList<Node>();
794 while (roots.size() > 0) {
Chet Haase7c608f22010-10-22 17:54:04 -0700795 int numRoots = roots.size();
796 for (int i = 0; i < numRoots; ++i) {
797 Node root = roots.get(i);
Chet Haase17fb4b02010-06-28 17:55:07 -0700798 mSortedNodes.add(root);
799 if (root.nodeDependents != null) {
Chet Haase7c608f22010-10-22 17:54:04 -0700800 int numDependents = root.nodeDependents.size();
801 for (int j = 0; j < numDependents; ++j) {
802 Node node = root.nodeDependents.get(j);
Chet Haase17fb4b02010-06-28 17:55:07 -0700803 node.nodeDependencies.remove(root);
804 if (node.nodeDependencies.size() == 0) {
805 tmpRoots.add(node);
806 }
807 }
808 }
809 }
Chet Haase010dbaa2010-07-19 17:29:49 -0700810 roots.clear();
Chet Haase17fb4b02010-06-28 17:55:07 -0700811 roots.addAll(tmpRoots);
812 tmpRoots.clear();
813 }
814 mNeedsSort = false;
815 if (mSortedNodes.size() != mNodes.size()) {
816 throw new IllegalStateException("Circular dependencies cannot exist"
Chet Haasea18a86b2010-09-07 13:20:00 -0700817 + " in AnimatorSet");
Chet Haase17fb4b02010-06-28 17:55:07 -0700818 }
819 } else {
820 // Doesn't need sorting, but still need to add in the nodeDependencies list
821 // because these get removed as the event listeners fire and the dependencies
822 // are satisfied
Chet Haase7c608f22010-10-22 17:54:04 -0700823 int numNodes = mNodes.size();
824 for (int i = 0; i < numNodes; ++i) {
825 Node node = mNodes.get(i);
Chet Haase17fb4b02010-06-28 17:55:07 -0700826 if (node.dependencies != null && node.dependencies.size() > 0) {
Chet Haase7c608f22010-10-22 17:54:04 -0700827 int numDependencies = node.dependencies.size();
828 for (int j = 0; j < numDependencies; ++j) {
829 Dependency dependency = node.dependencies.get(j);
Chet Haase17fb4b02010-06-28 17:55:07 -0700830 if (node.nodeDependencies == null) {
831 node.nodeDependencies = new ArrayList<Node>();
832 }
833 if (!node.nodeDependencies.contains(dependency.node)) {
834 node.nodeDependencies.add(dependency.node);
835 }
836 }
837 }
Chet Haase7dfacdb2011-07-11 17:01:56 -0700838 // nodes are 'done' by default; they become un-done when started, and done
839 // again when ended
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700840 node.done = false;
Chet Haase17fb4b02010-06-28 17:55:07 -0700841 }
842 }
843 }
844
845 /**
846 * Dependency holds information about the node that some other node is
847 * dependent upon and the nature of that dependency.
848 *
849 */
850 private static class Dependency {
851 static final int WITH = 0; // dependent node must start with this dependency node
852 static final int AFTER = 1; // dependent node must start when this dependency node finishes
853
854 // The node that the other node with this Dependency is dependent upon
855 public Node node;
856
857 // The nature of the dependency (WITH or AFTER)
858 public int rule;
859
860 public Dependency(Node node, int rule) {
861 this.node = node;
862 this.rule = rule;
863 }
864 }
865
866 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700867 * A Node is an embodiment of both the Animator that it wraps as well as
Chet Haase17fb4b02010-06-28 17:55:07 -0700868 * any dependencies that are associated with that Animation. This includes
869 * both dependencies upon other nodes (in the dependencies list) as
870 * well as dependencies of other nodes upon this (in the nodeDependents list).
871 */
Chet Haase49afa5b2010-08-23 11:39:53 -0700872 private static class Node implements Cloneable {
Chet Haasea18a86b2010-09-07 13:20:00 -0700873 public Animator animation;
Chet Haase17fb4b02010-06-28 17:55:07 -0700874
875 /**
876 * These are the dependencies that this node's animation has on other
877 * nodes. For example, if this node's animation should begin with some
878 * other animation ends, then there will be an item in this node's
879 * dependencies list for that other animation's node.
880 */
881 public ArrayList<Dependency> dependencies = null;
882
883 /**
884 * tmpDependencies is a runtime detail. We use the dependencies list for sorting.
885 * But we also use the list to keep track of when multiple dependencies are satisfied,
886 * but removing each dependency as it is satisfied. We do not want to remove
887 * the dependency itself from the list, because we need to retain that information
Chet Haasea18a86b2010-09-07 13:20:00 -0700888 * if the AnimatorSet is launched in the future. So we create a copy of the dependency
889 * list when the AnimatorSet starts and use this tmpDependencies list to track the
Chet Haase17fb4b02010-06-28 17:55:07 -0700890 * list of satisfied dependencies.
891 */
892 public ArrayList<Dependency> tmpDependencies = null;
893
894 /**
895 * nodeDependencies is just a list of the nodes that this Node is dependent upon.
896 * This information is used in sortNodes(), to determine when a node is a root.
897 */
898 public ArrayList<Node> nodeDependencies = null;
899
900 /**
901 * nodeDepdendents is the list of nodes that have this node as a dependency. This
902 * is a utility field used in sortNodes to facilitate removing this node as a
903 * dependency when it is a root node.
904 */
905 public ArrayList<Node> nodeDependents = null;
906
907 /**
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700908 * Flag indicating whether the animation in this node is finished. This flag
Chet Haasea18a86b2010-09-07 13:20:00 -0700909 * is used by AnimatorSet to check, as each animation ends, whether all child animations
910 * are done and it's time to send out an end event for the entire AnimatorSet.
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700911 */
912 public boolean done = false;
913
914 /**
Chet Haase17fb4b02010-06-28 17:55:07 -0700915 * Constructs the Node with the animation that it encapsulates. A Node has no
916 * dependencies by default; dependencies are added via the addDependency()
917 * method.
918 *
919 * @param animation The animation that the Node encapsulates.
920 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700921 public Node(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700922 this.animation = animation;
923 }
924
925 /**
926 * Add a dependency to this Node. The dependency includes information about the
927 * node that this node is dependency upon and the nature of the dependency.
928 * @param dependency
929 */
930 public void addDependency(Dependency dependency) {
931 if (dependencies == null) {
932 dependencies = new ArrayList<Dependency>();
933 nodeDependencies = new ArrayList<Node>();
934 }
935 dependencies.add(dependency);
936 if (!nodeDependencies.contains(dependency.node)) {
937 nodeDependencies.add(dependency.node);
938 }
939 Node dependencyNode = dependency.node;
940 if (dependencyNode.nodeDependents == null) {
941 dependencyNode.nodeDependents = new ArrayList<Node>();
942 }
943 dependencyNode.nodeDependents.add(this);
944 }
Chet Haase49afa5b2010-08-23 11:39:53 -0700945
946 @Override
Chet Haase21cd1382010-09-01 17:42:29 -0700947 public Node clone() {
948 try {
949 Node node = (Node) super.clone();
Chet Haasea18a86b2010-09-07 13:20:00 -0700950 node.animation = (Animator) animation.clone();
Chet Haase21cd1382010-09-01 17:42:29 -0700951 return node;
952 } catch (CloneNotSupportedException e) {
953 throw new AssertionError();
954 }
Chet Haase49afa5b2010-08-23 11:39:53 -0700955 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700956 }
957
958 /**
959 * The <code>Builder</code> object is a utility class to facilitate adding animations to a
Chet Haasea18a86b2010-09-07 13:20:00 -0700960 * <code>AnimatorSet</code> along with the relationships between the various animations. The
Chet Haase17fb4b02010-06-28 17:55:07 -0700961 * intention of the <code>Builder</code> methods, along with the {@link
Chet Haase8b699792011-08-05 15:20:19 -0700962 * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible
963 * to express the dependency relationships of animations in a natural way. Developers can also
964 * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
Chet Haasea18a86b2010-09-07 13:20:00 -0700965 * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
966 * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
Chet Haase17fb4b02010-06-28 17:55:07 -0700967 * <p/>
968 * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
Chet Haasea18a86b2010-09-07 13:20:00 -0700969 * internally via a call to {@link AnimatorSet#play(Animator)}.</p>
Chet Haase17fb4b02010-06-28 17:55:07 -0700970 * <p/>
Chet Haasea18a86b2010-09-07 13:20:00 -0700971 * <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 -0700972 * play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
973 * <pre>
Chet Haasea18a86b2010-09-07 13:20:00 -0700974 * AnimatorSet s = new AnimatorSet();
Chet Haase17fb4b02010-06-28 17:55:07 -0700975 * s.play(anim1).with(anim2);
976 * s.play(anim2).before(anim3);
977 * s.play(anim4).after(anim3);
978 * </pre>
979 * <p/>
Chet Haasea18a86b2010-09-07 13:20:00 -0700980 * <p>Note in the example that both {@link Builder#before(Animator)} and {@link
981 * Builder#after(Animator)} are used. These are just different ways of expressing the same
Chet Haase17fb4b02010-06-28 17:55:07 -0700982 * relationship and are provided to make it easier to say things in a way that is more natural,
983 * depending on the situation.</p>
984 * <p/>
985 * <p>It is possible to make several calls into the same <code>Builder</code> object to express
986 * multiple relationships. However, note that it is only the animation passed into the initial
Chet Haasea18a86b2010-09-07 13:20:00 -0700987 * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
Chet Haase17fb4b02010-06-28 17:55:07 -0700988 * calls to the <code>Builder</code> object. For example, the following code starts both anim2
989 * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
990 * anim3:
991 * <pre>
Chet Haasea18a86b2010-09-07 13:20:00 -0700992 * AnimatorSet s = new AnimatorSet();
Chet Haase17fb4b02010-06-28 17:55:07 -0700993 * s.play(anim1).before(anim2).before(anim3);
994 * </pre>
995 * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
996 * relationship correctly:</p>
997 * <pre>
Chet Haasea18a86b2010-09-07 13:20:00 -0700998 * AnimatorSet s = new AnimatorSet();
Chet Haase17fb4b02010-06-28 17:55:07 -0700999 * s.play(anim1).before(anim2);
1000 * s.play(anim2).before(anim3);
1001 * </pre>
1002 * <p/>
1003 * <p>Note that it is possible to express relationships that cannot be resolved and will not
1004 * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
1005 * sense. In general, circular dependencies like this one (or more indirect ones where a depends
Chet Haasea18a86b2010-09-07 13:20:00 -07001006 * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
1007 * that can boil down to a simple, one-way relationship of animations starting with, before, and
Chet Haase17fb4b02010-06-28 17:55:07 -07001008 * after other, different, animations.</p>
1009 */
1010 public class Builder {
1011
1012 /**
1013 * This tracks the current node being processed. It is supplied to the play() method
Chet Haasea18a86b2010-09-07 13:20:00 -07001014 * of AnimatorSet and passed into the constructor of Builder.
Chet Haase17fb4b02010-06-28 17:55:07 -07001015 */
1016 private Node mCurrentNode;
1017
1018 /**
Chet Haasea18a86b2010-09-07 13:20:00 -07001019 * package-private constructor. Builders are only constructed by AnimatorSet, when the
Chet Haase17fb4b02010-06-28 17:55:07 -07001020 * play() method is called.
1021 *
1022 * @param anim The animation that is the dependency for the other animations passed into
1023 * the other methods of this Builder object.
1024 */
Chet Haasea18a86b2010-09-07 13:20:00 -07001025 Builder(Animator anim) {
Chet Haase17fb4b02010-06-28 17:55:07 -07001026 mCurrentNode = mNodeMap.get(anim);
1027 if (mCurrentNode == null) {
1028 mCurrentNode = new Node(anim);
1029 mNodeMap.put(anim, mCurrentNode);
1030 mNodes.add(mCurrentNode);
1031 }
1032 }
1033
1034 /**
1035 * Sets up the given animation to play at the same time as the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001036 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
Chet Haase17fb4b02010-06-28 17:55:07 -07001037 *
1038 * @param anim The animation that will play when the animation supplied to the
Chet Haasea18a86b2010-09-07 13:20:00 -07001039 * {@link AnimatorSet#play(Animator)} method starts.
Chet Haase17fb4b02010-06-28 17:55:07 -07001040 */
Chet Haase2970c492010-11-09 13:58:04 -08001041 public Builder with(Animator anim) {
Chet Haase17fb4b02010-06-28 17:55:07 -07001042 Node node = mNodeMap.get(anim);
1043 if (node == null) {
1044 node = new Node(anim);
1045 mNodeMap.put(anim, node);
1046 mNodes.add(node);
1047 }
1048 Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH);
1049 node.addDependency(dependency);
Chet Haase2970c492010-11-09 13:58:04 -08001050 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001051 }
1052
1053 /**
1054 * Sets up the given animation to play when the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001055 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
Chet Haase17fb4b02010-06-28 17:55:07 -07001056 * ends.
1057 *
1058 * @param anim The animation that will play when the animation supplied to the
Chet Haasea18a86b2010-09-07 13:20:00 -07001059 * {@link AnimatorSet#play(Animator)} method ends.
Chet Haase17fb4b02010-06-28 17:55:07 -07001060 */
Chet Haase2970c492010-11-09 13:58:04 -08001061 public Builder before(Animator anim) {
Chet Haase17fb4b02010-06-28 17:55:07 -07001062 Node node = mNodeMap.get(anim);
1063 if (node == null) {
1064 node = new Node(anim);
1065 mNodeMap.put(anim, node);
1066 mNodes.add(node);
1067 }
1068 Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER);
1069 node.addDependency(dependency);
Chet Haase2970c492010-11-09 13:58:04 -08001070 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001071 }
1072
1073 /**
1074 * Sets up the given animation to play when the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001075 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
Chet Haase17fb4b02010-06-28 17:55:07 -07001076 * to start when the animation supplied in this method call ends.
1077 *
1078 * @param anim The animation whose end will cause the animation supplied to the
Chet Haasea18a86b2010-09-07 13:20:00 -07001079 * {@link AnimatorSet#play(Animator)} method to play.
Chet Haase17fb4b02010-06-28 17:55:07 -07001080 */
Chet Haase2970c492010-11-09 13:58:04 -08001081 public Builder after(Animator anim) {
Chet Haase17fb4b02010-06-28 17:55:07 -07001082 Node node = mNodeMap.get(anim);
1083 if (node == null) {
1084 node = new Node(anim);
1085 mNodeMap.put(anim, node);
1086 mNodes.add(node);
1087 }
1088 Dependency dependency = new Dependency(node, Dependency.AFTER);
1089 mCurrentNode.addDependency(dependency);
Chet Haase2970c492010-11-09 13:58:04 -08001090 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001091 }
1092
1093 /**
1094 * Sets up the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001095 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
Chet Haase17fb4b02010-06-28 17:55:07 -07001096 * to play when the given amount of time elapses.
1097 *
1098 * @param delay The number of milliseconds that should elapse before the
1099 * animation starts.
1100 */
Chet Haase2970c492010-11-09 13:58:04 -08001101 public Builder after(long delay) {
Chet Haasea18a86b2010-09-07 13:20:00 -07001102 // setup dummy ValueAnimator just to run the clock
Chet Haase2794eb32010-10-12 16:29:28 -07001103 ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
1104 anim.setDuration(delay);
1105 after(anim);
Chet Haase2970c492010-11-09 13:58:04 -08001106 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001107 }
1108
1109 }
1110
1111}