blob: 3385a17002bcebfa69caebc4d8b46db9c352f55b [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
Doris Liud7444422015-05-11 13:23:31 -070019import android.util.ArrayMap;
Doris Liu13099142015-07-10 17:32:41 -070020import android.util.Log;
Doris Liud7444422015-05-11 13:23:31 -070021
Chet Haase17fb4b02010-06-28 17:55:07 -070022import java.util.ArrayList;
Chet Haase37a7bec2010-11-30 15:55:39 -080023import java.util.Collection;
Chet Haase37a7bec2010-11-30 15:55:39 -080024import java.util.List;
Chet Haase17fb4b02010-06-28 17:55:07 -070025
26/**
Chet Haasea18a86b2010-09-07 13:20:00 -070027 * This class plays a set of {@link Animator} objects in the specified order. Animations
Chet Haase17fb4b02010-06-28 17:55:07 -070028 * can be set up to play together, in sequence, or after a specified delay.
29 *
Chet Haasea18a86b2010-09-07 13:20:00 -070030 * <p>There are two different approaches to adding animations to a <code>AnimatorSet</code>:
31 * either the {@link AnimatorSet#playTogether(Animator[]) playTogether()} or
32 * {@link AnimatorSet#playSequentially(Animator[]) playSequentially()} methods can be called to add
33 * a set of animations all at once, or the {@link AnimatorSet#play(Animator)} can be
34 * used in conjunction with methods in the {@link AnimatorSet.Builder Builder}
Chet Haase17fb4b02010-06-28 17:55:07 -070035 * class to add animations
36 * one by one.</p>
37 *
Chet Haasea18a86b2010-09-07 13:20:00 -070038 * <p>It is possible to set up a <code>AnimatorSet</code> with circular dependencies between
Chet Haase17fb4b02010-06-28 17:55:07 -070039 * its animations. For example, an animation a1 could be set up to start before animation a2, a2
40 * before a3, and a3 before a1. The results of this configuration are undefined, but will typically
41 * result in none of the affected animations being played. Because of this (and because
42 * circular dependencies do not make logical sense anyway), circular dependencies
43 * should be avoided, and the dependency flow of animations should only be in one direction.
Joe Fernandez3aef8e1d2011-12-20 10:38:34 -080044 *
45 * <div class="special reference">
46 * <h3>Developer Guides</h3>
47 * <p>For more information about animating with {@code AnimatorSet}, read the
48 * <a href="{@docRoot}guide/topics/graphics/prop-animation.html#choreography">Property
49 * Animation</a> developer guide.</p>
50 * </div>
Chet Haase17fb4b02010-06-28 17:55:07 -070051 */
Chet Haasea18a86b2010-09-07 13:20:00 -070052public final class AnimatorSet extends Animator {
Chet Haase17fb4b02010-06-28 17:55:07 -070053
Doris Liu13099142015-07-10 17:32:41 -070054 private static final String TAG = "AnimatorSet";
Chet Haase17fb4b02010-06-28 17:55:07 -070055 /**
Chet Haase49afa5b2010-08-23 11:39:53 -070056 * Internal variables
57 * NOTE: This object implements the clone() method, making a deep copy of any referenced
58 * objects. As other non-trivial fields are added to this class, make sure to add logic
59 * to clone() to make deep copies of them.
60 */
61
62 /**
Chet Haase3b69b6f2010-07-29 09:09:05 -070063 * Tracks animations currently being played, so that we know what to
Chet Haasea18a86b2010-09-07 13:20:00 -070064 * cancel or end when cancel() or end() is called on this AnimatorSet
Chet Haase17fb4b02010-06-28 17:55:07 -070065 */
Chet Haasea18a86b2010-09-07 13:20:00 -070066 private ArrayList<Animator> mPlayingSet = new ArrayList<Animator>();
Chet Haase17fb4b02010-06-28 17:55:07 -070067
68 /**
Chet Haasea18a86b2010-09-07 13:20:00 -070069 * Contains all nodes, mapped to their respective Animators. When new
70 * dependency information is added for an Animator, we want to add it
71 * to a single node representing that Animator, not create a new Node
Chet Haase17fb4b02010-06-28 17:55:07 -070072 * if one already exists.
73 */
Doris Liud7444422015-05-11 13:23:31 -070074 private ArrayMap<Animator, Node> mNodeMap = new ArrayMap<Animator, Node>();
Chet Haase17fb4b02010-06-28 17:55:07 -070075
76 /**
Chet Haasea18a86b2010-09-07 13:20:00 -070077 * Set of all nodes created for this AnimatorSet. This list is used upon
78 * starting the set, and the nodes are placed in sorted order into the
Chet Haase17fb4b02010-06-28 17:55:07 -070079 * sortedNodes collection.
80 */
Chet Haase49afa5b2010-08-23 11:39:53 -070081 private ArrayList<Node> mNodes = new ArrayList<Node>();
Chet Haase17fb4b02010-06-28 17:55:07 -070082
83 /**
Doris Liu13099142015-07-10 17:32:41 -070084 * Animator Listener that tracks the lifecycle of each Animator in the set. It will be added
85 * to each Animator before they start and removed after they end.
Chet Haase17fb4b02010-06-28 17:55:07 -070086 */
Doris Liu13099142015-07-10 17:32:41 -070087 private AnimatorSetListener mSetListener = new AnimatorSetListener(this);
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 */
Doris Liu13099142015-07-10 17:32:41 -070096 private boolean mTerminated = false;
97
98 /**
99 * Tracks whether any change has been made to the AnimatorSet, which is then used to
100 * determine whether the dependency graph should be re-constructed.
101 */
102 private boolean mDependencyDirty = false;
Chet Haase010dbaa2010-07-19 17:29:49 -0700103
Chet Haase8b699792011-08-05 15:20:19 -0700104 /**
105 * Indicates whether an AnimatorSet has been start()'d, whether or
106 * not there is a nonzero startDelay.
107 */
108 private boolean mStarted = false;
109
Chet Haase21cd1382010-09-01 17:42:29 -0700110 // The amount of time in ms to delay starting the animation after start() is called
111 private long mStartDelay = 0;
112
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800113 // Animator used for a nonzero startDelay
Doris Liu13099142015-07-10 17:32:41 -0700114 private ValueAnimator mDelayAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(0);
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800115
Doris Liu13099142015-07-10 17:32:41 -0700116 // Root of the dependency tree of all the animators in the set. In this tree, parent-child
117 // relationship captures the order of animation (i.e. parent and child will play sequentially),
118 // and sibling relationship indicates "with" relationship, as sibling animators start at the
119 // same time.
120 private Node mRootNode = new Node(mDelayAnim);
Chet Haase21cd1382010-09-01 17:42:29 -0700121
122 // How long the child animations should last in ms. The default value is negative, which
Chet Haasea18a86b2010-09-07 13:20:00 -0700123 // simply means that there is no duration set on the AnimatorSet. When a real duration is
Chet Haase21cd1382010-09-01 17:42:29 -0700124 // set, it is passed along to the child animations.
125 private long mDuration = -1;
126
Chet Haase430742f2013-04-12 11:18:36 -0700127 // Records the interpolator for the set. Null value indicates that no interpolator
128 // was set on this AnimatorSet, so it should not be passed down to the children.
129 private TimeInterpolator mInterpolator = null;
130
Doris Liu13099142015-07-10 17:32:41 -0700131 // Whether the AnimatorSet can be reversed.
ztenghui7bc6a3f2014-07-15 15:12:12 -0700132 private boolean mReversible = true;
Doris Liu13099142015-07-10 17:32:41 -0700133 // The total duration of finishing all the Animators in the set.
134 private long mTotalDuration = 0;
135
136 public AnimatorSet() {
137 super();
138 mNodeMap.put(mDelayAnim, mRootNode);
139 mNodes.add(mRootNode);
140 }
141
Chet Haase010dbaa2010-07-19 17:29:49 -0700142 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700143 * Sets up this AnimatorSet to play all of the supplied animations at the same time.
Chet Haase430742f2013-04-12 11:18:36 -0700144 * This is equivalent to calling {@link #play(Animator)} with the first animator in the
145 * set and then {@link Builder#with(Animator)} with each of the other animators. Note that
146 * an Animator with a {@link Animator#setStartDelay(long) startDelay} will not actually
147 * start until that delay elapses, which means that if the first animator in the list
148 * supplied to this constructor has a startDelay, none of the other animators will start
149 * until that first animator's startDelay has elapsed.
Chet Haase17fb4b02010-06-28 17:55:07 -0700150 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700151 * @param items The animations that will be started simultaneously.
Chet Haase17fb4b02010-06-28 17:55:07 -0700152 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700153 public void playTogether(Animator... items) {
154 if (items != null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700155 Builder builder = play(items[0]);
156 for (int i = 1; i < items.length; ++i) {
157 builder.with(items[i]);
Chet Haase17fb4b02010-06-28 17:55:07 -0700158 }
159 }
160 }
161
162 /**
Chet Haase37a7bec2010-11-30 15:55:39 -0800163 * Sets up this AnimatorSet to play all of the supplied animations at the same time.
164 *
165 * @param items The animations that will be started simultaneously.
166 */
167 public void playTogether(Collection<Animator> items) {
168 if (items != null && items.size() > 0) {
Chet Haase37a7bec2010-11-30 15:55:39 -0800169 Builder builder = null;
170 for (Animator anim : items) {
171 if (builder == null) {
172 builder = play(anim);
173 } else {
174 builder.with(anim);
175 }
176 }
177 }
178 }
179
180 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700181 * Sets up this AnimatorSet to play each of the supplied animations when the
Chet Haase17fb4b02010-06-28 17:55:07 -0700182 * previous animation ends.
183 *
Chet Haase37a7bec2010-11-30 15:55:39 -0800184 * @param items The animations that will be started one after another.
Chet Haase17fb4b02010-06-28 17:55:07 -0700185 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700186 public void playSequentially(Animator... items) {
187 if (items != null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700188 if (items.length == 1) {
189 play(items[0]);
Chet Haase17fb4b02010-06-28 17:55:07 -0700190 } else {
ztenghui7bc6a3f2014-07-15 15:12:12 -0700191 mReversible = false;
Chet Haasea18a86b2010-09-07 13:20:00 -0700192 for (int i = 0; i < items.length - 1; ++i) {
Doris Liu13099142015-07-10 17:32:41 -0700193 play(items[i]).before(items[i + 1]);
Chet Haase17fb4b02010-06-28 17:55:07 -0700194 }
195 }
196 }
197 }
198
199 /**
Chet Haase37a7bec2010-11-30 15:55:39 -0800200 * Sets up this AnimatorSet to play each of the supplied animations when the
201 * previous animation ends.
202 *
203 * @param items The animations that will be started one after another.
204 */
205 public void playSequentially(List<Animator> items) {
206 if (items != null && items.size() > 0) {
Chet Haase37a7bec2010-11-30 15:55:39 -0800207 if (items.size() == 1) {
208 play(items.get(0));
209 } else {
ztenghui7bc6a3f2014-07-15 15:12:12 -0700210 mReversible = false;
Chet Haase37a7bec2010-11-30 15:55:39 -0800211 for (int i = 0; i < items.size() - 1; ++i) {
Doris Liu13099142015-07-10 17:32:41 -0700212 play(items.get(i)).before(items.get(i + 1));
Chet Haase37a7bec2010-11-30 15:55:39 -0800213 }
214 }
215 }
216 }
217
218 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700219 * Returns the current list of child Animator objects controlled by this
220 * AnimatorSet. This is a copy of the internal list; modifications to the returned list
221 * will not affect the AnimatorSet, although changes to the underlying Animator objects
222 * will affect those objects being managed by the AnimatorSet.
Chet Haasef54a8d72010-07-22 14:44:59 -0700223 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700224 * @return ArrayList<Animator> The list of child animations of this AnimatorSet.
Chet Haasef54a8d72010-07-22 14:44:59 -0700225 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700226 public ArrayList<Animator> getChildAnimations() {
227 ArrayList<Animator> childList = new ArrayList<Animator>();
Doris Liu13099142015-07-10 17:32:41 -0700228 int size = mNodes.size();
229 for (int i = 0; i < size; i++) {
230 Node node = mNodes.get(i);
Doris Liudbf69e42015-08-05 20:00:11 -0700231 if (node != mRootNode) {
232 childList.add(node.mAnimation);
233 }
Chet Haasef54a8d72010-07-22 14:44:59 -0700234 }
235 return childList;
236 }
237
238 /**
Chet Haase811ed1062010-08-06 10:38:15 -0700239 * Sets the target object for all current {@link #getChildAnimations() child animations}
Chet Haasea18a86b2010-09-07 13:20:00 -0700240 * of this AnimatorSet that take targets ({@link ObjectAnimator} and
241 * AnimatorSet).
Chet Haase811ed1062010-08-06 10:38:15 -0700242 *
243 * @param target The object being animated
244 */
Chet Haase21cd1382010-09-01 17:42:29 -0700245 @Override
Chet Haase811ed1062010-08-06 10:38:15 -0700246 public void setTarget(Object target) {
Doris Liu13099142015-07-10 17:32:41 -0700247 int size = mNodes.size();
248 for (int i = 0; i < size; i++) {
249 Node node = mNodes.get(i);
250 Animator animation = node.mAnimation;
Chet Haasea18a86b2010-09-07 13:20:00 -0700251 if (animation instanceof AnimatorSet) {
252 ((AnimatorSet)animation).setTarget(target);
253 } else if (animation instanceof ObjectAnimator) {
254 ((ObjectAnimator)animation).setTarget(target);
Chet Haase811ed1062010-08-06 10:38:15 -0700255 }
256 }
257 }
258
259 /**
Yigit Boyard422dc32014-09-25 12:23:35 -0700260 * @hide
261 */
262 @Override
263 public int getChangingConfigurations() {
264 int conf = super.getChangingConfigurations();
265 final int nodeCount = mNodes.size();
266 for (int i = 0; i < nodeCount; i ++) {
Doris Liu13099142015-07-10 17:32:41 -0700267 conf |= mNodes.get(i).mAnimation.getChangingConfigurations();
Yigit Boyard422dc32014-09-25 12:23:35 -0700268 }
269 return conf;
270 }
271
272 /**
Chet Haasee0ee2e92010-10-07 09:06:18 -0700273 * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations}
Chet Haase430742f2013-04-12 11:18:36 -0700274 * of this AnimatorSet. The default value is null, which means that no interpolator
275 * is set on this AnimatorSet. Setting the interpolator to any non-null value
276 * will cause that interpolator to be set on the child animations
277 * when the set is started.
Chet Haase21cd1382010-09-01 17:42:29 -0700278 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700279 * @param interpolator the interpolator to be used by each child animation of this AnimatorSet
Chet Haase21cd1382010-09-01 17:42:29 -0700280 */
281 @Override
Chet Haasee0ee2e92010-10-07 09:06:18 -0700282 public void setInterpolator(TimeInterpolator interpolator) {
Chet Haase430742f2013-04-12 11:18:36 -0700283 mInterpolator = interpolator;
284 }
285
286 @Override
287 public TimeInterpolator getInterpolator() {
288 return mInterpolator;
Chet Haase21cd1382010-09-01 17:42:29 -0700289 }
290
291 /**
Chet Haase17fb4b02010-06-28 17:55:07 -0700292 * This method creates a <code>Builder</code> object, which is used to
293 * set up playing constraints. This initial <code>play()</code> method
294 * tells the <code>Builder</code> the animation that is the dependency for
295 * the succeeding commands to the <code>Builder</code>. For example,
Chet Haasea18a86b2010-09-07 13:20:00 -0700296 * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play
Chet Haase17fb4b02010-06-28 17:55:07 -0700297 * <code>a1</code> and <code>a2</code> at the same time,
Chet Haasea18a86b2010-09-07 13:20:00 -0700298 * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play
Chet Haase17fb4b02010-06-28 17:55:07 -0700299 * <code>a1</code> first, followed by <code>a2</code>, and
Chet Haasea18a86b2010-09-07 13:20:00 -0700300 * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play
Chet Haase17fb4b02010-06-28 17:55:07 -0700301 * <code>a2</code> first, followed by <code>a1</code>.
302 *
303 * <p>Note that <code>play()</code> is the only way to tell the
304 * <code>Builder</code> the animation upon which the dependency is created,
305 * so successive calls to the various functions in <code>Builder</code>
306 * will all refer to the initial parameter supplied in <code>play()</code>
307 * as the dependency of the other animations. For example, calling
308 * <code>play(a1).before(a2).before(a3)</code> will play both <code>a2</code>
309 * and <code>a3</code> when a1 ends; it does not set up a dependency between
310 * <code>a2</code> and <code>a3</code>.</p>
311 *
312 * @param anim The animation that is the dependency used in later calls to the
313 * methods in the returned <code>Builder</code> object. A null parameter will result
314 * in a null <code>Builder</code> return value.
Chet Haasea18a86b2010-09-07 13:20:00 -0700315 * @return Builder The object that constructs the AnimatorSet based on the dependencies
Chet Haase17fb4b02010-06-28 17:55:07 -0700316 * outlined in the calls to <code>play</code> and the other methods in the
317 * <code>Builder</code object.
318 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700319 public Builder play(Animator anim) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700320 if (anim != null) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700321 return new Builder(anim);
322 }
323 return null;
324 }
325
326 /**
327 * {@inheritDoc}
328 *
Chet Haase8b699792011-08-05 15:20:19 -0700329 * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it
330 * is responsible for.</p>
Chet Haase17fb4b02010-06-28 17:55:07 -0700331 */
332 @SuppressWarnings("unchecked")
333 @Override
334 public void cancel() {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700335 mTerminated = true;
Chet Haase8b699792011-08-05 15:20:19 -0700336 if (isStarted()) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700337 ArrayList<AnimatorListener> tmpListeners = null;
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800338 if (mListeners != null) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700339 tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
Doris Liu13099142015-07-10 17:32:41 -0700340 int size = tmpListeners.size();
341 for (int i = 0; i < size; i++) {
342 tmpListeners.get(i).onAnimationCancel(this);
Chet Haase7dfacdb2011-07-11 17:01:56 -0700343 }
344 }
Doris Liu13099142015-07-10 17:32:41 -0700345 ArrayList<Animator> playingSet = new ArrayList<>(mPlayingSet);
346 int setSize = playingSet.size();
347 for (int i = 0; i < setSize; i++) {
348 playingSet.get(i).cancel();
Chet Haase7dfacdb2011-07-11 17:01:56 -0700349 }
350 if (tmpListeners != null) {
Doris Liu13099142015-07-10 17:32:41 -0700351 int size = tmpListeners.size();
352 for (int i = 0; i < size; i++) {
353 tmpListeners.get(i).onAnimationEnd(this);
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800354 }
355 }
Chet Haase8b699792011-08-05 15:20:19 -0700356 mStarted = false;
Chet Haase17fb4b02010-06-28 17:55:07 -0700357 }
358 }
359
360 /**
361 * {@inheritDoc}
362 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700363 * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is
Chet Haase17fb4b02010-06-28 17:55:07 -0700364 * responsible for.</p>
365 */
366 @Override
367 public void end() {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700368 mTerminated = true;
Chet Haase8b699792011-08-05 15:20:19 -0700369 if (isStarted()) {
Doris Liu13099142015-07-10 17:32:41 -0700370 endRemainingAnimations();
371 }
372 if (mListeners != null) {
373 ArrayList<AnimatorListener> tmpListeners =
374 (ArrayList<AnimatorListener>) mListeners.clone();
375 for (int i = 0; i < tmpListeners.size(); i++) {
376 tmpListeners.get(i).onAnimationEnd(this);
377 }
378 }
379 mStarted = false;
380 }
381
382 /**
383 * Iterate the animations that haven't finished or haven't started, and end them.
384 */
385 private void endRemainingAnimations() {
386 ArrayList<Animator> remainingList = new ArrayList<Animator>(mNodes.size());
387 remainingList.addAll(mPlayingSet);
388
389 int index = 0;
390 while (index < remainingList.size()) {
391 Animator anim = remainingList.get(index);
392 anim.end();
393 index++;
394 Node node = mNodeMap.get(anim);
395 if (node.mChildNodes != null) {
396 int childSize = node.mChildNodes.size();
397 for (int i = 0; i < childSize; i++) {
398 Node child = node.mChildNodes.get(i);
399 if (child.mLatestParent != node) {
400 continue;
Chet Haase7dfacdb2011-07-11 17:01:56 -0700401 }
Doris Liu13099142015-07-10 17:32:41 -0700402 remainingList.add(child.mAnimation);
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700403 }
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700404 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700405 }
406 }
407
Doris Liu13099142015-07-10 17:32:41 -0700408
Chet Haase17fb4b02010-06-28 17:55:07 -0700409 /**
Chet Haase8b699792011-08-05 15:20:19 -0700410 * Returns true if any of the child animations of this AnimatorSet have been started and have
Doris Liuee684552015-08-28 13:21:03 -0700411 * not yet ended. Child animations will not be started until the AnimatorSet has gone past
412 * its initial delay set through {@link #setStartDelay(long)}.
413 *
414 * @return Whether this AnimatorSet has gone past the initial delay, and at least one child
415 * animation has been started and not yet ended.
Chet Haase673e42f2010-08-25 16:32:37 -0700416 */
417 @Override
418 public boolean isRunning() {
Doris Liu13099142015-07-10 17:32:41 -0700419 int size = mNodes.size();
420 for (int i = 0; i < size; i++) {
Doris Liu458f20e2015-08-10 17:32:54 -0700421 Node node = mNodes.get(i);
Doris Liuee684552015-08-28 13:21:03 -0700422 if (node != mRootNode && node.mAnimation.isStarted()) {
Chet Haase673e42f2010-08-25 16:32:37 -0700423 return true;
424 }
425 }
Chet Haase8b699792011-08-05 15:20:19 -0700426 return false;
427 }
428
429 @Override
430 public boolean isStarted() {
431 return mStarted;
Chet Haase673e42f2010-08-25 16:32:37 -0700432 }
433
434 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700435 * The amount of time, in milliseconds, to delay starting the animation after
436 * {@link #start()} is called.
437 *
438 * @return the number of milliseconds to delay running the animation
439 */
440 @Override
441 public long getStartDelay() {
442 return mStartDelay;
443 }
444
445 /**
446 * The amount of time, in milliseconds, to delay starting the animation after
447 * {@link #start()} is called.
448
449 * @param startDelay The amount of the delay, in milliseconds
450 */
451 @Override
452 public void setStartDelay(long startDelay) {
ztenghui7bc6a3f2014-07-15 15:12:12 -0700453 if (mStartDelay > 0) {
454 mReversible = false;
455 }
Doris Liu13099142015-07-10 17:32:41 -0700456 long delta = startDelay - mStartDelay;
Doris Liu49db4242015-08-10 13:33:06 -0700457 if (delta == 0) {
458 return;
459 }
Chet Haase21cd1382010-09-01 17:42:29 -0700460 mStartDelay = startDelay;
Doris Liu13099142015-07-10 17:32:41 -0700461 if (!mDependencyDirty) {
462 // Dependency graph already constructed, update all the nodes' start/end time
463 int size = mNodes.size();
464 for (int i = 0; i < size; i++) {
465 Node node = mNodes.get(i);
466 if (node == mRootNode) {
467 node.mEndTime = mStartDelay;
468 } else {
469 node.mStartTime = node.mStartTime == DURATION_INFINITE ?
470 DURATION_INFINITE : node.mStartTime + delta;
471 node.mEndTime = node.mEndTime == DURATION_INFINITE ?
472 DURATION_INFINITE : node.mEndTime + delta;
Doris Liu13099142015-07-10 17:32:41 -0700473 }
474 }
Doris Liu49db4242015-08-10 13:33:06 -0700475 // Update total duration, if necessary.
476 if (mTotalDuration != DURATION_INFINITE) {
477 mTotalDuration += delta;
478 }
Doris Liu13099142015-07-10 17:32:41 -0700479 }
Chet Haase21cd1382010-09-01 17:42:29 -0700480 }
481
482 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700483 * Gets the length of each of the child animations of this AnimatorSet. This value may
484 * be less than 0, which indicates that no duration has been set on this AnimatorSet
Chet Haase21cd1382010-09-01 17:42:29 -0700485 * and each of the child animations will use their own duration.
486 *
487 * @return The length of the animation, in milliseconds, of each of the child
Chet Haasea18a86b2010-09-07 13:20:00 -0700488 * animations of this AnimatorSet.
Chet Haase21cd1382010-09-01 17:42:29 -0700489 */
490 @Override
491 public long getDuration() {
492 return mDuration;
493 }
494
495 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700496 * Sets the length of each of the current child animations of this AnimatorSet. By default,
497 * each child animation will use its own duration. If the duration is set on the AnimatorSet,
Chet Haase21cd1382010-09-01 17:42:29 -0700498 * then each child animation inherits this duration.
499 *
500 * @param duration The length of the animation, in milliseconds, of each of the child
Chet Haasea18a86b2010-09-07 13:20:00 -0700501 * animations of this AnimatorSet.
Chet Haase21cd1382010-09-01 17:42:29 -0700502 */
503 @Override
Chet Haase2794eb32010-10-12 16:29:28 -0700504 public AnimatorSet setDuration(long duration) {
Chet Haase21cd1382010-09-01 17:42:29 -0700505 if (duration < 0) {
506 throw new IllegalArgumentException("duration must be a value of zero or greater");
507 }
Doris Liu13099142015-07-10 17:32:41 -0700508 mDependencyDirty = true;
Chet Haasec299a332012-04-12 07:51:50 -0700509 // Just record the value for now - it will be used later when the AnimatorSet starts
Chet Haase21cd1382010-09-01 17:42:29 -0700510 mDuration = duration;
Chet Haase2794eb32010-10-12 16:29:28 -0700511 return this;
Chet Haase21cd1382010-09-01 17:42:29 -0700512 }
513
Chet Haase2970c492010-11-09 13:58:04 -0800514 @Override
515 public void setupStartValues() {
Doris Liu13099142015-07-10 17:32:41 -0700516 int size = mNodes.size();
517 for (int i = 0; i < size; i++) {
518 Node node = mNodes.get(i);
Doris Liu458f20e2015-08-10 17:32:54 -0700519 if (node != mRootNode) {
520 node.mAnimation.setupStartValues();
521 }
Chet Haase2970c492010-11-09 13:58:04 -0800522 }
523 }
524
525 @Override
526 public void setupEndValues() {
Doris Liu13099142015-07-10 17:32:41 -0700527 int size = mNodes.size();
528 for (int i = 0; i < size; i++) {
529 Node node = mNodes.get(i);
Doris Liu458f20e2015-08-10 17:32:54 -0700530 if (node != mRootNode) {
531 node.mAnimation.setupEndValues();
532 }
Chet Haase2970c492010-11-09 13:58:04 -0800533 }
534 }
535
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700536 @Override
537 public void pause() {
538 boolean previouslyPaused = mPaused;
539 super.pause();
540 if (!previouslyPaused && mPaused) {
541 if (mDelayAnim != null) {
542 mDelayAnim.pause();
543 } else {
Doris Liu13099142015-07-10 17:32:41 -0700544 int size = mNodes.size();
545 for (int i = 0; i < size; i++) {
546 Node node = mNodes.get(i);
Doris Liu458f20e2015-08-10 17:32:54 -0700547 if (node != mRootNode) {
548 node.mAnimation.pause();
549 }
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700550 }
551 }
552 }
553 }
554
555 @Override
556 public void resume() {
557 boolean previouslyPaused = mPaused;
558 super.resume();
559 if (previouslyPaused && !mPaused) {
560 if (mDelayAnim != null) {
561 mDelayAnim.resume();
562 } else {
Doris Liu13099142015-07-10 17:32:41 -0700563 int size = mNodes.size();
564 for (int i = 0; i < size; i++) {
565 Node node = mNodes.get(i);
Doris Liu458f20e2015-08-10 17:32:54 -0700566 if (node != mRootNode) {
567 node.mAnimation.resume();
568 }
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700569 }
570 }
571 }
572 }
573
Chet Haase21cd1382010-09-01 17:42:29 -0700574 /**
Chet Haase17fb4b02010-06-28 17:55:07 -0700575 * {@inheritDoc}
576 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700577 * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which
Chet Haase17fb4b02010-06-28 17:55:07 -0700578 * it is responsible. The details of when exactly those animations are started depends on
579 * the dependency relationships that have been set up between the animations.
580 */
581 @SuppressWarnings("unchecked")
582 @Override
583 public void start() {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700584 mTerminated = false;
Chet Haase8b699792011-08-05 15:20:19 -0700585 mStarted = true;
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700586 mPaused = false;
Chet Haase010dbaa2010-07-19 17:29:49 -0700587
Doris Liu13099142015-07-10 17:32:41 -0700588 int size = mNodes.size();
589 for (int i = 0; i < size; i++) {
590 Node node = mNodes.get(i);
591 node.mEnded = false;
592 node.mAnimation.setAllowRunningAsynchronously(false);
John Reckf5945a02014-09-05 15:57:47 -0700593 }
594
Chet Haase430742f2013-04-12 11:18:36 -0700595 if (mInterpolator != null) {
Doris Liu13099142015-07-10 17:32:41 -0700596 for (int i = 0; i < size; i++) {
597 Node node = mNodes.get(i);
598 node.mAnimation.setInterpolator(mInterpolator);
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800599 }
600 }
601
Doris Liu13099142015-07-10 17:32:41 -0700602 updateAnimatorsDuration();
603 createDependencyGraph();
604
Chet Haase17fb4b02010-06-28 17:55:07 -0700605 // Now that all dependencies are set up, start the animations that should be started.
Doris Liuf57bfe22015-10-01 13:26:01 -0700606 boolean setIsEmpty = false;
607 if (mStartDelay > 0) {
608 start(mRootNode);
609 } else if (mNodes.size() > 1) {
610 // No delay, but there are other animators in the set
611 onChildAnimatorEnded(mDelayAnim);
612 } else {
613 // Set is empty, no delay, no other animation. Skip to end in this case
614 setIsEmpty = true;
615 }
616
Chet Haase17fb4b02010-06-28 17:55:07 -0700617 if (mListeners != null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700618 ArrayList<AnimatorListener> tmpListeners =
619 (ArrayList<AnimatorListener>) mListeners.clone();
Chet Haase7c608f22010-10-22 17:54:04 -0700620 int numListeners = tmpListeners.size();
621 for (int i = 0; i < numListeners; ++i) {
622 tmpListeners.get(i).onAnimationStart(this);
Chet Haase8b699792011-08-05 15:20:19 -0700623 }
624 }
Doris Liuf57bfe22015-10-01 13:26:01 -0700625 if (setIsEmpty) {
626 // In the case of empty AnimatorSet, we will trigger the onAnimationEnd() right away.
627 onChildAnimatorEnded(mDelayAnim);
Chet Haase17fb4b02010-06-28 17:55:07 -0700628 }
629 }
630
Doris Liu13099142015-07-10 17:32:41 -0700631 private void updateAnimatorsDuration() {
632 if (mDuration >= 0) {
633 // If the duration was set on this AnimatorSet, pass it along to all child animations
634 int size = mNodes.size();
635 for (int i = 0; i < size; i++) {
636 Node node = mNodes.get(i);
637 // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
638 // insert "play-after" delays
639 node.mAnimation.setDuration(mDuration);
640 }
641 }
642 mDelayAnim.setDuration(mStartDelay);
643 }
644
645 void start(final Node node) {
646 final Animator anim = node.mAnimation;
647 mPlayingSet.add(anim);
648 anim.addListener(mSetListener);
649 anim.start();
650 }
651
Chet Haase49afa5b2010-08-23 11:39:53 -0700652 @Override
Chet Haasea18a86b2010-09-07 13:20:00 -0700653 public AnimatorSet clone() {
654 final AnimatorSet anim = (AnimatorSet) super.clone();
Chet Haase49afa5b2010-08-23 11:39:53 -0700655 /*
656 * The basic clone() operation copies all items. This doesn't work very well for
Chet Haasea18a86b2010-09-07 13:20:00 -0700657 * AnimatorSet, because it will copy references that need to be recreated and state
Chet Haase49afa5b2010-08-23 11:39:53 -0700658 * that may not apply. What we need to do now is put the clone in an uninitialized
659 * state, with fresh, empty data structures. Then we will build up the nodes list
660 * manually, as we clone each Node (and its animation). The clone will then be sorted,
661 * and will populate any appropriate lists, when it is started.
662 */
Yigit Boyard422dc32014-09-25 12:23:35 -0700663 final int nodeCount = mNodes.size();
Chet Haase7dfacdb2011-07-11 17:01:56 -0700664 anim.mTerminated = false;
Chet Haase8b699792011-08-05 15:20:19 -0700665 anim.mStarted = false;
Chet Haasea18a86b2010-09-07 13:20:00 -0700666 anim.mPlayingSet = new ArrayList<Animator>();
Doris Liud7444422015-05-11 13:23:31 -0700667 anim.mNodeMap = new ArrayMap<Animator, Node>();
Yigit Boyard422dc32014-09-25 12:23:35 -0700668 anim.mNodes = new ArrayList<Node>(nodeCount);
ztenghui7bc6a3f2014-07-15 15:12:12 -0700669 anim.mReversible = mReversible;
Doris Liu13099142015-07-10 17:32:41 -0700670 anim.mSetListener = new AnimatorSetListener(anim);
Chet Haase49afa5b2010-08-23 11:39:53 -0700671
672 // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
Chet Haasea18a86b2010-09-07 13:20:00 -0700673 // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
Chet Haase49afa5b2010-08-23 11:39:53 -0700674 // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
Yigit Boyard422dc32014-09-25 12:23:35 -0700675
676 for (int n = 0; n < nodeCount; n++) {
677 final Node node = mNodes.get(n);
Chet Haase49afa5b2010-08-23 11:39:53 -0700678 Node nodeClone = node.clone();
Yigit Boyard422dc32014-09-25 12:23:35 -0700679 node.mTmpClone = nodeClone;
Chet Haase49afa5b2010-08-23 11:39:53 -0700680 anim.mNodes.add(nodeClone);
Doris Liu13099142015-07-10 17:32:41 -0700681 anim.mNodeMap.put(nodeClone.mAnimation, nodeClone);
Yigit Boyard422dc32014-09-25 12:23:35 -0700682
Doris Liu13099142015-07-10 17:32:41 -0700683 // clear out any listeners that were set up by the AnimatorSet
684 final ArrayList<AnimatorListener> cloneListeners = nodeClone.mAnimation.getListeners();
Chet Haase49afa5b2010-08-23 11:39:53 -0700685 if (cloneListeners != null) {
Yigit Boyard422dc32014-09-25 12:23:35 -0700686 for (int i = cloneListeners.size() - 1; i >= 0; i--) {
687 final AnimatorListener listener = cloneListeners.get(i);
Chet Haasea18a86b2010-09-07 13:20:00 -0700688 if (listener instanceof AnimatorSetListener) {
Yigit Boyard422dc32014-09-25 12:23:35 -0700689 cloneListeners.remove(i);
Chet Haase49afa5b2010-08-23 11:39:53 -0700690 }
691 }
692 }
693 }
Doris Liu13099142015-07-10 17:32:41 -0700694
695 anim.mRootNode = mRootNode.mTmpClone;
696 anim.mDelayAnim = (ValueAnimator) anim.mRootNode.mAnimation;
697
Chet Haase49afa5b2010-08-23 11:39:53 -0700698 // Now that we've cloned all of the nodes, we're ready to walk through their
699 // dependencies, mapping the old dependencies to the new nodes
Doris Liu13099142015-07-10 17:32:41 -0700700 for (int i = 0; i < nodeCount; i++) {
701 Node node = mNodes.get(i);
702 // Update dependencies for node's clone
703 node.mTmpClone.mLatestParent = node.mLatestParent == null ?
704 null : node.mLatestParent.mTmpClone;
705 int size = node.mChildNodes == null ? 0 : node.mChildNodes.size();
706 for (int j = 0; j < size; j++) {
707 node.mTmpClone.mChildNodes.set(j, node.mChildNodes.get(j).mTmpClone);
Yigit Boyard422dc32014-09-25 12:23:35 -0700708 }
Doris Liu13099142015-07-10 17:32:41 -0700709 size = node.mSiblings == null ? 0 : node.mSiblings.size();
710 for (int j = 0; j < size; j++) {
711 node.mTmpClone.mSiblings.set(j, node.mSiblings.get(j).mTmpClone);
Yigit Boyard422dc32014-09-25 12:23:35 -0700712 }
Doris Liu13099142015-07-10 17:32:41 -0700713 size = node.mParents == null ? 0 : node.mParents.size();
714 for (int j = 0; j < size; j++) {
715 node.mTmpClone.mParents.set(j, node.mParents.get(j).mTmpClone);
Chet Haase49afa5b2010-08-23 11:39:53 -0700716 }
717 }
Doris Liu13099142015-07-10 17:32:41 -0700718
Yigit Boyard422dc32014-09-25 12:23:35 -0700719 for (int n = 0; n < nodeCount; n++) {
720 mNodes.get(n).mTmpClone = null;
721 }
Chet Haase49afa5b2010-08-23 11:39:53 -0700722 return anim;
723 }
724
Chet Haase17fb4b02010-06-28 17:55:07 -0700725
Doris Liu6f2276b2015-08-06 13:51:31 -0700726 private static class AnimatorSetListener implements AnimatorListener {
Chet Haase17fb4b02010-06-28 17:55:07 -0700727
Chet Haasea18a86b2010-09-07 13:20:00 -0700728 private AnimatorSet mAnimatorSet;
Chet Haase17fb4b02010-06-28 17:55:07 -0700729
Chet Haasea18a86b2010-09-07 13:20:00 -0700730 AnimatorSetListener(AnimatorSet animatorSet) {
731 mAnimatorSet = animatorSet;
Chet Haase17fb4b02010-06-28 17:55:07 -0700732 }
733
Chet Haasea18a86b2010-09-07 13:20:00 -0700734 public void onAnimationCancel(Animator animation) {
Doris Liu13099142015-07-10 17:32:41 -0700735
Doris Liu6f2276b2015-08-06 13:51:31 -0700736 if (!mAnimatorSet.mTerminated) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700737 // Listeners are already notified of the AnimatorSet canceling in cancel().
738 // The logic below only kicks in when animations end normally
Doris Liu13099142015-07-10 17:32:41 -0700739 if (mAnimatorSet.mPlayingSet.size() == 0) {
740 ArrayList<AnimatorListener> listeners = mAnimatorSet.mListeners;
741 if (listeners != null) {
742 int numListeners = listeners.size();
Chet Haase7dfacdb2011-07-11 17:01:56 -0700743 for (int i = 0; i < numListeners; ++i) {
Doris Liu13099142015-07-10 17:32:41 -0700744 listeners.get(i).onAnimationCancel(mAnimatorSet);
Chet Haase7dfacdb2011-07-11 17:01:56 -0700745 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700746 }
747 }
748 }
749 }
750
751 @SuppressWarnings("unchecked")
Chet Haasea18a86b2010-09-07 13:20:00 -0700752 public void onAnimationEnd(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700753 animation.removeListener(this);
Doris Liu13099142015-07-10 17:32:41 -0700754 mAnimatorSet.mPlayingSet.remove(animation);
Doris Liuf57bfe22015-10-01 13:26:01 -0700755 mAnimatorSet.onChildAnimatorEnded(animation);
Chet Haase17fb4b02010-06-28 17:55:07 -0700756 }
757
758 // Nothing to do
Chet Haasea18a86b2010-09-07 13:20:00 -0700759 public void onAnimationRepeat(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700760 }
761
762 // Nothing to do
Chet Haasea18a86b2010-09-07 13:20:00 -0700763 public void onAnimationStart(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700764 }
765
766 }
767
Doris Liuf57bfe22015-10-01 13:26:01 -0700768 private void onChildAnimatorEnded(Animator animation) {
769 Node animNode = mNodeMap.get(animation);
770 animNode.mEnded = true;
771
772 if (!mTerminated) {
773 List<Node> children = animNode.mChildNodes;
774 // Start children animations, if any.
775 int childrenSize = children == null ? 0 : children.size();
776 for (int i = 0; i < childrenSize; i++) {
777 if (children.get(i).mLatestParent == animNode) {
778 start(children.get(i));
779 }
780 }
781 // Listeners are already notified of the AnimatorSet ending in cancel() or
782 // end(); the logic below only kicks in when animations end normally
783 boolean allDone = true;
784 // Traverse the tree and find if there's any unfinished node
785 int size = mNodes.size();
786 for (int i = 0; i < size; i++) {
787 if (!mNodes.get(i).mEnded) {
788 allDone = false;
789 break;
790 }
791 }
792 if (allDone) {
793 // If this was the last child animation to end, then notify listeners that this
794 // AnimatorSet has ended
795 if (mListeners != null) {
796 ArrayList<AnimatorListener> tmpListeners =
797 (ArrayList<AnimatorListener>) mListeners.clone();
798 int numListeners = tmpListeners.size();
799 for (int i = 0; i < numListeners; ++i) {
800 tmpListeners.get(i).onAnimationEnd(this);
801 }
802 }
803 mStarted = false;
804 mPaused = false;
805 }
806 }
807 }
808
Chet Haase17fb4b02010-06-28 17:55:07 -0700809 /**
Doris Liuc4bb1852016-02-19 21:39:21 +0000810 * AnimatorSet is only reversible when the set contains no sequential animation, and no child
811 * animators have a start delay.
ztenghui7bc6a3f2014-07-15 15:12:12 -0700812 * @hide
813 */
814 @Override
815 public boolean canReverse() {
816 if (!mReversible) {
817 return false;
818 }
819 // Loop to make sure all the Nodes can reverse.
Doris Liu13099142015-07-10 17:32:41 -0700820 int size = mNodes.size();
821 for (int i = 0; i < size; i++) {
822 Node node = mNodes.get(i);
823 if (!node.mAnimation.canReverse() || node.mAnimation.getStartDelay() > 0) {
ztenghui7bc6a3f2014-07-15 15:12:12 -0700824 return false;
825 }
826 }
827 return true;
828 }
829
830 /**
831 * @hide
832 */
833 @Override
834 public void reverse() {
835 if (canReverse()) {
Doris Liu13099142015-07-10 17:32:41 -0700836 int size = mNodes.size();
837 for (int i = 0; i < size; i++) {
838 Node node = mNodes.get(i);
839 node.mAnimation.reverse();
ztenghui7bc6a3f2014-07-15 15:12:12 -0700840 }
841 }
842 }
843
Chet Haased4307532014-12-01 06:32:38 -0800844 @Override
845 public String toString() {
846 String returnVal = "AnimatorSet@" + Integer.toHexString(hashCode()) + "{";
Doris Liu13099142015-07-10 17:32:41 -0700847 int size = mNodes.size();
848 for (int i = 0; i < size; i++) {
849 Node node = mNodes.get(i);
850 returnVal += "\n " + node.mAnimation.toString();
Chet Haased4307532014-12-01 06:32:38 -0800851 }
852 return returnVal + "\n}";
853 }
854
Doris Liu13099142015-07-10 17:32:41 -0700855 private void printChildCount() {
856 // Print out the child count through a level traverse.
857 ArrayList<Node> list = new ArrayList<>(mNodes.size());
858 list.add(mRootNode);
859 Log.d(TAG, "Current tree: ");
860 int index = 0;
861 while (index < list.size()) {
862 int listSize = list.size();
863 StringBuilder builder = new StringBuilder();
864 for (; index < listSize; index++) {
865 Node node = list.get(index);
866 int num = 0;
867 if (node.mChildNodes != null) {
868 for (int i = 0; i < node.mChildNodes.size(); i++) {
869 Node child = node.mChildNodes.get(i);
870 if (child.mLatestParent == node) {
871 num++;
872 list.add(child);
873 }
874 }
875 }
876 builder.append(" ");
877 builder.append(num);
878 }
879 Log.d(TAG, builder.toString());
Chet Haase17fb4b02010-06-28 17:55:07 -0700880 }
881 }
882
Doris Liu13099142015-07-10 17:32:41 -0700883 private void createDependencyGraph() {
884 if (!mDependencyDirty) {
Doris Liu49db4242015-08-10 13:33:06 -0700885 // Check whether any duration of the child animations has changed
886 boolean durationChanged = false;
887 for (int i = 0; i < mNodes.size(); i++) {
888 Animator anim = mNodes.get(i).mAnimation;
889 if (mNodes.get(i).mTotalDuration != anim.getTotalDuration()) {
890 durationChanged = true;
891 break;
892 }
893 }
894 if (!durationChanged) {
895 return;
896 }
Doris Liu13099142015-07-10 17:32:41 -0700897 }
898
Doris Liu13099142015-07-10 17:32:41 -0700899 mDependencyDirty = false;
900 // Traverse all the siblings and make sure they have all the parents
901 int size = mNodes.size();
902 for (int i = 0; i < size; i++) {
903 mNodes.get(i).mParentsAdded = false;
904 }
905 for (int i = 0; i < size; i++) {
906 Node node = mNodes.get(i);
907 if (node.mParentsAdded) {
908 continue;
909 }
910
911 node.mParentsAdded = true;
912 if (node.mSiblings == null) {
913 continue;
914 }
915
916 // Find all the siblings
917 findSiblings(node, node.mSiblings);
918 node.mSiblings.remove(node);
919
920 // Get parents from all siblings
921 int siblingSize = node.mSiblings.size();
922 for (int j = 0; j < siblingSize; j++) {
923 node.addParents(node.mSiblings.get(j).mParents);
924 }
925
926 // Now make sure all siblings share the same set of parents
927 for (int j = 0; j < siblingSize; j++) {
928 Node sibling = node.mSiblings.get(j);
929 sibling.addParents(node.mParents);
930 sibling.mParentsAdded = true;
931 }
932 }
933
934 for (int i = 0; i < size; i++) {
935 Node node = mNodes.get(i);
936 if (node != mRootNode && node.mParents == null) {
937 node.addParent(mRootNode);
938 }
939 }
940
941 // Do a DFS on the tree
942 ArrayList<Node> visited = new ArrayList<Node>(mNodes.size());
943 // Assign start/end time
944 mRootNode.mStartTime = 0;
945 mRootNode.mEndTime = mDelayAnim.getDuration();
946 updatePlayTime(mRootNode, visited);
947
948 long maxEndTime = 0;
949 for (int i = 0; i < size; i++) {
950 Node node = mNodes.get(i);
Doris Liu49db4242015-08-10 13:33:06 -0700951 node.mTotalDuration = node.mAnimation.getTotalDuration();
Doris Liu13099142015-07-10 17:32:41 -0700952 if (node.mEndTime == DURATION_INFINITE) {
953 maxEndTime = DURATION_INFINITE;
954 break;
955 } else {
956 maxEndTime = node.mEndTime > maxEndTime ? node.mEndTime : maxEndTime;
957 }
958 }
959 mTotalDuration = maxEndTime;
960 }
961
962 /**
963 * Based on parent's start/end time, calculate children's start/end time. If cycle exists in
964 * the graph, all the nodes on the cycle will be marked to start at {@link #DURATION_INFINITE},
965 * meaning they will ever play.
966 */
967 private void updatePlayTime(Node parent, ArrayList<Node> visited) {
968 if (parent.mChildNodes == null) {
Doris Liu49db4242015-08-10 13:33:06 -0700969 if (parent == mRootNode) {
970 // All the animators are in a cycle
971 for (int i = 0; i < mNodes.size(); i++) {
972 Node node = mNodes.get(i);
973 if (node != mRootNode) {
974 node.mStartTime = DURATION_INFINITE;
975 node.mEndTime = DURATION_INFINITE;
976 }
977 }
978 }
Doris Liu13099142015-07-10 17:32:41 -0700979 return;
980 }
981
982 visited.add(parent);
983 int childrenSize = parent.mChildNodes.size();
984 for (int i = 0; i < childrenSize; i++) {
985 Node child = parent.mChildNodes.get(i);
986 int index = visited.indexOf(child);
987 if (index >= 0) {
988 // Child has been visited, cycle found. Mark all the nodes in the cycle.
Doris Liu49db4242015-08-10 13:33:06 -0700989 for (int j = index; j < visited.size(); j++) {
Doris Liu13099142015-07-10 17:32:41 -0700990 visited.get(j).mLatestParent = null;
991 visited.get(j).mStartTime = DURATION_INFINITE;
992 visited.get(j).mEndTime = DURATION_INFINITE;
993 }
994 child.mStartTime = DURATION_INFINITE;
995 child.mEndTime = DURATION_INFINITE;
996 child.mLatestParent = null;
997 Log.w(TAG, "Cycle found in AnimatorSet: " + this);
998 continue;
999 }
1000
1001 if (child.mStartTime != DURATION_INFINITE) {
1002 if (parent.mEndTime == DURATION_INFINITE) {
1003 child.mLatestParent = parent;
1004 child.mStartTime = DURATION_INFINITE;
1005 child.mEndTime = DURATION_INFINITE;
1006 } else {
1007 if (parent.mEndTime >= child.mStartTime) {
1008 child.mLatestParent = parent;
1009 child.mStartTime = parent.mEndTime;
1010 }
1011
1012 long duration = child.mAnimation.getTotalDuration();
1013 child.mEndTime = duration == DURATION_INFINITE ?
1014 DURATION_INFINITE : child.mStartTime + duration;
1015 }
1016 }
1017 updatePlayTime(child, visited);
1018 }
1019 visited.remove(parent);
1020 }
1021
1022 // Recursively find all the siblings
1023 private void findSiblings(Node node, ArrayList<Node> siblings) {
1024 if (!siblings.contains(node)) {
1025 siblings.add(node);
1026 if (node.mSiblings == null) {
1027 return;
1028 }
1029 for (int i = 0; i < node.mSiblings.size(); i++) {
1030 findSiblings(node.mSiblings.get(i), siblings);
1031 }
1032 }
1033 }
1034
Doris Liu766431a2016-02-04 22:17:11 +00001035 /**
1036 * @hide
1037 * TODO: For animatorSet defined in XML, we can use a flag to indicate what the play order
1038 * if defined (i.e. sequential or together), then we can use the flag instead of calculate
1039 * dynamically.
1040 * @return whether all the animators in the set are supposed to play together
1041 */
1042 public boolean shouldPlayTogether() {
1043 updateAnimatorsDuration();
1044 createDependencyGraph();
1045 // All the child nodes are set out to play right after the delay animation
1046 return mRootNode.mChildNodes.size() == mNodes.size() - 1;
1047 }
1048
Doris Liu13099142015-07-10 17:32:41 -07001049 @Override
1050 public long getTotalDuration() {
1051 updateAnimatorsDuration();
1052 createDependencyGraph();
1053 return mTotalDuration;
1054 }
1055
1056 private Node getNodeForAnimation(Animator anim) {
1057 Node node = mNodeMap.get(anim);
1058 if (node == null) {
1059 node = new Node(anim);
1060 mNodeMap.put(anim, node);
1061 mNodes.add(node);
1062 }
1063 return node;
1064 }
1065
Chet Haase17fb4b02010-06-28 17:55:07 -07001066 /**
Chet Haasea18a86b2010-09-07 13:20:00 -07001067 * A Node is an embodiment of both the Animator that it wraps as well as
Chet Haase17fb4b02010-06-28 17:55:07 -07001068 * any dependencies that are associated with that Animation. This includes
1069 * both dependencies upon other nodes (in the dependencies list) as
1070 * well as dependencies of other nodes upon this (in the nodeDependents list).
1071 */
Chet Haase49afa5b2010-08-23 11:39:53 -07001072 private static class Node implements Cloneable {
Doris Liu13099142015-07-10 17:32:41 -07001073 Animator mAnimation;
Chet Haase17fb4b02010-06-28 17:55:07 -07001074
1075 /**
Doris Liu13099142015-07-10 17:32:41 -07001076 * Child nodes are the nodes associated with animations that will be played immediately
1077 * after current node.
Chet Haase17fb4b02010-06-28 17:55:07 -07001078 */
Doris Liu13099142015-07-10 17:32:41 -07001079 ArrayList<Node> mChildNodes = null;
Chet Haase1e0ac5a2010-08-27 08:32:11 -07001080
1081 /**
Yigit Boyard422dc32014-09-25 12:23:35 -07001082 * Temporary field to hold the clone in AnimatorSet#clone. Cleaned after clone is complete
1083 */
1084 private Node mTmpClone = null;
1085
1086 /**
Doris Liu13099142015-07-10 17:32:41 -07001087 * Flag indicating whether the animation in this node is finished. This flag
1088 * is used by AnimatorSet to check, as each animation ends, whether all child animations
1089 * are mEnded and it's time to send out an end event for the entire AnimatorSet.
1090 */
1091 boolean mEnded = false;
1092
1093 /**
1094 * Nodes with animations that are defined to play simultaneously with the animation
1095 * associated with this current node.
1096 */
1097 ArrayList<Node> mSiblings;
1098
1099 /**
1100 * Parent nodes are the nodes with animations preceding current node's animation. Parent
1101 * nodes here are derived from user defined animation sequence.
1102 */
1103 ArrayList<Node> mParents;
1104
1105 /**
1106 * Latest parent is the parent node associated with a animation that finishes after all
1107 * the other parents' animations.
1108 */
1109 Node mLatestParent = null;
1110
1111 boolean mParentsAdded = false;
1112 long mStartTime = 0;
1113 long mEndTime = 0;
Doris Liu49db4242015-08-10 13:33:06 -07001114 long mTotalDuration = 0;
Doris Liu13099142015-07-10 17:32:41 -07001115
1116 /**
Chet Haase17fb4b02010-06-28 17:55:07 -07001117 * Constructs the Node with the animation that it encapsulates. A Node has no
1118 * dependencies by default; dependencies are added via the addDependency()
1119 * method.
1120 *
1121 * @param animation The animation that the Node encapsulates.
1122 */
Chet Haasea18a86b2010-09-07 13:20:00 -07001123 public Node(Animator animation) {
Doris Liu13099142015-07-10 17:32:41 -07001124 this.mAnimation = animation;
Chet Haase17fb4b02010-06-28 17:55:07 -07001125 }
Chet Haase49afa5b2010-08-23 11:39:53 -07001126
1127 @Override
Chet Haase21cd1382010-09-01 17:42:29 -07001128 public Node clone() {
1129 try {
1130 Node node = (Node) super.clone();
Doris Liu13099142015-07-10 17:32:41 -07001131 node.mAnimation = mAnimation.clone();
1132 if (mChildNodes != null) {
1133 node.mChildNodes = new ArrayList<>(mChildNodes);
1134 }
Doris Liu5e666162015-08-03 16:06:56 -07001135 if (mSiblings != null) {
1136 node.mSiblings = new ArrayList<>(mSiblings);
1137 }
1138 if (mParents != null) {
1139 node.mParents = new ArrayList<>(mParents);
1140 }
Doris Liu13099142015-07-10 17:32:41 -07001141 node.mEnded = false;
Chet Haase21cd1382010-09-01 17:42:29 -07001142 return node;
1143 } catch (CloneNotSupportedException e) {
1144 throw new AssertionError();
1145 }
Chet Haase49afa5b2010-08-23 11:39:53 -07001146 }
Doris Liu13099142015-07-10 17:32:41 -07001147
1148 void addChild(Node node) {
1149 if (mChildNodes == null) {
1150 mChildNodes = new ArrayList<>();
1151 }
1152 if (!mChildNodes.contains(node)) {
1153 mChildNodes.add(node);
1154 node.addParent(this);
1155 }
1156 }
1157
1158 public void addSibling(Node node) {
1159 if (mSiblings == null) {
1160 mSiblings = new ArrayList<Node>();
1161 }
1162 if (!mSiblings.contains(node)) {
1163 mSiblings.add(node);
1164 node.addSibling(this);
1165 }
1166 }
1167
1168 public void addParent(Node node) {
1169 if (mParents == null) {
1170 mParents = new ArrayList<Node>();
1171 }
1172 if (!mParents.contains(node)) {
1173 mParents.add(node);
1174 node.addChild(this);
1175 }
1176 }
1177
1178 public void addParents(ArrayList<Node> parents) {
1179 if (parents == null) {
1180 return;
1181 }
1182 int size = parents.size();
1183 for (int i = 0; i < size; i++) {
1184 addParent(parents.get(i));
1185 }
1186 }
Chet Haase17fb4b02010-06-28 17:55:07 -07001187 }
1188
1189 /**
1190 * The <code>Builder</code> object is a utility class to facilitate adding animations to a
Chet Haasea18a86b2010-09-07 13:20:00 -07001191 * <code>AnimatorSet</code> along with the relationships between the various animations. The
Chet Haase17fb4b02010-06-28 17:55:07 -07001192 * intention of the <code>Builder</code> methods, along with the {@link
Chet Haase8b699792011-08-05 15:20:19 -07001193 * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible
1194 * to express the dependency relationships of animations in a natural way. Developers can also
1195 * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
Chet Haasea18a86b2010-09-07 13:20:00 -07001196 * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
1197 * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
Chet Haase17fb4b02010-06-28 17:55:07 -07001198 * <p/>
1199 * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
Chet Haasea18a86b2010-09-07 13:20:00 -07001200 * internally via a call to {@link AnimatorSet#play(Animator)}.</p>
Chet Haase17fb4b02010-06-28 17:55:07 -07001201 * <p/>
Chet Haasea18a86b2010-09-07 13:20:00 -07001202 * <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 -07001203 * play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
1204 * <pre>
Chet Haasea18a86b2010-09-07 13:20:00 -07001205 * AnimatorSet s = new AnimatorSet();
Chet Haase17fb4b02010-06-28 17:55:07 -07001206 * s.play(anim1).with(anim2);
1207 * s.play(anim2).before(anim3);
1208 * s.play(anim4).after(anim3);
1209 * </pre>
1210 * <p/>
Chet Haasea18a86b2010-09-07 13:20:00 -07001211 * <p>Note in the example that both {@link Builder#before(Animator)} and {@link
1212 * Builder#after(Animator)} are used. These are just different ways of expressing the same
Chet Haase17fb4b02010-06-28 17:55:07 -07001213 * relationship and are provided to make it easier to say things in a way that is more natural,
1214 * depending on the situation.</p>
1215 * <p/>
1216 * <p>It is possible to make several calls into the same <code>Builder</code> object to express
1217 * multiple relationships. However, note that it is only the animation passed into the initial
Chet Haasea18a86b2010-09-07 13:20:00 -07001218 * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
Chet Haase17fb4b02010-06-28 17:55:07 -07001219 * calls to the <code>Builder</code> object. For example, the following code starts both anim2
1220 * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
1221 * anim3:
1222 * <pre>
Chet Haasea18a86b2010-09-07 13:20:00 -07001223 * AnimatorSet s = new AnimatorSet();
Chet Haase17fb4b02010-06-28 17:55:07 -07001224 * s.play(anim1).before(anim2).before(anim3);
1225 * </pre>
1226 * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
1227 * relationship correctly:</p>
1228 * <pre>
Chet Haasea18a86b2010-09-07 13:20:00 -07001229 * AnimatorSet s = new AnimatorSet();
Chet Haase17fb4b02010-06-28 17:55:07 -07001230 * s.play(anim1).before(anim2);
1231 * s.play(anim2).before(anim3);
1232 * </pre>
1233 * <p/>
1234 * <p>Note that it is possible to express relationships that cannot be resolved and will not
1235 * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
1236 * sense. In general, circular dependencies like this one (or more indirect ones where a depends
Chet Haasea18a86b2010-09-07 13:20:00 -07001237 * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
1238 * that can boil down to a simple, one-way relationship of animations starting with, before, and
Chet Haase17fb4b02010-06-28 17:55:07 -07001239 * after other, different, animations.</p>
1240 */
1241 public class Builder {
1242
1243 /**
1244 * This tracks the current node being processed. It is supplied to the play() method
Chet Haasea18a86b2010-09-07 13:20:00 -07001245 * of AnimatorSet and passed into the constructor of Builder.
Chet Haase17fb4b02010-06-28 17:55:07 -07001246 */
1247 private Node mCurrentNode;
1248
1249 /**
Chet Haasea18a86b2010-09-07 13:20:00 -07001250 * package-private constructor. Builders are only constructed by AnimatorSet, when the
Chet Haase17fb4b02010-06-28 17:55:07 -07001251 * play() method is called.
1252 *
1253 * @param anim The animation that is the dependency for the other animations passed into
1254 * the other methods of this Builder object.
1255 */
Chet Haasea18a86b2010-09-07 13:20:00 -07001256 Builder(Animator anim) {
Doris Liu13099142015-07-10 17:32:41 -07001257 mDependencyDirty = true;
1258 mCurrentNode = getNodeForAnimation(anim);
Chet Haase17fb4b02010-06-28 17:55:07 -07001259 }
1260
1261 /**
1262 * Sets up the given animation to play at the same time as the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001263 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
Chet Haase17fb4b02010-06-28 17:55:07 -07001264 *
1265 * @param anim The animation that will play when the animation supplied to the
Chet Haasea18a86b2010-09-07 13:20:00 -07001266 * {@link AnimatorSet#play(Animator)} method starts.
Chet Haase17fb4b02010-06-28 17:55:07 -07001267 */
Chet Haase2970c492010-11-09 13:58:04 -08001268 public Builder with(Animator anim) {
Doris Liu13099142015-07-10 17:32:41 -07001269 Node node = getNodeForAnimation(anim);
1270 mCurrentNode.addSibling(node);
Chet Haase2970c492010-11-09 13:58:04 -08001271 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001272 }
1273
1274 /**
1275 * Sets up the given animation to play when the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001276 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
Chet Haase17fb4b02010-06-28 17:55:07 -07001277 * ends.
1278 *
1279 * @param anim The animation that will play when the animation supplied to the
Chet Haasea18a86b2010-09-07 13:20:00 -07001280 * {@link AnimatorSet#play(Animator)} method ends.
Chet Haase17fb4b02010-06-28 17:55:07 -07001281 */
Chet Haase2970c492010-11-09 13:58:04 -08001282 public Builder before(Animator anim) {
ztenghui7bc6a3f2014-07-15 15:12:12 -07001283 mReversible = false;
Doris Liu13099142015-07-10 17:32:41 -07001284 Node node = getNodeForAnimation(anim);
1285 mCurrentNode.addChild(node);
Chet Haase2970c492010-11-09 13:58:04 -08001286 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001287 }
1288
1289 /**
1290 * Sets up the given animation to play when the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001291 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
Chet Haase17fb4b02010-06-28 17:55:07 -07001292 * to start when the animation supplied in this method call ends.
1293 *
1294 * @param anim The animation whose end will cause the animation supplied to the
Chet Haasea18a86b2010-09-07 13:20:00 -07001295 * {@link AnimatorSet#play(Animator)} method to play.
Chet Haase17fb4b02010-06-28 17:55:07 -07001296 */
Chet Haase2970c492010-11-09 13:58:04 -08001297 public Builder after(Animator anim) {
ztenghui7bc6a3f2014-07-15 15:12:12 -07001298 mReversible = false;
Doris Liu13099142015-07-10 17:32:41 -07001299 Node node = getNodeForAnimation(anim);
1300 mCurrentNode.addParent(node);
Chet Haase2970c492010-11-09 13:58:04 -08001301 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001302 }
1303
1304 /**
1305 * Sets up the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001306 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
Chet Haase17fb4b02010-06-28 17:55:07 -07001307 * to play when the given amount of time elapses.
1308 *
1309 * @param delay The number of milliseconds that should elapse before the
1310 * animation starts.
1311 */
Chet Haase2970c492010-11-09 13:58:04 -08001312 public Builder after(long delay) {
Chet Haasea18a86b2010-09-07 13:20:00 -07001313 // setup dummy ValueAnimator just to run the clock
Chet Haase2794eb32010-10-12 16:29:28 -07001314 ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
1315 anim.setDuration(delay);
1316 after(anim);
Chet Haase2970c492010-11-09 13:58:04 -08001317 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001318 }
1319
1320 }
1321
1322}