blob: f6ad847c5f33422625bb8b55311f9512417ebe0c [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
Chet Haase430742f2013-04-12 11:18:36 -0700123 // Records the interpolator for the set. Null value indicates that no interpolator
124 // was set on this AnimatorSet, so it should not be passed down to the children.
125 private TimeInterpolator mInterpolator = null;
126
ztenghui7bc6a3f2014-07-15 15:12:12 -0700127 private boolean mReversible = true;
Chet Haase010dbaa2010-07-19 17:29:49 -0700128 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700129 * Sets up this AnimatorSet to play all of the supplied animations at the same time.
Chet Haase430742f2013-04-12 11:18:36 -0700130 * This is equivalent to calling {@link #play(Animator)} with the first animator in the
131 * set and then {@link Builder#with(Animator)} with each of the other animators. Note that
132 * an Animator with a {@link Animator#setStartDelay(long) startDelay} will not actually
133 * start until that delay elapses, which means that if the first animator in the list
134 * supplied to this constructor has a startDelay, none of the other animators will start
135 * until that first animator's startDelay has elapsed.
Chet Haase17fb4b02010-06-28 17:55:07 -0700136 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700137 * @param items The animations that will be started simultaneously.
Chet Haase17fb4b02010-06-28 17:55:07 -0700138 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700139 public void playTogether(Animator... items) {
140 if (items != null) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700141 mNeedsSort = true;
Chet Haasea18a86b2010-09-07 13:20:00 -0700142 Builder builder = play(items[0]);
143 for (int i = 1; i < items.length; ++i) {
144 builder.with(items[i]);
Chet Haase17fb4b02010-06-28 17:55:07 -0700145 }
146 }
147 }
148
149 /**
Chet Haase37a7bec2010-11-30 15:55:39 -0800150 * Sets up this AnimatorSet to play all of the supplied animations at the same time.
151 *
152 * @param items The animations that will be started simultaneously.
153 */
154 public void playTogether(Collection<Animator> items) {
155 if (items != null && items.size() > 0) {
156 mNeedsSort = true;
157 Builder builder = null;
158 for (Animator anim : items) {
159 if (builder == null) {
160 builder = play(anim);
161 } else {
162 builder.with(anim);
163 }
164 }
165 }
166 }
167
168 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700169 * Sets up this AnimatorSet to play each of the supplied animations when the
Chet Haase17fb4b02010-06-28 17:55:07 -0700170 * previous animation ends.
171 *
Chet Haase37a7bec2010-11-30 15:55:39 -0800172 * @param items The animations that will be started one after another.
Chet Haase17fb4b02010-06-28 17:55:07 -0700173 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700174 public void playSequentially(Animator... items) {
175 if (items != null) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700176 mNeedsSort = true;
Chet Haasea18a86b2010-09-07 13:20:00 -0700177 if (items.length == 1) {
178 play(items[0]);
Chet Haase17fb4b02010-06-28 17:55:07 -0700179 } else {
ztenghui7bc6a3f2014-07-15 15:12:12 -0700180 mReversible = false;
Chet Haasea18a86b2010-09-07 13:20:00 -0700181 for (int i = 0; i < items.length - 1; ++i) {
182 play(items[i]).before(items[i+1]);
Chet Haase17fb4b02010-06-28 17:55:07 -0700183 }
184 }
185 }
186 }
187
188 /**
Chet Haase37a7bec2010-11-30 15:55:39 -0800189 * Sets up this AnimatorSet to play each of the supplied animations when the
190 * previous animation ends.
191 *
192 * @param items The animations that will be started one after another.
193 */
194 public void playSequentially(List<Animator> items) {
195 if (items != null && items.size() > 0) {
196 mNeedsSort = true;
197 if (items.size() == 1) {
198 play(items.get(0));
199 } else {
ztenghui7bc6a3f2014-07-15 15:12:12 -0700200 mReversible = false;
Chet Haase37a7bec2010-11-30 15:55:39 -0800201 for (int i = 0; i < items.size() - 1; ++i) {
202 play(items.get(i)).before(items.get(i+1));
203 }
204 }
205 }
206 }
207
208 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700209 * Returns the current list of child Animator objects controlled by this
210 * AnimatorSet. This is a copy of the internal list; modifications to the returned list
211 * will not affect the AnimatorSet, although changes to the underlying Animator objects
212 * will affect those objects being managed by the AnimatorSet.
Chet Haasef54a8d72010-07-22 14:44:59 -0700213 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700214 * @return ArrayList<Animator> The list of child animations of this AnimatorSet.
Chet Haasef54a8d72010-07-22 14:44:59 -0700215 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700216 public ArrayList<Animator> getChildAnimations() {
217 ArrayList<Animator> childList = new ArrayList<Animator>();
Chet Haasef54a8d72010-07-22 14:44:59 -0700218 for (Node node : mNodes) {
219 childList.add(node.animation);
220 }
221 return childList;
222 }
223
224 /**
Chet Haase811ed1062010-08-06 10:38:15 -0700225 * Sets the target object for all current {@link #getChildAnimations() child animations}
Chet Haasea18a86b2010-09-07 13:20:00 -0700226 * of this AnimatorSet that take targets ({@link ObjectAnimator} and
227 * AnimatorSet).
Chet Haase811ed1062010-08-06 10:38:15 -0700228 *
229 * @param target The object being animated
230 */
Chet Haase21cd1382010-09-01 17:42:29 -0700231 @Override
Chet Haase811ed1062010-08-06 10:38:15 -0700232 public void setTarget(Object target) {
233 for (Node node : mNodes) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700234 Animator animation = node.animation;
235 if (animation instanceof AnimatorSet) {
236 ((AnimatorSet)animation).setTarget(target);
237 } else if (animation instanceof ObjectAnimator) {
238 ((ObjectAnimator)animation).setTarget(target);
Chet Haase811ed1062010-08-06 10:38:15 -0700239 }
240 }
241 }
242
243 /**
Yigit Boyard422dc32014-09-25 12:23:35 -0700244 * @hide
245 */
246 @Override
247 public int getChangingConfigurations() {
248 int conf = super.getChangingConfigurations();
249 final int nodeCount = mNodes.size();
250 for (int i = 0; i < nodeCount; i ++) {
251 conf |= mNodes.get(i).animation.getChangingConfigurations();
252 }
253 return conf;
254 }
255
256 /**
Chet Haasee0ee2e92010-10-07 09:06:18 -0700257 * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations}
Chet Haase430742f2013-04-12 11:18:36 -0700258 * of this AnimatorSet. The default value is null, which means that no interpolator
259 * is set on this AnimatorSet. Setting the interpolator to any non-null value
260 * will cause that interpolator to be set on the child animations
261 * when the set is started.
Chet Haase21cd1382010-09-01 17:42:29 -0700262 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700263 * @param interpolator the interpolator to be used by each child animation of this AnimatorSet
Chet Haase21cd1382010-09-01 17:42:29 -0700264 */
265 @Override
Chet Haasee0ee2e92010-10-07 09:06:18 -0700266 public void setInterpolator(TimeInterpolator interpolator) {
Chet Haase430742f2013-04-12 11:18:36 -0700267 mInterpolator = interpolator;
268 }
269
270 @Override
271 public TimeInterpolator getInterpolator() {
272 return mInterpolator;
Chet Haase21cd1382010-09-01 17:42:29 -0700273 }
274
275 /**
Chet Haase17fb4b02010-06-28 17:55:07 -0700276 * This method creates a <code>Builder</code> object, which is used to
277 * set up playing constraints. This initial <code>play()</code> method
278 * tells the <code>Builder</code> the animation that is the dependency for
279 * the succeeding commands to the <code>Builder</code>. For example,
Chet Haasea18a86b2010-09-07 13:20:00 -0700280 * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play
Chet Haase17fb4b02010-06-28 17:55:07 -0700281 * <code>a1</code> and <code>a2</code> at the same time,
Chet Haasea18a86b2010-09-07 13:20:00 -0700282 * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play
Chet Haase17fb4b02010-06-28 17:55:07 -0700283 * <code>a1</code> first, followed by <code>a2</code>, and
Chet Haasea18a86b2010-09-07 13:20:00 -0700284 * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play
Chet Haase17fb4b02010-06-28 17:55:07 -0700285 * <code>a2</code> first, followed by <code>a1</code>.
286 *
287 * <p>Note that <code>play()</code> is the only way to tell the
288 * <code>Builder</code> the animation upon which the dependency is created,
289 * so successive calls to the various functions in <code>Builder</code>
290 * will all refer to the initial parameter supplied in <code>play()</code>
291 * as the dependency of the other animations. For example, calling
292 * <code>play(a1).before(a2).before(a3)</code> will play both <code>a2</code>
293 * and <code>a3</code> when a1 ends; it does not set up a dependency between
294 * <code>a2</code> and <code>a3</code>.</p>
295 *
296 * @param anim The animation that is the dependency used in later calls to the
297 * methods in the returned <code>Builder</code> object. A null parameter will result
298 * in a null <code>Builder</code> return value.
Chet Haasea18a86b2010-09-07 13:20:00 -0700299 * @return Builder The object that constructs the AnimatorSet based on the dependencies
Chet Haase17fb4b02010-06-28 17:55:07 -0700300 * outlined in the calls to <code>play</code> and the other methods in the
301 * <code>Builder</code object.
302 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700303 public Builder play(Animator anim) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700304 if (anim != null) {
305 mNeedsSort = true;
306 return new Builder(anim);
307 }
308 return null;
309 }
310
311 /**
312 * {@inheritDoc}
313 *
Chet Haase8b699792011-08-05 15:20:19 -0700314 * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it
315 * is responsible for.</p>
Chet Haase17fb4b02010-06-28 17:55:07 -0700316 */
317 @SuppressWarnings("unchecked")
318 @Override
319 public void cancel() {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700320 mTerminated = true;
Chet Haase8b699792011-08-05 15:20:19 -0700321 if (isStarted()) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700322 ArrayList<AnimatorListener> tmpListeners = null;
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800323 if (mListeners != null) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700324 tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
325 for (AnimatorListener listener : tmpListeners) {
326 listener.onAnimationCancel(this);
327 }
328 }
329 if (mDelayAnim != null && mDelayAnim.isRunning()) {
330 // If we're currently in the startDelay period, just cancel that animator and
331 // send out the end event to all listeners
332 mDelayAnim.cancel();
333 } else if (mSortedNodes.size() > 0) {
334 for (Node node : mSortedNodes) {
335 node.animation.cancel();
336 }
337 }
338 if (tmpListeners != null) {
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800339 for (AnimatorListener listener : tmpListeners) {
340 listener.onAnimationEnd(this);
341 }
342 }
Chet Haase8b699792011-08-05 15:20:19 -0700343 mStarted = false;
Chet Haase17fb4b02010-06-28 17:55:07 -0700344 }
345 }
346
347 /**
348 * {@inheritDoc}
349 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700350 * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is
Chet Haase17fb4b02010-06-28 17:55:07 -0700351 * responsible for.</p>
352 */
353 @Override
354 public void end() {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700355 mTerminated = true;
Chet Haase8b699792011-08-05 15:20:19 -0700356 if (isStarted()) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700357 if (mSortedNodes.size() != mNodes.size()) {
358 // hasn't been started yet - sort the nodes now, then end them
359 sortNodes();
360 for (Node node : mSortedNodes) {
361 if (mSetListener == null) {
362 mSetListener = new AnimatorSetListener(this);
363 }
364 node.animation.addListener(mSetListener);
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700365 }
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700366 }
Chet Haase7dfacdb2011-07-11 17:01:56 -0700367 if (mDelayAnim != null) {
368 mDelayAnim.cancel();
369 }
370 if (mSortedNodes.size() > 0) {
371 for (Node node : mSortedNodes) {
372 node.animation.end();
373 }
374 }
375 if (mListeners != null) {
376 ArrayList<AnimatorListener> tmpListeners =
377 (ArrayList<AnimatorListener>) mListeners.clone();
378 for (AnimatorListener listener : tmpListeners) {
379 listener.onAnimationEnd(this);
380 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700381 }
Chet Haase8b699792011-08-05 15:20:19 -0700382 mStarted = false;
Chet Haase17fb4b02010-06-28 17:55:07 -0700383 }
384 }
385
386 /**
Chet Haase8b699792011-08-05 15:20:19 -0700387 * Returns true if any of the child animations of this AnimatorSet have been started and have
388 * not yet ended.
Chet Haasea18a86b2010-09-07 13:20:00 -0700389 * @return Whether this AnimatorSet has been started and has not yet ended.
Chet Haase673e42f2010-08-25 16:32:37 -0700390 */
391 @Override
392 public boolean isRunning() {
393 for (Node node : mNodes) {
394 if (node.animation.isRunning()) {
395 return true;
396 }
397 }
Chet Haase8b699792011-08-05 15:20:19 -0700398 return false;
399 }
400
401 @Override
402 public boolean isStarted() {
403 return mStarted;
Chet Haase673e42f2010-08-25 16:32:37 -0700404 }
405
406 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700407 * The amount of time, in milliseconds, to delay starting the animation after
408 * {@link #start()} is called.
409 *
410 * @return the number of milliseconds to delay running the animation
411 */
412 @Override
413 public long getStartDelay() {
414 return mStartDelay;
415 }
416
417 /**
418 * The amount of time, in milliseconds, to delay starting the animation after
419 * {@link #start()} is called.
420
421 * @param startDelay The amount of the delay, in milliseconds
422 */
423 @Override
424 public void setStartDelay(long startDelay) {
ztenghui7bc6a3f2014-07-15 15:12:12 -0700425 if (mStartDelay > 0) {
426 mReversible = false;
427 }
Chet Haase21cd1382010-09-01 17:42:29 -0700428 mStartDelay = startDelay;
429 }
430
431 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700432 * Gets the length of each of the child animations of this AnimatorSet. This value may
433 * be less than 0, which indicates that no duration has been set on this AnimatorSet
Chet Haase21cd1382010-09-01 17:42:29 -0700434 * and each of the child animations will use their own duration.
435 *
436 * @return The length of the animation, in milliseconds, of each of the child
Chet Haasea18a86b2010-09-07 13:20:00 -0700437 * animations of this AnimatorSet.
Chet Haase21cd1382010-09-01 17:42:29 -0700438 */
439 @Override
440 public long getDuration() {
441 return mDuration;
442 }
443
444 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700445 * Sets the length of each of the current child animations of this AnimatorSet. By default,
446 * each child animation will use its own duration. If the duration is set on the AnimatorSet,
Chet Haase21cd1382010-09-01 17:42:29 -0700447 * then each child animation inherits this duration.
448 *
449 * @param duration The length of the animation, in milliseconds, of each of the child
Chet Haasea18a86b2010-09-07 13:20:00 -0700450 * animations of this AnimatorSet.
Chet Haase21cd1382010-09-01 17:42:29 -0700451 */
452 @Override
Chet Haase2794eb32010-10-12 16:29:28 -0700453 public AnimatorSet setDuration(long duration) {
Chet Haase21cd1382010-09-01 17:42:29 -0700454 if (duration < 0) {
455 throw new IllegalArgumentException("duration must be a value of zero or greater");
456 }
Chet Haasec299a332012-04-12 07:51:50 -0700457 // Just record the value for now - it will be used later when the AnimatorSet starts
Chet Haase21cd1382010-09-01 17:42:29 -0700458 mDuration = duration;
Chet Haase2794eb32010-10-12 16:29:28 -0700459 return this;
Chet Haase21cd1382010-09-01 17:42:29 -0700460 }
461
Chet Haase2970c492010-11-09 13:58:04 -0800462 @Override
463 public void setupStartValues() {
464 for (Node node : mNodes) {
465 node.animation.setupStartValues();
466 }
467 }
468
469 @Override
470 public void setupEndValues() {
471 for (Node node : mNodes) {
472 node.animation.setupEndValues();
473 }
474 }
475
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700476 @Override
477 public void pause() {
478 boolean previouslyPaused = mPaused;
479 super.pause();
480 if (!previouslyPaused && mPaused) {
481 if (mDelayAnim != null) {
482 mDelayAnim.pause();
483 } else {
484 for (Node node : mNodes) {
485 node.animation.pause();
486 }
487 }
488 }
489 }
490
491 @Override
492 public void resume() {
493 boolean previouslyPaused = mPaused;
494 super.resume();
495 if (previouslyPaused && !mPaused) {
496 if (mDelayAnim != null) {
497 mDelayAnim.resume();
498 } else {
499 for (Node node : mNodes) {
500 node.animation.resume();
501 }
502 }
503 }
504 }
505
Chet Haase21cd1382010-09-01 17:42:29 -0700506 /**
Chet Haase17fb4b02010-06-28 17:55:07 -0700507 * {@inheritDoc}
508 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700509 * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which
Chet Haase17fb4b02010-06-28 17:55:07 -0700510 * it is responsible. The details of when exactly those animations are started depends on
511 * the dependency relationships that have been set up between the animations.
512 */
513 @SuppressWarnings("unchecked")
514 @Override
515 public void start() {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700516 mTerminated = false;
Chet Haase8b699792011-08-05 15:20:19 -0700517 mStarted = true;
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700518 mPaused = false;
Chet Haase010dbaa2010-07-19 17:29:49 -0700519
John Reckf5945a02014-09-05 15:57:47 -0700520 for (Node node : mNodes) {
521 node.animation.setAllowRunningAsynchronously(false);
Doris Liu0084e372015-04-10 12:39:35 -0700522 copyDurationScaleInfoTo(node.animation);
John Reckf5945a02014-09-05 15:57:47 -0700523 }
524
Chet Haasec299a332012-04-12 07:51:50 -0700525 if (mDuration >= 0) {
526 // If the duration was set on this AnimatorSet, pass it along to all child animations
527 for (Node node : mNodes) {
528 // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
529 // insert "play-after" delays
530 node.animation.setDuration(mDuration);
531 }
532 }
Chet Haase430742f2013-04-12 11:18:36 -0700533 if (mInterpolator != null) {
534 for (Node node : mNodes) {
535 node.animation.setInterpolator(mInterpolator);
536 }
537 }
ztenghui7bc6a3f2014-07-15 15:12:12 -0700538 // First, sort the nodes (if necessary). This will ensure that sortedNodes
Chet Haase17fb4b02010-06-28 17:55:07 -0700539 // contains the animation nodes in the correct order.
540 sortNodes();
541
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800542 int numSortedNodes = mSortedNodes.size();
543 for (int i = 0; i < numSortedNodes; ++i) {
544 Node node = mSortedNodes.get(i);
545 // First, clear out the old listeners
546 ArrayList<AnimatorListener> oldListeners = node.animation.getListeners();
547 if (oldListeners != null && oldListeners.size() > 0) {
Martijn Coenend45204b2011-07-29 15:16:19 -0500548 final ArrayList<AnimatorListener> clonedListeners = new
549 ArrayList<AnimatorListener>(oldListeners);
550
551 for (AnimatorListener listener : clonedListeners) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700552 if (listener instanceof DependencyListener ||
553 listener instanceof AnimatorSetListener) {
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800554 node.animation.removeListener(listener);
555 }
556 }
557 }
558 }
559
Chet Haase17fb4b02010-06-28 17:55:07 -0700560 // nodesToStart holds the list of nodes to be started immediately. We don't want to
561 // start the animations in the loop directly because we first need to set up
562 // dependencies on all of the nodes. For example, we don't want to start an animation
563 // when some other animation also wants to start when the first animation begins.
Chet Haase21cd1382010-09-01 17:42:29 -0700564 final ArrayList<Node> nodesToStart = new ArrayList<Node>();
Chet Haase7c608f22010-10-22 17:54:04 -0700565 for (int i = 0; i < numSortedNodes; ++i) {
566 Node node = mSortedNodes.get(i);
Chet Haasea18a86b2010-09-07 13:20:00 -0700567 if (mSetListener == null) {
568 mSetListener = new AnimatorSetListener(this);
Chet Haase17fb4b02010-06-28 17:55:07 -0700569 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700570 if (node.dependencies == null || node.dependencies.size() == 0) {
571 nodesToStart.add(node);
572 } else {
Chet Haase7c608f22010-10-22 17:54:04 -0700573 int numDependencies = node.dependencies.size();
574 for (int j = 0; j < numDependencies; ++j) {
575 Dependency dependency = node.dependencies.get(j);
Chet Haase17fb4b02010-06-28 17:55:07 -0700576 dependency.node.animation.addListener(
Chet Haase010dbaa2010-07-19 17:29:49 -0700577 new DependencyListener(this, node, dependency.rule));
Chet Haase17fb4b02010-06-28 17:55:07 -0700578 }
579 node.tmpDependencies = (ArrayList<Dependency>) node.dependencies.clone();
580 }
Chet Haasea18a86b2010-09-07 13:20:00 -0700581 node.animation.addListener(mSetListener);
Chet Haase17fb4b02010-06-28 17:55:07 -0700582 }
583 // Now that all dependencies are set up, start the animations that should be started.
Chet Haase21cd1382010-09-01 17:42:29 -0700584 if (mStartDelay <= 0) {
585 for (Node node : nodesToStart) {
586 node.animation.start();
587 mPlayingSet.add(node.animation);
588 }
589 } else {
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800590 mDelayAnim = ValueAnimator.ofFloat(0f, 1f);
591 mDelayAnim.setDuration(mStartDelay);
592 mDelayAnim.addListener(new AnimatorListenerAdapter() {
593 boolean canceled = false;
594 public void onAnimationCancel(Animator anim) {
595 canceled = true;
596 }
Chet Haasea18a86b2010-09-07 13:20:00 -0700597 public void onAnimationEnd(Animator anim) {
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800598 if (!canceled) {
599 int numNodes = nodesToStart.size();
600 for (int i = 0; i < numNodes; ++i) {
601 Node node = nodesToStart.get(i);
602 node.animation.start();
603 mPlayingSet.add(node.animation);
604 }
Chet Haase21cd1382010-09-01 17:42:29 -0700605 }
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700606 mDelayAnim = null;
Chet Haase21cd1382010-09-01 17:42:29 -0700607 }
608 });
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800609 mDelayAnim.start();
Chet Haase17fb4b02010-06-28 17:55:07 -0700610 }
611 if (mListeners != null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700612 ArrayList<AnimatorListener> tmpListeners =
613 (ArrayList<AnimatorListener>) mListeners.clone();
Chet Haase7c608f22010-10-22 17:54:04 -0700614 int numListeners = tmpListeners.size();
615 for (int i = 0; i < numListeners; ++i) {
616 tmpListeners.get(i).onAnimationStart(this);
Chet Haase8b699792011-08-05 15:20:19 -0700617 }
618 }
619 if (mNodes.size() == 0 && mStartDelay == 0) {
620 // Handle unusual case where empty AnimatorSet is started - should send out
621 // end event immediately since the event will not be sent out at all otherwise
622 mStarted = false;
623 if (mListeners != null) {
624 ArrayList<AnimatorListener> tmpListeners =
625 (ArrayList<AnimatorListener>) mListeners.clone();
626 int numListeners = tmpListeners.size();
627 for (int i = 0; i < numListeners; ++i) {
Chet Haase2970c492010-11-09 13:58:04 -0800628 tmpListeners.get(i).onAnimationEnd(this);
629 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700630 }
631 }
632 }
633
Chet Haase49afa5b2010-08-23 11:39:53 -0700634 @Override
Chet Haasea18a86b2010-09-07 13:20:00 -0700635 public AnimatorSet clone() {
636 final AnimatorSet anim = (AnimatorSet) super.clone();
Chet Haase49afa5b2010-08-23 11:39:53 -0700637 /*
638 * The basic clone() operation copies all items. This doesn't work very well for
Chet Haasea18a86b2010-09-07 13:20:00 -0700639 * AnimatorSet, because it will copy references that need to be recreated and state
Chet Haase49afa5b2010-08-23 11:39:53 -0700640 * that may not apply. What we need to do now is put the clone in an uninitialized
641 * state, with fresh, empty data structures. Then we will build up the nodes list
642 * manually, as we clone each Node (and its animation). The clone will then be sorted,
643 * and will populate any appropriate lists, when it is started.
644 */
Yigit Boyard422dc32014-09-25 12:23:35 -0700645 final int nodeCount = mNodes.size();
Chet Haase49afa5b2010-08-23 11:39:53 -0700646 anim.mNeedsSort = true;
Chet Haase7dfacdb2011-07-11 17:01:56 -0700647 anim.mTerminated = false;
Chet Haase8b699792011-08-05 15:20:19 -0700648 anim.mStarted = false;
Chet Haasea18a86b2010-09-07 13:20:00 -0700649 anim.mPlayingSet = new ArrayList<Animator>();
650 anim.mNodeMap = new HashMap<Animator, Node>();
Yigit Boyard422dc32014-09-25 12:23:35 -0700651 anim.mNodes = new ArrayList<Node>(nodeCount);
652 anim.mSortedNodes = new ArrayList<Node>(nodeCount);
ztenghui7bc6a3f2014-07-15 15:12:12 -0700653 anim.mReversible = mReversible;
Chet Haase6f6578e2014-09-05 16:53:25 -0700654 anim.mSetListener = null;
Chet Haase49afa5b2010-08-23 11:39:53 -0700655
656 // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
Chet Haasea18a86b2010-09-07 13:20:00 -0700657 // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
Chet Haase49afa5b2010-08-23 11:39:53 -0700658 // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
Yigit Boyard422dc32014-09-25 12:23:35 -0700659
660 for (int n = 0; n < nodeCount; n++) {
661 final Node node = mNodes.get(n);
Chet Haase49afa5b2010-08-23 11:39:53 -0700662 Node nodeClone = node.clone();
Yigit Boyard422dc32014-09-25 12:23:35 -0700663 node.mTmpClone = nodeClone;
Chet Haase49afa5b2010-08-23 11:39:53 -0700664 anim.mNodes.add(nodeClone);
665 anim.mNodeMap.put(nodeClone.animation, nodeClone);
666 // Clear out the dependencies in the clone; we'll set these up manually later
667 nodeClone.dependencies = null;
668 nodeClone.tmpDependencies = null;
669 nodeClone.nodeDependents = null;
670 nodeClone.nodeDependencies = null;
Yigit Boyard422dc32014-09-25 12:23:35 -0700671
Chet Haasea18a86b2010-09-07 13:20:00 -0700672 // clear out any listeners that were set up by the AnimatorSet; these will
Chet Haase49afa5b2010-08-23 11:39:53 -0700673 // be set up when the clone's nodes are sorted
Yigit Boyard422dc32014-09-25 12:23:35 -0700674 final ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners();
Chet Haase49afa5b2010-08-23 11:39:53 -0700675 if (cloneListeners != null) {
Yigit Boyard422dc32014-09-25 12:23:35 -0700676 for (int i = cloneListeners.size() - 1; i >= 0; i--) {
677 final AnimatorListener listener = cloneListeners.get(i);
Chet Haasea18a86b2010-09-07 13:20:00 -0700678 if (listener instanceof AnimatorSetListener) {
Yigit Boyard422dc32014-09-25 12:23:35 -0700679 cloneListeners.remove(i);
Chet Haase49afa5b2010-08-23 11:39:53 -0700680 }
681 }
682 }
683 }
684 // Now that we've cloned all of the nodes, we're ready to walk through their
685 // dependencies, mapping the old dependencies to the new nodes
Yigit Boyard422dc32014-09-25 12:23:35 -0700686 for (int n = 0; n < nodeCount; n++) {
687 final Node node = mNodes.get(n);
688 final Node clone = node.mTmpClone;
Chet Haase49afa5b2010-08-23 11:39:53 -0700689 if (node.dependencies != null) {
Yigit Boyard422dc32014-09-25 12:23:35 -0700690 clone.dependencies = new ArrayList<Dependency>(node.dependencies.size());
691 final int depSize = node.dependencies.size();
692 for (int i = 0; i < depSize; i ++) {
693 final Dependency dependency = node.dependencies.get(i);
694 Dependency cloneDependency = new Dependency(dependency.node.mTmpClone,
Chet Haase49afa5b2010-08-23 11:39:53 -0700695 dependency.rule);
Yigit Boyard422dc32014-09-25 12:23:35 -0700696 clone.dependencies.add(cloneDependency);
697 }
698 }
699 if (node.nodeDependents != null) {
700 clone.nodeDependents = new ArrayList<Node>(node.nodeDependents.size());
701 for (Node dep : node.nodeDependents) {
702 clone.nodeDependents.add(dep.mTmpClone);
703 }
704 }
705 if (node.nodeDependencies != null) {
706 clone.nodeDependencies = new ArrayList<Node>(node.nodeDependencies.size());
707 for (Node dep : node.nodeDependencies) {
708 clone.nodeDependencies.add(dep.mTmpClone);
Chet Haase49afa5b2010-08-23 11:39:53 -0700709 }
710 }
711 }
Yigit Boyard422dc32014-09-25 12:23:35 -0700712 for (int n = 0; n < nodeCount; n++) {
713 mNodes.get(n).mTmpClone = null;
714 }
Chet Haase49afa5b2010-08-23 11:39:53 -0700715 return anim;
716 }
717
Chet Haase17fb4b02010-06-28 17:55:07 -0700718 /**
719 * This class is the mechanism by which animations are started based on events in other
720 * animations. If an animation has multiple dependencies on other animations, then
721 * all dependencies must be satisfied before the animation is started.
722 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700723 private static class DependencyListener implements AnimatorListener {
Chet Haase17fb4b02010-06-28 17:55:07 -0700724
Chet Haasea18a86b2010-09-07 13:20:00 -0700725 private AnimatorSet mAnimatorSet;
Chet Haase010dbaa2010-07-19 17:29:49 -0700726
Chet Haase17fb4b02010-06-28 17:55:07 -0700727 // The node upon which the dependency is based.
728 private Node mNode;
729
730 // The Dependency rule (WITH or AFTER) that the listener should wait for on
731 // the node
732 private int mRule;
733
Chet Haasea18a86b2010-09-07 13:20:00 -0700734 public DependencyListener(AnimatorSet animatorSet, Node node, int rule) {
735 this.mAnimatorSet = animatorSet;
Chet Haase17fb4b02010-06-28 17:55:07 -0700736 this.mNode = node;
737 this.mRule = rule;
738 }
739
740 /**
Chet Haase010dbaa2010-07-19 17:29:49 -0700741 * Ignore cancel events for now. We may want to handle this eventually,
742 * to prevent follow-on animations from running when some dependency
743 * animation is canceled.
Chet Haase17fb4b02010-06-28 17:55:07 -0700744 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700745 public void onAnimationCancel(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700746 }
747
748 /**
749 * An end event is received - see if this is an event we are listening for
750 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700751 public void onAnimationEnd(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700752 if (mRule == Dependency.AFTER) {
753 startIfReady(animation);
754 }
755 }
756
757 /**
758 * Ignore repeat events for now
759 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700760 public void onAnimationRepeat(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700761 }
762
763 /**
764 * A start event is received - see if this is an event we are listening for
765 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700766 public void onAnimationStart(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700767 if (mRule == Dependency.WITH) {
768 startIfReady(animation);
769 }
770 }
771
772 /**
773 * Check whether the event received is one that the node was waiting for.
774 * If so, mark it as complete and see whether it's time to start
775 * the animation.
776 * @param dependencyAnimation the animation that sent the event.
777 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700778 private void startIfReady(Animator dependencyAnimation) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700779 if (mAnimatorSet.mTerminated) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700780 // if the parent AnimatorSet was canceled, then don't start any dependent anims
Chet Haase010dbaa2010-07-19 17:29:49 -0700781 return;
782 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700783 Dependency dependencyToRemove = null;
Chet Haase7c608f22010-10-22 17:54:04 -0700784 int numDependencies = mNode.tmpDependencies.size();
785 for (int i = 0; i < numDependencies; ++i) {
786 Dependency dependency = mNode.tmpDependencies.get(i);
Chet Haase17fb4b02010-06-28 17:55:07 -0700787 if (dependency.rule == mRule &&
788 dependency.node.animation == dependencyAnimation) {
789 // rule fired - remove the dependency and listener and check to
790 // see whether it's time to start the animation
791 dependencyToRemove = dependency;
792 dependencyAnimation.removeListener(this);
793 break;
794 }
795 }
796 mNode.tmpDependencies.remove(dependencyToRemove);
797 if (mNode.tmpDependencies.size() == 0) {
798 // all dependencies satisfied: start the animation
799 mNode.animation.start();
Chet Haasea18a86b2010-09-07 13:20:00 -0700800 mAnimatorSet.mPlayingSet.add(mNode.animation);
Chet Haase17fb4b02010-06-28 17:55:07 -0700801 }
802 }
803
804 }
805
Chet Haasea18a86b2010-09-07 13:20:00 -0700806 private class AnimatorSetListener implements AnimatorListener {
Chet Haase17fb4b02010-06-28 17:55:07 -0700807
Chet Haasea18a86b2010-09-07 13:20:00 -0700808 private AnimatorSet mAnimatorSet;
Chet Haase17fb4b02010-06-28 17:55:07 -0700809
Chet Haasea18a86b2010-09-07 13:20:00 -0700810 AnimatorSetListener(AnimatorSet animatorSet) {
811 mAnimatorSet = animatorSet;
Chet Haase17fb4b02010-06-28 17:55:07 -0700812 }
813
Chet Haasea18a86b2010-09-07 13:20:00 -0700814 public void onAnimationCancel(Animator animation) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700815 if (!mTerminated) {
816 // Listeners are already notified of the AnimatorSet canceling in cancel().
817 // The logic below only kicks in when animations end normally
818 if (mPlayingSet.size() == 0) {
819 if (mListeners != null) {
820 int numListeners = mListeners.size();
821 for (int i = 0; i < numListeners; ++i) {
822 mListeners.get(i).onAnimationCancel(mAnimatorSet);
823 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700824 }
825 }
826 }
827 }
828
829 @SuppressWarnings("unchecked")
Chet Haasea18a86b2010-09-07 13:20:00 -0700830 public void onAnimationEnd(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700831 animation.removeListener(this);
832 mPlayingSet.remove(animation);
Chet Haasea18a86b2010-09-07 13:20:00 -0700833 Node animNode = mAnimatorSet.mNodeMap.get(animation);
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700834 animNode.done = true;
Chet Haase7dfacdb2011-07-11 17:01:56 -0700835 if (!mTerminated) {
836 // Listeners are already notified of the AnimatorSet ending in cancel() or
837 // end(); the logic below only kicks in when animations end normally
838 ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes;
839 boolean allDone = true;
840 int numSortedNodes = sortedNodes.size();
841 for (int i = 0; i < numSortedNodes; ++i) {
842 if (!sortedNodes.get(i).done) {
843 allDone = false;
844 break;
845 }
Chet Haasea6e4a1752010-07-23 11:08:47 -0700846 }
Chet Haase7dfacdb2011-07-11 17:01:56 -0700847 if (allDone) {
848 // If this was the last child animation to end, then notify listeners that this
849 // AnimatorSet has ended
850 if (mListeners != null) {
851 ArrayList<AnimatorListener> tmpListeners =
852 (ArrayList<AnimatorListener>) mListeners.clone();
853 int numListeners = tmpListeners.size();
854 for (int i = 0; i < numListeners; ++i) {
855 tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
856 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700857 }
Chet Haase8b699792011-08-05 15:20:19 -0700858 mAnimatorSet.mStarted = false;
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700859 mAnimatorSet.mPaused = false;
Chet Haase17fb4b02010-06-28 17:55:07 -0700860 }
861 }
862 }
863
864 // Nothing to do
Chet Haasea18a86b2010-09-07 13:20:00 -0700865 public void onAnimationRepeat(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700866 }
867
868 // Nothing to do
Chet Haasea18a86b2010-09-07 13:20:00 -0700869 public void onAnimationStart(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700870 }
871
872 }
873
874 /**
875 * This method sorts the current set of nodes, if needed. The sort is a simple
876 * DependencyGraph sort, which goes like this:
877 * - All nodes without dependencies become 'roots'
878 * - while roots list is not null
879 * - for each root r
880 * - add r to sorted list
881 * - remove r as a dependency from any other node
882 * - any nodes with no dependencies are added to the roots list
883 */
884 private void sortNodes() {
885 if (mNeedsSort) {
886 mSortedNodes.clear();
887 ArrayList<Node> roots = new ArrayList<Node>();
Chet Haase7c608f22010-10-22 17:54:04 -0700888 int numNodes = mNodes.size();
889 for (int i = 0; i < numNodes; ++i) {
890 Node node = mNodes.get(i);
Chet Haase17fb4b02010-06-28 17:55:07 -0700891 if (node.dependencies == null || node.dependencies.size() == 0) {
892 roots.add(node);
893 }
894 }
895 ArrayList<Node> tmpRoots = new ArrayList<Node>();
896 while (roots.size() > 0) {
Chet Haase7c608f22010-10-22 17:54:04 -0700897 int numRoots = roots.size();
898 for (int i = 0; i < numRoots; ++i) {
899 Node root = roots.get(i);
Chet Haase17fb4b02010-06-28 17:55:07 -0700900 mSortedNodes.add(root);
901 if (root.nodeDependents != null) {
Chet Haase7c608f22010-10-22 17:54:04 -0700902 int numDependents = root.nodeDependents.size();
903 for (int j = 0; j < numDependents; ++j) {
904 Node node = root.nodeDependents.get(j);
Chet Haase17fb4b02010-06-28 17:55:07 -0700905 node.nodeDependencies.remove(root);
906 if (node.nodeDependencies.size() == 0) {
907 tmpRoots.add(node);
908 }
909 }
910 }
911 }
Chet Haase010dbaa2010-07-19 17:29:49 -0700912 roots.clear();
Chet Haase17fb4b02010-06-28 17:55:07 -0700913 roots.addAll(tmpRoots);
914 tmpRoots.clear();
915 }
916 mNeedsSort = false;
917 if (mSortedNodes.size() != mNodes.size()) {
918 throw new IllegalStateException("Circular dependencies cannot exist"
Chet Haasea18a86b2010-09-07 13:20:00 -0700919 + " in AnimatorSet");
Chet Haase17fb4b02010-06-28 17:55:07 -0700920 }
921 } else {
922 // Doesn't need sorting, but still need to add in the nodeDependencies list
923 // because these get removed as the event listeners fire and the dependencies
924 // are satisfied
Chet Haase7c608f22010-10-22 17:54:04 -0700925 int numNodes = mNodes.size();
926 for (int i = 0; i < numNodes; ++i) {
927 Node node = mNodes.get(i);
Chet Haase17fb4b02010-06-28 17:55:07 -0700928 if (node.dependencies != null && node.dependencies.size() > 0) {
Chet Haase7c608f22010-10-22 17:54:04 -0700929 int numDependencies = node.dependencies.size();
930 for (int j = 0; j < numDependencies; ++j) {
931 Dependency dependency = node.dependencies.get(j);
Chet Haase17fb4b02010-06-28 17:55:07 -0700932 if (node.nodeDependencies == null) {
933 node.nodeDependencies = new ArrayList<Node>();
934 }
935 if (!node.nodeDependencies.contains(dependency.node)) {
936 node.nodeDependencies.add(dependency.node);
937 }
938 }
939 }
Chet Haase7dfacdb2011-07-11 17:01:56 -0700940 // nodes are 'done' by default; they become un-done when started, and done
941 // again when ended
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700942 node.done = false;
Chet Haase17fb4b02010-06-28 17:55:07 -0700943 }
944 }
945 }
946
947 /**
ztenghui7bc6a3f2014-07-15 15:12:12 -0700948 * @hide
949 */
950 @Override
951 public boolean canReverse() {
952 if (!mReversible) {
953 return false;
954 }
955 // Loop to make sure all the Nodes can reverse.
956 for (Node node : mNodes) {
957 if (!node.animation.canReverse() || node.animation.getStartDelay() > 0) {
958 return false;
959 }
960 }
961 return true;
962 }
963
964 /**
965 * @hide
966 */
967 @Override
968 public void reverse() {
969 if (canReverse()) {
970 for (Node node : mNodes) {
971 node.animation.reverse();
972 }
973 }
974 }
975
Chet Haased4307532014-12-01 06:32:38 -0800976 @Override
977 public String toString() {
978 String returnVal = "AnimatorSet@" + Integer.toHexString(hashCode()) + "{";
979 boolean prevNeedsSort = mNeedsSort;
980 sortNodes();
981 mNeedsSort = prevNeedsSort;
982 for (Node node : mSortedNodes) {
983 returnVal += "\n " + node.animation.toString();
984 }
985 return returnVal + "\n}";
986 }
987
ztenghui7bc6a3f2014-07-15 15:12:12 -0700988 /**
Chet Haase17fb4b02010-06-28 17:55:07 -0700989 * Dependency holds information about the node that some other node is
990 * dependent upon and the nature of that dependency.
991 *
992 */
993 private static class Dependency {
994 static final int WITH = 0; // dependent node must start with this dependency node
995 static final int AFTER = 1; // dependent node must start when this dependency node finishes
996
997 // The node that the other node with this Dependency is dependent upon
998 public Node node;
999
1000 // The nature of the dependency (WITH or AFTER)
1001 public int rule;
1002
1003 public Dependency(Node node, int rule) {
1004 this.node = node;
1005 this.rule = rule;
1006 }
1007 }
1008
1009 /**
Chet Haasea18a86b2010-09-07 13:20:00 -07001010 * A Node is an embodiment of both the Animator that it wraps as well as
Chet Haase17fb4b02010-06-28 17:55:07 -07001011 * any dependencies that are associated with that Animation. This includes
1012 * both dependencies upon other nodes (in the dependencies list) as
1013 * well as dependencies of other nodes upon this (in the nodeDependents list).
1014 */
Chet Haase49afa5b2010-08-23 11:39:53 -07001015 private static class Node implements Cloneable {
Chet Haasea18a86b2010-09-07 13:20:00 -07001016 public Animator animation;
Chet Haase17fb4b02010-06-28 17:55:07 -07001017
1018 /**
1019 * These are the dependencies that this node's animation has on other
1020 * nodes. For example, if this node's animation should begin with some
1021 * other animation ends, then there will be an item in this node's
1022 * dependencies list for that other animation's node.
1023 */
1024 public ArrayList<Dependency> dependencies = null;
1025
1026 /**
1027 * tmpDependencies is a runtime detail. We use the dependencies list for sorting.
1028 * But we also use the list to keep track of when multiple dependencies are satisfied,
1029 * but removing each dependency as it is satisfied. We do not want to remove
1030 * the dependency itself from the list, because we need to retain that information
Chet Haasea18a86b2010-09-07 13:20:00 -07001031 * if the AnimatorSet is launched in the future. So we create a copy of the dependency
1032 * list when the AnimatorSet starts and use this tmpDependencies list to track the
Chet Haase17fb4b02010-06-28 17:55:07 -07001033 * list of satisfied dependencies.
1034 */
1035 public ArrayList<Dependency> tmpDependencies = null;
1036
1037 /**
1038 * nodeDependencies is just a list of the nodes that this Node is dependent upon.
1039 * This information is used in sortNodes(), to determine when a node is a root.
1040 */
1041 public ArrayList<Node> nodeDependencies = null;
1042
1043 /**
1044 * nodeDepdendents is the list of nodes that have this node as a dependency. This
1045 * is a utility field used in sortNodes to facilitate removing this node as a
1046 * dependency when it is a root node.
1047 */
1048 public ArrayList<Node> nodeDependents = null;
1049
1050 /**
Chet Haase1e0ac5a2010-08-27 08:32:11 -07001051 * Flag indicating whether the animation in this node is finished. This flag
Chet Haasea18a86b2010-09-07 13:20:00 -07001052 * is used by AnimatorSet to check, as each animation ends, whether all child animations
1053 * are done and it's time to send out an end event for the entire AnimatorSet.
Chet Haase1e0ac5a2010-08-27 08:32:11 -07001054 */
1055 public boolean done = false;
1056
1057 /**
Yigit Boyard422dc32014-09-25 12:23:35 -07001058 * Temporary field to hold the clone in AnimatorSet#clone. Cleaned after clone is complete
1059 */
1060 private Node mTmpClone = null;
1061
1062 /**
Chet Haase17fb4b02010-06-28 17:55:07 -07001063 * Constructs the Node with the animation that it encapsulates. A Node has no
1064 * dependencies by default; dependencies are added via the addDependency()
1065 * method.
1066 *
1067 * @param animation The animation that the Node encapsulates.
1068 */
Chet Haasea18a86b2010-09-07 13:20:00 -07001069 public Node(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -07001070 this.animation = animation;
1071 }
1072
1073 /**
1074 * Add a dependency to this Node. The dependency includes information about the
1075 * node that this node is dependency upon and the nature of the dependency.
1076 * @param dependency
1077 */
1078 public void addDependency(Dependency dependency) {
1079 if (dependencies == null) {
1080 dependencies = new ArrayList<Dependency>();
1081 nodeDependencies = new ArrayList<Node>();
1082 }
1083 dependencies.add(dependency);
1084 if (!nodeDependencies.contains(dependency.node)) {
1085 nodeDependencies.add(dependency.node);
1086 }
1087 Node dependencyNode = dependency.node;
1088 if (dependencyNode.nodeDependents == null) {
1089 dependencyNode.nodeDependents = new ArrayList<Node>();
1090 }
1091 dependencyNode.nodeDependents.add(this);
1092 }
Chet Haase49afa5b2010-08-23 11:39:53 -07001093
1094 @Override
Chet Haase21cd1382010-09-01 17:42:29 -07001095 public Node clone() {
1096 try {
1097 Node node = (Node) super.clone();
ztenghuib1e80d82015-04-21 13:00:08 -07001098 node.animation = animation.clone();
1099 node.done = false;
Chet Haase21cd1382010-09-01 17:42:29 -07001100 return node;
1101 } catch (CloneNotSupportedException e) {
1102 throw new AssertionError();
1103 }
Chet Haase49afa5b2010-08-23 11:39:53 -07001104 }
Chet Haase17fb4b02010-06-28 17:55:07 -07001105 }
1106
1107 /**
1108 * The <code>Builder</code> object is a utility class to facilitate adding animations to a
Chet Haasea18a86b2010-09-07 13:20:00 -07001109 * <code>AnimatorSet</code> along with the relationships between the various animations. The
Chet Haase17fb4b02010-06-28 17:55:07 -07001110 * intention of the <code>Builder</code> methods, along with the {@link
Chet Haase8b699792011-08-05 15:20:19 -07001111 * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible
1112 * to express the dependency relationships of animations in a natural way. Developers can also
1113 * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
Chet Haasea18a86b2010-09-07 13:20:00 -07001114 * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
1115 * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
Chet Haase17fb4b02010-06-28 17:55:07 -07001116 * <p/>
1117 * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
Chet Haasea18a86b2010-09-07 13:20:00 -07001118 * internally via a call to {@link AnimatorSet#play(Animator)}.</p>
Chet Haase17fb4b02010-06-28 17:55:07 -07001119 * <p/>
Chet Haasea18a86b2010-09-07 13:20:00 -07001120 * <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 -07001121 * play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
1122 * <pre>
Chet Haasea18a86b2010-09-07 13:20:00 -07001123 * AnimatorSet s = new AnimatorSet();
Chet Haase17fb4b02010-06-28 17:55:07 -07001124 * s.play(anim1).with(anim2);
1125 * s.play(anim2).before(anim3);
1126 * s.play(anim4).after(anim3);
1127 * </pre>
1128 * <p/>
Chet Haasea18a86b2010-09-07 13:20:00 -07001129 * <p>Note in the example that both {@link Builder#before(Animator)} and {@link
1130 * Builder#after(Animator)} are used. These are just different ways of expressing the same
Chet Haase17fb4b02010-06-28 17:55:07 -07001131 * relationship and are provided to make it easier to say things in a way that is more natural,
1132 * depending on the situation.</p>
1133 * <p/>
1134 * <p>It is possible to make several calls into the same <code>Builder</code> object to express
1135 * multiple relationships. However, note that it is only the animation passed into the initial
Chet Haasea18a86b2010-09-07 13:20:00 -07001136 * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
Chet Haase17fb4b02010-06-28 17:55:07 -07001137 * calls to the <code>Builder</code> object. For example, the following code starts both anim2
1138 * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
1139 * anim3:
1140 * <pre>
Chet Haasea18a86b2010-09-07 13:20:00 -07001141 * AnimatorSet s = new AnimatorSet();
Chet Haase17fb4b02010-06-28 17:55:07 -07001142 * s.play(anim1).before(anim2).before(anim3);
1143 * </pre>
1144 * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
1145 * relationship correctly:</p>
1146 * <pre>
Chet Haasea18a86b2010-09-07 13:20:00 -07001147 * AnimatorSet s = new AnimatorSet();
Chet Haase17fb4b02010-06-28 17:55:07 -07001148 * s.play(anim1).before(anim2);
1149 * s.play(anim2).before(anim3);
1150 * </pre>
1151 * <p/>
1152 * <p>Note that it is possible to express relationships that cannot be resolved and will not
1153 * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
1154 * sense. In general, circular dependencies like this one (or more indirect ones where a depends
Chet Haasea18a86b2010-09-07 13:20:00 -07001155 * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
1156 * that can boil down to a simple, one-way relationship of animations starting with, before, and
Chet Haase17fb4b02010-06-28 17:55:07 -07001157 * after other, different, animations.</p>
1158 */
1159 public class Builder {
1160
1161 /**
1162 * This tracks the current node being processed. It is supplied to the play() method
Chet Haasea18a86b2010-09-07 13:20:00 -07001163 * of AnimatorSet and passed into the constructor of Builder.
Chet Haase17fb4b02010-06-28 17:55:07 -07001164 */
1165 private Node mCurrentNode;
1166
1167 /**
Chet Haasea18a86b2010-09-07 13:20:00 -07001168 * package-private constructor. Builders are only constructed by AnimatorSet, when the
Chet Haase17fb4b02010-06-28 17:55:07 -07001169 * play() method is called.
1170 *
1171 * @param anim The animation that is the dependency for the other animations passed into
1172 * the other methods of this Builder object.
1173 */
Chet Haasea18a86b2010-09-07 13:20:00 -07001174 Builder(Animator anim) {
Chet Haase17fb4b02010-06-28 17:55:07 -07001175 mCurrentNode = mNodeMap.get(anim);
1176 if (mCurrentNode == null) {
1177 mCurrentNode = new Node(anim);
1178 mNodeMap.put(anim, mCurrentNode);
1179 mNodes.add(mCurrentNode);
1180 }
1181 }
1182
1183 /**
1184 * Sets up the given animation to play at the same time as the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001185 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
Chet Haase17fb4b02010-06-28 17:55:07 -07001186 *
1187 * @param anim The animation that will play when the animation supplied to the
Chet Haasea18a86b2010-09-07 13:20:00 -07001188 * {@link AnimatorSet#play(Animator)} method starts.
Chet Haase17fb4b02010-06-28 17:55:07 -07001189 */
Chet Haase2970c492010-11-09 13:58:04 -08001190 public Builder with(Animator anim) {
Chet Haase17fb4b02010-06-28 17:55:07 -07001191 Node node = mNodeMap.get(anim);
1192 if (node == null) {
1193 node = new Node(anim);
1194 mNodeMap.put(anim, node);
1195 mNodes.add(node);
1196 }
1197 Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH);
1198 node.addDependency(dependency);
Chet Haase2970c492010-11-09 13:58:04 -08001199 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001200 }
1201
1202 /**
1203 * Sets up the given animation to play when the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001204 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
Chet Haase17fb4b02010-06-28 17:55:07 -07001205 * ends.
1206 *
1207 * @param anim The animation that will play when the animation supplied to the
Chet Haasea18a86b2010-09-07 13:20:00 -07001208 * {@link AnimatorSet#play(Animator)} method ends.
Chet Haase17fb4b02010-06-28 17:55:07 -07001209 */
Chet Haase2970c492010-11-09 13:58:04 -08001210 public Builder before(Animator anim) {
ztenghui7bc6a3f2014-07-15 15:12:12 -07001211 mReversible = false;
Chet Haase17fb4b02010-06-28 17:55:07 -07001212 Node node = mNodeMap.get(anim);
1213 if (node == null) {
1214 node = new Node(anim);
1215 mNodeMap.put(anim, node);
1216 mNodes.add(node);
1217 }
1218 Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER);
1219 node.addDependency(dependency);
Chet Haase2970c492010-11-09 13:58:04 -08001220 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001221 }
1222
1223 /**
1224 * Sets up the given animation to play when the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001225 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
Chet Haase17fb4b02010-06-28 17:55:07 -07001226 * to start when the animation supplied in this method call ends.
1227 *
1228 * @param anim The animation whose end will cause the animation supplied to the
Chet Haasea18a86b2010-09-07 13:20:00 -07001229 * {@link AnimatorSet#play(Animator)} method to play.
Chet Haase17fb4b02010-06-28 17:55:07 -07001230 */
Chet Haase2970c492010-11-09 13:58:04 -08001231 public Builder after(Animator anim) {
ztenghui7bc6a3f2014-07-15 15:12:12 -07001232 mReversible = false;
Chet Haase17fb4b02010-06-28 17:55:07 -07001233 Node node = mNodeMap.get(anim);
1234 if (node == null) {
1235 node = new Node(anim);
1236 mNodeMap.put(anim, node);
1237 mNodes.add(node);
1238 }
1239 Dependency dependency = new Dependency(node, Dependency.AFTER);
1240 mCurrentNode.addDependency(dependency);
Chet Haase2970c492010-11-09 13:58:04 -08001241 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001242 }
1243
1244 /**
1245 * Sets up the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001246 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
Chet Haase17fb4b02010-06-28 17:55:07 -07001247 * to play when the given amount of time elapses.
1248 *
1249 * @param delay The number of milliseconds that should elapse before the
1250 * animation starts.
1251 */
Chet Haase2970c492010-11-09 13:58:04 -08001252 public Builder after(long delay) {
Chet Haasea18a86b2010-09-07 13:20:00 -07001253 // setup dummy ValueAnimator just to run the clock
Chet Haase2794eb32010-10-12 16:29:28 -07001254 ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
1255 anim.setDuration(delay);
1256 after(anim);
Chet Haase2970c492010-11-09 13:58:04 -08001257 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001258 }
1259
1260 }
1261
1262}