blob: cdd72be76805a2c57845a1e771a409ae3e53dd8b [file] [log] [blame]
Chet Haase21cd1382010-09-01 17:42:29 -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 android.view.View;
20import android.view.ViewGroup;
Chet Haasecca2c982011-05-20 14:34:18 -070021import android.view.ViewParent;
Chet Haase21cd1382010-09-01 17:42:29 -070022import android.view.ViewTreeObserver;
23import android.view.animation.AccelerateDecelerateInterpolator;
24import android.view.animation.DecelerateInterpolator;
Chet Haase21cd1382010-09-01 17:42:29 -070025
26import java.util.ArrayList;
Chet Haase1a76dcd2011-10-06 11:16:40 -070027import java.util.Collection;
Chet Haase21cd1382010-09-01 17:42:29 -070028import java.util.HashMap;
Chet Haaseeb1d8512011-07-18 12:12:36 -070029import java.util.LinkedHashMap;
Chet Haase21cd1382010-09-01 17:42:29 -070030import java.util.List;
Chet Haase06985942015-05-05 16:43:24 -070031import java.util.Map;
Chet Haase21cd1382010-09-01 17:42:29 -070032
33/**
34 * This class enables automatic animations on layout changes in ViewGroup objects. To enable
35 * transitions for a layout container, create a LayoutTransition object and set it on any
36 * ViewGroup by calling {@link ViewGroup#setLayoutTransition(LayoutTransition)}. This will cause
37 * default animations to run whenever items are added to or removed from that container. To specify
Chet Haasea18a86b2010-09-07 13:20:00 -070038 * custom animations, use the {@link LayoutTransition#setAnimator(int, Animator)
39 * setAnimator()} method.
Chet Haase21cd1382010-09-01 17:42:29 -070040 *
Chet Haasedaf98e92011-01-10 14:10:36 -080041 * <p>One of the core concepts of these transition animations is that there are two types of
Chet Haase21cd1382010-09-01 17:42:29 -070042 * changes that cause the transition and four different animations that run because of
43 * those changes. The changes that trigger the transition are items being added to a container
44 * (referred to as an "appearing" transition) or removed from a container (also known as
Chet Haasedaf98e92011-01-10 14:10:36 -080045 * "disappearing"). Setting the visibility of views (between GONE and VISIBLE) will trigger
46 * the same add/remove logic. The animations that run due to those events are one that animates
Chet Haase21cd1382010-09-01 17:42:29 -070047 * items being added, one that animates items being removed, and two that animate the other
48 * items in the container that change due to the add/remove occurrence. Users of
49 * the transition may want different animations for the changing items depending on whether
Chet Haasedaf98e92011-01-10 14:10:36 -080050 * they are changing due to an appearing or disappearing event, so there is one animation for
Chet Haase21cd1382010-09-01 17:42:29 -070051 * each of these variations of the changing event. Most of the API of this class is concerned
52 * with setting up the basic properties of the animations used in these four situations,
53 * or with setting up custom animations for any or all of the four.</p>
54 *
Chet Haasec54ed962011-05-06 14:13:05 -070055 * <p>By default, the DISAPPEARING animation begins immediately, as does the CHANGE_APPEARING
56 * animation. The other animations begin after a delay that is set to the default duration
57 * of the animations. This behavior facilitates a sequence of animations in transitions as
58 * follows: when an item is being added to a layout, the other children of that container will
59 * move first (thus creating space for the new item), then the appearing animation will run to
60 * animate the item being added. Conversely, when an item is removed from a container, the
61 * animation to remove it will run first, then the animations of the other children in the
62 * layout will run (closing the gap created in the layout when the item was removed). If this
63 * default choreography behavior is not desired, the {@link #setDuration(int, long)} and
64 * {@link #setStartDelay(int, long)} of any or all of the animations can be changed as
65 * appropriate.</p>
66 *
Chet Haase21cd1382010-09-01 17:42:29 -070067 * <p>The animations specified for the transition, both the defaults and any custom animations
68 * set on the transition object, are templates only. That is, these animations exist to hold the
69 * basic animation properties, such as the duration, start delay, and properties being animated.
70 * But the actual target object, as well as the start and end values for those properties, are
71 * set automatically in the process of setting up the transition each time it runs. Each of the
72 * animations is cloned from the original copy and the clone is then populated with the dynamic
73 * values of the target being animated (such as one of the items in a layout container that is
74 * moving as a result of the layout event) as well as the values that are changing (such as the
75 * position and size of that object). The actual values that are pushed to each animation
76 * depends on what properties are specified for the animation. For example, the default
Chet Haasecca2c982011-05-20 14:34:18 -070077 * CHANGE_APPEARING animation animates the <code>left</code>, <code>top</code>, <code>right</code>,
78 * <code>bottom</code>, <code>scrollX</code>, and <code>scrollY</code> properties.
79 * Values for these properties are updated with the pre- and post-layout
Chet Haase21cd1382010-09-01 17:42:29 -070080 * values when the transition begins. Custom animations will be similarly populated with
Chet Haasea18a86b2010-09-07 13:20:00 -070081 * the target and values being animated, assuming they use ObjectAnimator objects with
Chet Haase21cd1382010-09-01 17:42:29 -070082 * property names that are known on the target object.</p>
Chet Haasedaf98e92011-01-10 14:10:36 -080083 *
84 * <p>This class, and the associated XML flag for containers, animateLayoutChanges="true",
85 * provides a simple utility meant for automating changes in straightforward situations.
86 * Using LayoutTransition at multiple levels of a nested view hierarchy may not work due to the
87 * interrelationship of the various levels of layout. Also, a container that is being scrolled
88 * at the same time as items are being added or removed is probably not a good candidate for
89 * this utility, because the before/after locations calculated by LayoutTransition
90 * may not match the actual locations when the animations finish due to the container
91 * being scrolled as the animations are running. You can work around that
92 * particular issue by disabling the 'changing' animations by setting the CHANGE_APPEARING
93 * and CHANGE_DISAPPEARING animations to null, and setting the startDelay of the
94 * other animations appropriately.</p>
Chet Haase21cd1382010-09-01 17:42:29 -070095 */
96public class LayoutTransition {
97
98 /**
99 * A flag indicating the animation that runs on those items that are changing
100 * due to a new item appearing in the container.
101 */
102 public static final int CHANGE_APPEARING = 0;
103
104 /**
105 * A flag indicating the animation that runs on those items that are changing
Chet Haase9e90a992011-01-04 16:23:21 -0800106 * due to an item disappearing from the container.
Chet Haase21cd1382010-09-01 17:42:29 -0700107 */
108 public static final int CHANGE_DISAPPEARING = 1;
109
110 /**
Chet Haase9e90a992011-01-04 16:23:21 -0800111 * A flag indicating the animation that runs on those items that are appearing
112 * in the container.
Chet Haase21cd1382010-09-01 17:42:29 -0700113 */
114 public static final int APPEARING = 2;
115
116 /**
Chet Haase9e90a992011-01-04 16:23:21 -0800117 * A flag indicating the animation that runs on those items that are disappearing
118 * from the container.
Chet Haase21cd1382010-09-01 17:42:29 -0700119 */
120 public static final int DISAPPEARING = 3;
121
122 /**
Chet Haase7dd4a532012-04-16 13:35:09 -0700123 * A flag indicating the animation that runs on those items that are changing
124 * due to a layout change not caused by items being added to or removed
125 * from the container. This transition type is not enabled by default; it can be
126 * enabled via {@link #enableTransitionType(int)}.
127 */
128 public static final int CHANGING = 4;
129
130 /**
131 * Private bit fields used to set the collection of enabled transition types for
132 * mTransitionTypes.
133 */
134 private static final int FLAG_APPEARING = 0x01;
135 private static final int FLAG_DISAPPEARING = 0x02;
136 private static final int FLAG_CHANGE_APPEARING = 0x04;
137 private static final int FLAG_CHANGE_DISAPPEARING = 0x08;
138 private static final int FLAG_CHANGING = 0x10;
139
140 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700141 * These variables hold the animations that are currently used to run the transition effects.
142 * These animations are set to defaults, but can be changed to custom animations by
Chet Haasea18a86b2010-09-07 13:20:00 -0700143 * calls to setAnimator().
Chet Haase21cd1382010-09-01 17:42:29 -0700144 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700145 private Animator mDisappearingAnim = null;
146 private Animator mAppearingAnim = null;
147 private Animator mChangingAppearingAnim = null;
148 private Animator mChangingDisappearingAnim = null;
Chet Haase7dd4a532012-04-16 13:35:09 -0700149 private Animator mChangingAnim = null;
Chet Haase21cd1382010-09-01 17:42:29 -0700150
151 /**
152 * These are the default animations, defined in the constructor, that will be used
153 * unless the user specifies custom animations.
154 */
Chet Haase7dd4a532012-04-16 13:35:09 -0700155 private static ObjectAnimator defaultChange;
Chet Haasea18a86b2010-09-07 13:20:00 -0700156 private static ObjectAnimator defaultChangeIn;
157 private static ObjectAnimator defaultChangeOut;
158 private static ObjectAnimator defaultFadeIn;
159 private static ObjectAnimator defaultFadeOut;
Chet Haase21cd1382010-09-01 17:42:29 -0700160
161 /**
162 * The default duration used by all animations.
163 */
164 private static long DEFAULT_DURATION = 300;
165
166 /**
Chet Haase7dd4a532012-04-16 13:35:09 -0700167 * The durations of the different animations
Chet Haase21cd1382010-09-01 17:42:29 -0700168 */
169 private long mChangingAppearingDuration = DEFAULT_DURATION;
170 private long mChangingDisappearingDuration = DEFAULT_DURATION;
Chet Haase7dd4a532012-04-16 13:35:09 -0700171 private long mChangingDuration = DEFAULT_DURATION;
Chet Haase21cd1382010-09-01 17:42:29 -0700172 private long mAppearingDuration = DEFAULT_DURATION;
173 private long mDisappearingDuration = DEFAULT_DURATION;
174
175 /**
Chet Haase7dd4a532012-04-16 13:35:09 -0700176 * The start delays of the different animations. Note that the default behavior of
Chet Haase21cd1382010-09-01 17:42:29 -0700177 * the appearing item is the default duration, since it should wait for the items to move
178 * before fading it. Same for the changing animation when disappearing; it waits for the item
179 * to fade out before moving the other items.
180 */
181 private long mAppearingDelay = DEFAULT_DURATION;
182 private long mDisappearingDelay = 0;
183 private long mChangingAppearingDelay = 0;
184 private long mChangingDisappearingDelay = DEFAULT_DURATION;
Chet Haase7dd4a532012-04-16 13:35:09 -0700185 private long mChangingDelay = 0;
Chet Haase21cd1382010-09-01 17:42:29 -0700186
187 /**
Chet Haase7dd4a532012-04-16 13:35:09 -0700188 * The inter-animation delays used on the changing animations
Chet Haase21cd1382010-09-01 17:42:29 -0700189 */
190 private long mChangingAppearingStagger = 0;
191 private long mChangingDisappearingStagger = 0;
Chet Haase7dd4a532012-04-16 13:35:09 -0700192 private long mChangingStagger = 0;
Chet Haase21cd1382010-09-01 17:42:29 -0700193
194 /**
Chet Haase33d08762013-10-10 11:11:37 -0700195 * Static interpolators - these are stateless and can be shared across the instances
196 */
197 private static TimeInterpolator ACCEL_DECEL_INTERPOLATOR =
198 new AccelerateDecelerateInterpolator();
199 private static TimeInterpolator DECEL_INTERPOLATOR = new DecelerateInterpolator();
200 private static TimeInterpolator sAppearingInterpolator = ACCEL_DECEL_INTERPOLATOR;
201 private static TimeInterpolator sDisappearingInterpolator = ACCEL_DECEL_INTERPOLATOR;
202 private static TimeInterpolator sChangingAppearingInterpolator = DECEL_INTERPOLATOR;
203 private static TimeInterpolator sChangingDisappearingInterpolator = DECEL_INTERPOLATOR;
204 private static TimeInterpolator sChangingInterpolator = DECEL_INTERPOLATOR;
205
206 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700207 * The default interpolators used for the animations
208 */
Chet Haase33d08762013-10-10 11:11:37 -0700209 private TimeInterpolator mAppearingInterpolator = sAppearingInterpolator;
210 private TimeInterpolator mDisappearingInterpolator = sDisappearingInterpolator;
211 private TimeInterpolator mChangingAppearingInterpolator = sChangingAppearingInterpolator;
212 private TimeInterpolator mChangingDisappearingInterpolator = sChangingDisappearingInterpolator;
213 private TimeInterpolator mChangingInterpolator = sChangingInterpolator;
Chet Haase21cd1382010-09-01 17:42:29 -0700214
215 /**
Chet Haasee64ea872010-12-13 16:15:53 -0800216 * These hashmaps are used to store the animations that are currently running as part of
Chet Haase21cd1382010-09-01 17:42:29 -0700217 * the transition. The reason for this is that a further layout event should cause
218 * existing animations to stop where they are prior to starting new animations. So
219 * we cache all of the current animations in this map for possible cancellation on
Chet Haaseeb1d8512011-07-18 12:12:36 -0700220 * another layout event. LinkedHashMaps are used to preserve the order in which animations
221 * are inserted, so that we process events (such as setting up start values) in the same order.
Chet Haase21cd1382010-09-01 17:42:29 -0700222 */
Chet Haaseeb1d8512011-07-18 12:12:36 -0700223 private final HashMap<View, Animator> pendingAnimations =
Chet Haasee8e45d32011-03-02 17:07:35 -0800224 new HashMap<View, Animator>();
Chet Haaseeb1d8512011-07-18 12:12:36 -0700225 private final LinkedHashMap<View, Animator> currentChangingAnimations =
226 new LinkedHashMap<View, Animator>();
227 private final LinkedHashMap<View, Animator> currentAppearingAnimations =
228 new LinkedHashMap<View, Animator>();
229 private final LinkedHashMap<View, Animator> currentDisappearingAnimations =
230 new LinkedHashMap<View, Animator>();
Chet Haase21cd1382010-09-01 17:42:29 -0700231
232 /**
233 * This hashmap is used to track the listeners that have been added to the children of
234 * a container. When a layout change occurs, an animation is created for each View, so that
235 * the pre-layout values can be cached in that animation. Then a listener is added to the
236 * view to see whether the layout changes the bounds of that view. If so, the animation
237 * is set with the final values and then run. If not, the animation is not started. When
238 * the process of setting up and running all appropriate animations is done, we need to
239 * remove these listeners and clear out the map.
240 */
Ben Komalocbda9102010-12-16 15:55:27 -0800241 private final HashMap<View, View.OnLayoutChangeListener> layoutChangeListenerMap =
Chet Haase21cd1382010-09-01 17:42:29 -0700242 new HashMap<View, View.OnLayoutChangeListener>();
243
244 /**
245 * Used to track the current delay being assigned to successive animations as they are
246 * started. This value is incremented for each new animation, then zeroed before the next
247 * transition begins.
248 */
249 private long staggerDelay;
250
251 /**
Chet Haase7dd4a532012-04-16 13:35:09 -0700252 * These are the types of transition animations that the LayoutTransition is reacting
253 * to. By default, appearing/disappearing and the change animations related to them are
254 * enabled (not CHANGING).
255 */
256 private int mTransitionTypes = FLAG_CHANGE_APPEARING | FLAG_CHANGE_DISAPPEARING |
257 FLAG_APPEARING | FLAG_DISAPPEARING;
258 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700259 * The set of listeners that should be notified when APPEARING/DISAPPEARING transitions
260 * start and end.
261 */
262 private ArrayList<TransitionListener> mListeners;
263
Chet Haasecca2c982011-05-20 14:34:18 -0700264 /**
265 * Controls whether changing animations automatically animate the parent hierarchy as well.
266 * This behavior prevents artifacts when wrap_content layouts snap to the end state as the
267 * transition begins, causing visual glitches and clipping.
268 * Default value is true.
269 */
270 private boolean mAnimateParentHierarchy = true;
271
Chet Haase21cd1382010-09-01 17:42:29 -0700272
273 /**
274 * Constructs a LayoutTransition object. By default, the object will listen to layout
275 * events on any ViewGroup that it is set on and will run default animations for each
276 * type of layout event.
277 */
278 public LayoutTransition() {
279 if (defaultChangeIn == null) {
280 // "left" is just a placeholder; we'll put real properties/values in when needed
Chet Haase2794eb32010-10-12 16:29:28 -0700281 PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 1);
282 PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 1);
283 PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", 0, 1);
284 PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom", 0, 1);
Chet Haasecca2c982011-05-20 14:34:18 -0700285 PropertyValuesHolder pvhScrollX = PropertyValuesHolder.ofInt("scrollX", 0, 1);
286 PropertyValuesHolder pvhScrollY = PropertyValuesHolder.ofInt("scrollY", 0, 1);
Chet Haase622e05c2011-07-15 15:24:18 -0700287 defaultChangeIn = ObjectAnimator.ofPropertyValuesHolder((Object)null,
Chet Haasecca2c982011-05-20 14:34:18 -0700288 pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScrollX, pvhScrollY);
Chet Haase2794eb32010-10-12 16:29:28 -0700289 defaultChangeIn.setDuration(DEFAULT_DURATION);
Chet Haase21cd1382010-09-01 17:42:29 -0700290 defaultChangeIn.setStartDelay(mChangingAppearingDelay);
291 defaultChangeIn.setInterpolator(mChangingAppearingInterpolator);
292 defaultChangeOut = defaultChangeIn.clone();
293 defaultChangeOut.setStartDelay(mChangingDisappearingDelay);
294 defaultChangeOut.setInterpolator(mChangingDisappearingInterpolator);
Chet Haase7dd4a532012-04-16 13:35:09 -0700295 defaultChange = defaultChangeIn.clone();
296 defaultChange.setStartDelay(mChangingDelay);
297 defaultChange.setInterpolator(mChangingInterpolator);
Chet Haasecca2c982011-05-20 14:34:18 -0700298
Chet Haase622e05c2011-07-15 15:24:18 -0700299 defaultFadeIn = ObjectAnimator.ofFloat(null, "alpha", 0f, 1f);
Chet Haase2794eb32010-10-12 16:29:28 -0700300 defaultFadeIn.setDuration(DEFAULT_DURATION);
Chet Haase21cd1382010-09-01 17:42:29 -0700301 defaultFadeIn.setStartDelay(mAppearingDelay);
302 defaultFadeIn.setInterpolator(mAppearingInterpolator);
Chet Haase622e05c2011-07-15 15:24:18 -0700303 defaultFadeOut = ObjectAnimator.ofFloat(null, "alpha", 1f, 0f);
Chet Haase2794eb32010-10-12 16:29:28 -0700304 defaultFadeOut.setDuration(DEFAULT_DURATION);
Chet Haase21cd1382010-09-01 17:42:29 -0700305 defaultFadeOut.setStartDelay(mDisappearingDelay);
306 defaultFadeOut.setInterpolator(mDisappearingInterpolator);
307 }
308 mChangingAppearingAnim = defaultChangeIn;
309 mChangingDisappearingAnim = defaultChangeOut;
Chet Haase7dd4a532012-04-16 13:35:09 -0700310 mChangingAnim = defaultChange;
Chet Haase21cd1382010-09-01 17:42:29 -0700311 mAppearingAnim = defaultFadeIn;
312 mDisappearingAnim = defaultFadeOut;
313 }
314
315 /**
316 * Sets the duration to be used by all animations of this transition object. If you want to
317 * set the duration of just one of the animations in particular, use the
318 * {@link #setDuration(int, long)} method.
319 *
320 * @param duration The length of time, in milliseconds, that the transition animations
321 * should last.
322 */
323 public void setDuration(long duration) {
324 mChangingAppearingDuration = duration;
325 mChangingDisappearingDuration = duration;
Chet Haase7dd4a532012-04-16 13:35:09 -0700326 mChangingDuration = duration;
Chet Haase21cd1382010-09-01 17:42:29 -0700327 mAppearingDuration = duration;
328 mDisappearingDuration = duration;
329 }
330
331 /**
Chet Haase7dd4a532012-04-16 13:35:09 -0700332 * Enables the specified transitionType for this LayoutTransition object.
333 * By default, a LayoutTransition listens for changes in children being
334 * added/remove/hidden/shown in the container, and runs the animations associated with
335 * those events. That is, all transition types besides {@link #CHANGING} are enabled by default.
336 * You can also enable {@link #CHANGING} animations by calling this method with the
337 * {@link #CHANGING} transitionType.
338 *
339 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
340 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}.
341 */
342 public void enableTransitionType(int transitionType) {
343 switch (transitionType) {
344 case APPEARING:
345 mTransitionTypes |= FLAG_APPEARING;
346 break;
347 case DISAPPEARING:
348 mTransitionTypes |= FLAG_DISAPPEARING;
349 break;
350 case CHANGE_APPEARING:
351 mTransitionTypes |= FLAG_CHANGE_APPEARING;
352 break;
353 case CHANGE_DISAPPEARING:
354 mTransitionTypes |= FLAG_CHANGE_DISAPPEARING;
355 break;
356 case CHANGING:
357 mTransitionTypes |= FLAG_CHANGING;
358 break;
359 }
360 }
361
362 /**
363 * Disables the specified transitionType for this LayoutTransition object.
364 * By default, all transition types except {@link #CHANGING} are enabled.
365 *
366 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
367 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}.
368 */
369 public void disableTransitionType(int transitionType) {
370 switch (transitionType) {
371 case APPEARING:
372 mTransitionTypes &= ~FLAG_APPEARING;
373 break;
374 case DISAPPEARING:
375 mTransitionTypes &= ~FLAG_DISAPPEARING;
376 break;
377 case CHANGE_APPEARING:
378 mTransitionTypes &= ~FLAG_CHANGE_APPEARING;
379 break;
380 case CHANGE_DISAPPEARING:
381 mTransitionTypes &= ~FLAG_CHANGE_DISAPPEARING;
382 break;
383 case CHANGING:
384 mTransitionTypes &= ~FLAG_CHANGING;
385 break;
386 }
387 }
388
389 /**
390 * Returns whether the specified transitionType is enabled for this LayoutTransition object.
391 * By default, all transition types except {@link #CHANGING} are enabled.
392 *
393 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
394 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}.
395 * @return true if the specified transitionType is currently enabled, false otherwise.
396 */
397 public boolean isTransitionTypeEnabled(int transitionType) {
398 switch (transitionType) {
399 case APPEARING:
400 return (mTransitionTypes & FLAG_APPEARING) == FLAG_APPEARING;
401 case DISAPPEARING:
402 return (mTransitionTypes & FLAG_DISAPPEARING) == FLAG_DISAPPEARING;
403 case CHANGE_APPEARING:
404 return (mTransitionTypes & FLAG_CHANGE_APPEARING) == FLAG_CHANGE_APPEARING;
405 case CHANGE_DISAPPEARING:
406 return (mTransitionTypes & FLAG_CHANGE_DISAPPEARING) == FLAG_CHANGE_DISAPPEARING;
407 case CHANGING:
408 return (mTransitionTypes & FLAG_CHANGING) == FLAG_CHANGING;
409 }
410 return false;
411 }
412
413 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700414 * Sets the start delay on one of the animation objects used by this transition. The
415 * <code>transitionType</code> parameter determines the animation whose start delay
416 * is being set.
417 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700418 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
419 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines
420 * the animation whose start delay is being set.
Chet Haase21cd1382010-09-01 17:42:29 -0700421 * @param delay The length of time, in milliseconds, to delay before starting the animation.
Chet Haasea18a86b2010-09-07 13:20:00 -0700422 * @see Animator#setStartDelay(long)
Chet Haase21cd1382010-09-01 17:42:29 -0700423 */
424 public void setStartDelay(int transitionType, long delay) {
425 switch (transitionType) {
426 case CHANGE_APPEARING:
427 mChangingAppearingDelay = delay;
428 break;
429 case CHANGE_DISAPPEARING:
430 mChangingDisappearingDelay = delay;
431 break;
Chet Haase7dd4a532012-04-16 13:35:09 -0700432 case CHANGING:
433 mChangingDelay = delay;
434 break;
Chet Haase21cd1382010-09-01 17:42:29 -0700435 case APPEARING:
436 mAppearingDelay = delay;
437 break;
438 case DISAPPEARING:
439 mDisappearingDelay = delay;
440 break;
441 }
442 }
443
444 /**
445 * Gets the start delay on one of the animation objects used by this transition. The
446 * <code>transitionType</code> parameter determines the animation whose start delay
447 * is returned.
448 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700449 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
450 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines
451 * the animation whose start delay is returned.
Chet Haase21cd1382010-09-01 17:42:29 -0700452 * @return long The start delay of the specified animation.
Chet Haasea18a86b2010-09-07 13:20:00 -0700453 * @see Animator#getStartDelay()
Chet Haase21cd1382010-09-01 17:42:29 -0700454 */
455 public long getStartDelay(int transitionType) {
456 switch (transitionType) {
457 case CHANGE_APPEARING:
Rajdeep Duacf06d732012-01-02 08:45:48 +0530458 return mChangingAppearingDelay;
Chet Haase21cd1382010-09-01 17:42:29 -0700459 case CHANGE_DISAPPEARING:
Rajdeep Duacf06d732012-01-02 08:45:48 +0530460 return mChangingDisappearingDelay;
Chet Haase7dd4a532012-04-16 13:35:09 -0700461 case CHANGING:
462 return mChangingDelay;
Chet Haase21cd1382010-09-01 17:42:29 -0700463 case APPEARING:
Rajdeep Duacf06d732012-01-02 08:45:48 +0530464 return mAppearingDelay;
Chet Haase21cd1382010-09-01 17:42:29 -0700465 case DISAPPEARING:
Rajdeep Duacf06d732012-01-02 08:45:48 +0530466 return mDisappearingDelay;
Chet Haase21cd1382010-09-01 17:42:29 -0700467 }
468 // shouldn't reach here
469 return 0;
470 }
471
472 /**
473 * Sets the duration on one of the animation objects used by this transition. The
474 * <code>transitionType</code> parameter determines the animation whose duration
475 * is being set.
476 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700477 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
478 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines
479 * the animation whose duration is being set.
Chet Haase21cd1382010-09-01 17:42:29 -0700480 * @param duration The length of time, in milliseconds, that the specified animation should run.
Chet Haasea18a86b2010-09-07 13:20:00 -0700481 * @see Animator#setDuration(long)
Chet Haase21cd1382010-09-01 17:42:29 -0700482 */
483 public void setDuration(int transitionType, long duration) {
484 switch (transitionType) {
485 case CHANGE_APPEARING:
486 mChangingAppearingDuration = duration;
487 break;
488 case CHANGE_DISAPPEARING:
489 mChangingDisappearingDuration = duration;
490 break;
Chet Haase7dd4a532012-04-16 13:35:09 -0700491 case CHANGING:
492 mChangingDuration = duration;
493 break;
Chet Haase21cd1382010-09-01 17:42:29 -0700494 case APPEARING:
495 mAppearingDuration = duration;
496 break;
497 case DISAPPEARING:
498 mDisappearingDuration = duration;
499 break;
500 }
501 }
502
503 /**
504 * Gets the duration on one of the animation objects used by this transition. The
505 * <code>transitionType</code> parameter determines the animation whose duration
506 * is returned.
507 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700508 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
509 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines
510 * the animation whose duration is returned.
Chet Haase21cd1382010-09-01 17:42:29 -0700511 * @return long The duration of the specified animation.
Chet Haasea18a86b2010-09-07 13:20:00 -0700512 * @see Animator#getDuration()
Chet Haase21cd1382010-09-01 17:42:29 -0700513 */
514 public long getDuration(int transitionType) {
515 switch (transitionType) {
516 case CHANGE_APPEARING:
517 return mChangingAppearingDuration;
518 case CHANGE_DISAPPEARING:
519 return mChangingDisappearingDuration;
Chet Haase7dd4a532012-04-16 13:35:09 -0700520 case CHANGING:
521 return mChangingDuration;
Chet Haase21cd1382010-09-01 17:42:29 -0700522 case APPEARING:
523 return mAppearingDuration;
524 case DISAPPEARING:
525 return mDisappearingDuration;
526 }
527 // shouldn't reach here
528 return 0;
529 }
530
531 /**
532 * Sets the length of time to delay between starting each animation during one of the
Chet Haase7dd4a532012-04-16 13:35:09 -0700533 * change animations.
Chet Haase21cd1382010-09-01 17:42:29 -0700534 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700535 * @param transitionType A value of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING}, or
536 * {@link #CHANGING}.
Chet Haase21cd1382010-09-01 17:42:29 -0700537 * @param duration The length of time, in milliseconds, to delay before launching the next
538 * animation in the sequence.
539 */
540 public void setStagger(int transitionType, long duration) {
541 switch (transitionType) {
542 case CHANGE_APPEARING:
543 mChangingAppearingStagger = duration;
544 break;
545 case CHANGE_DISAPPEARING:
546 mChangingDisappearingStagger = duration;
547 break;
Chet Haase7dd4a532012-04-16 13:35:09 -0700548 case CHANGING:
549 mChangingStagger = duration;
550 break;
Chet Haase21cd1382010-09-01 17:42:29 -0700551 // noop other cases
552 }
553 }
554
555 /**
Chet Haase7dd4a532012-04-16 13:35:09 -0700556 * Gets the length of time to delay between starting each animation during one of the
557 * change animations.
Chet Haase21cd1382010-09-01 17:42:29 -0700558 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700559 * @param transitionType A value of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING}, or
560 * {@link #CHANGING}.
Chet Haase21cd1382010-09-01 17:42:29 -0700561 * @return long The length of time, in milliseconds, to delay before launching the next
562 * animation in the sequence.
563 */
564 public long getStagger(int transitionType) {
565 switch (transitionType) {
566 case CHANGE_APPEARING:
567 return mChangingAppearingStagger;
568 case CHANGE_DISAPPEARING:
569 return mChangingDisappearingStagger;
Chet Haase7dd4a532012-04-16 13:35:09 -0700570 case CHANGING:
571 return mChangingStagger;
Chet Haase21cd1382010-09-01 17:42:29 -0700572 }
573 // shouldn't reach here
574 return 0;
575 }
576
577 /**
578 * Sets the interpolator on one of the animation objects used by this transition. The
579 * <code>transitionType</code> parameter determines the animation whose interpolator
580 * is being set.
581 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700582 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
583 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines
584 * the animation whose interpolator is being set.
Chet Haase21cd1382010-09-01 17:42:29 -0700585 * @param interpolator The interpolator that the specified animation should use.
Chet Haasee0ee2e92010-10-07 09:06:18 -0700586 * @see Animator#setInterpolator(TimeInterpolator)
Chet Haase21cd1382010-09-01 17:42:29 -0700587 */
Chet Haasee0ee2e92010-10-07 09:06:18 -0700588 public void setInterpolator(int transitionType, TimeInterpolator interpolator) {
Chet Haase21cd1382010-09-01 17:42:29 -0700589 switch (transitionType) {
590 case CHANGE_APPEARING:
591 mChangingAppearingInterpolator = interpolator;
592 break;
593 case CHANGE_DISAPPEARING:
594 mChangingDisappearingInterpolator = interpolator;
595 break;
Chet Haase7dd4a532012-04-16 13:35:09 -0700596 case CHANGING:
597 mChangingInterpolator = interpolator;
598 break;
Chet Haase21cd1382010-09-01 17:42:29 -0700599 case APPEARING:
600 mAppearingInterpolator = interpolator;
601 break;
602 case DISAPPEARING:
603 mDisappearingInterpolator = interpolator;
604 break;
605 }
606 }
607
608 /**
609 * Gets the interpolator on one of the animation objects used by this transition. The
610 * <code>transitionType</code> parameter determines the animation whose interpolator
611 * is returned.
612 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700613 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
614 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines
615 * the animation whose interpolator is being returned.
Chet Haasee0ee2e92010-10-07 09:06:18 -0700616 * @return TimeInterpolator The interpolator that the specified animation uses.
617 * @see Animator#setInterpolator(TimeInterpolator)
Chet Haase21cd1382010-09-01 17:42:29 -0700618 */
Chet Haasee0ee2e92010-10-07 09:06:18 -0700619 public TimeInterpolator getInterpolator(int transitionType) {
Chet Haase21cd1382010-09-01 17:42:29 -0700620 switch (transitionType) {
621 case CHANGE_APPEARING:
622 return mChangingAppearingInterpolator;
623 case CHANGE_DISAPPEARING:
624 return mChangingDisappearingInterpolator;
Chet Haase7dd4a532012-04-16 13:35:09 -0700625 case CHANGING:
626 return mChangingInterpolator;
Chet Haase21cd1382010-09-01 17:42:29 -0700627 case APPEARING:
628 return mAppearingInterpolator;
629 case DISAPPEARING:
630 return mDisappearingInterpolator;
631 }
632 // shouldn't reach here
633 return null;
634 }
635
636 /**
637 * Sets the animation used during one of the transition types that may run. Any
Chet Haasea18a86b2010-09-07 13:20:00 -0700638 * Animator object can be used, but to be most useful in the context of layout
639 * transitions, the animation should either be a ObjectAnimator or a AnimatorSet
640 * of animations including PropertyAnimators. Also, these ObjectAnimator objects
Chet Haase21cd1382010-09-01 17:42:29 -0700641 * should be able to get and set values on their target objects automatically. For
Chet Haasea18a86b2010-09-07 13:20:00 -0700642 * example, a ObjectAnimator that animates the property "left" is able to set and get the
Chet Haase21cd1382010-09-01 17:42:29 -0700643 * <code>left</code> property from the View objects being animated by the layout
644 * transition. The transition works by setting target objects and properties
645 * dynamically, according to the pre- and post-layoout values of those objects, so
646 * having animations that can handle those properties appropriately will work best
647 * for custom animation. The dynamic setting of values is only the case for the
648 * CHANGE animations; the APPEARING and DISAPPEARING animations are simply run with
649 * the values they have.
650 *
651 * <p>It is also worth noting that any and all animations (and their underlying
652 * PropertyValuesHolder objects) will have their start and end values set according
653 * to the pre- and post-layout values. So, for example, a custom animation on "alpha"
654 * as the CHANGE_APPEARING animation will inherit the real value of alpha on the target
655 * object (presumably 1) as its starting and ending value when the animation begins.
656 * Animations which need to use values at the beginning and end that may not match the
657 * values queried when the transition begins may need to use a different mechanism
Chet Haasea18a86b2010-09-07 13:20:00 -0700658 * than a standard ObjectAnimator object.</p>
Chet Haase21cd1382010-09-01 17:42:29 -0700659 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700660 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
661 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the
662 * animation whose animator is being set.
Chet Haase5d6d7b92010-10-04 16:47:19 -0700663 * @param animator The animation being assigned. A value of <code>null</code> means that no
664 * animation will be run for the specified transitionType.
Chet Haase21cd1382010-09-01 17:42:29 -0700665 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700666 public void setAnimator(int transitionType, Animator animator) {
Chet Haase21cd1382010-09-01 17:42:29 -0700667 switch (transitionType) {
668 case CHANGE_APPEARING:
Chet Haase5d6d7b92010-10-04 16:47:19 -0700669 mChangingAppearingAnim = animator;
Chet Haase21cd1382010-09-01 17:42:29 -0700670 break;
671 case CHANGE_DISAPPEARING:
Chet Haase5d6d7b92010-10-04 16:47:19 -0700672 mChangingDisappearingAnim = animator;
Chet Haase21cd1382010-09-01 17:42:29 -0700673 break;
Chet Haase7dd4a532012-04-16 13:35:09 -0700674 case CHANGING:
675 mChangingAnim = animator;
676 break;
Chet Haase21cd1382010-09-01 17:42:29 -0700677 case APPEARING:
Chet Haase5d6d7b92010-10-04 16:47:19 -0700678 mAppearingAnim = animator;
Chet Haase21cd1382010-09-01 17:42:29 -0700679 break;
680 case DISAPPEARING:
Chet Haase5d6d7b92010-10-04 16:47:19 -0700681 mDisappearingAnim = animator;
Chet Haase21cd1382010-09-01 17:42:29 -0700682 break;
683 }
684 }
685
686 /**
687 * Gets the animation used during one of the transition types that may run.
688 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700689 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
690 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines
691 * the animation whose animator is being returned.
Chet Haasea18a86b2010-09-07 13:20:00 -0700692 * @return Animator The animation being used for the given transition type.
693 * @see #setAnimator(int, Animator)
Chet Haase21cd1382010-09-01 17:42:29 -0700694 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700695 public Animator getAnimator(int transitionType) {
Chet Haase21cd1382010-09-01 17:42:29 -0700696 switch (transitionType) {
697 case CHANGE_APPEARING:
698 return mChangingAppearingAnim;
699 case CHANGE_DISAPPEARING:
700 return mChangingDisappearingAnim;
Chet Haase7dd4a532012-04-16 13:35:09 -0700701 case CHANGING:
702 return mChangingAnim;
Chet Haase21cd1382010-09-01 17:42:29 -0700703 case APPEARING:
704 return mAppearingAnim;
705 case DISAPPEARING:
706 return mDisappearingAnim;
707 }
708 // shouldn't reach here
709 return null;
710 }
711
712 /**
Chet Haaseeb1d8512011-07-18 12:12:36 -0700713 * This function sets up animations on all of the views that change during layout.
Chet Haase21cd1382010-09-01 17:42:29 -0700714 * For every child in the parent, we create a change animation of the appropriate
Chet Haase7dd4a532012-04-16 13:35:09 -0700715 * type (appearing, disappearing, or changing) and ask it to populate its start values from its
Chet Haase21cd1382010-09-01 17:42:29 -0700716 * target view. We add layout listeners to all child views and listen for changes. For
717 * those views that change, we populate the end values for those animations and start them.
718 * Animations are not run on unchanging views.
719 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700720 * @param parent The container which is undergoing a change.
721 * @param newView The view being added to or removed from the parent. May be null if the
722 * changeReason is CHANGING.
723 * @param changeReason A value of APPEARING, DISAPPEARING, or CHANGING, indicating whether the
724 * transition is occurring because an item is being added to or removed from the parent, or
725 * if it is running in response to a layout operation (that is, if the value is CHANGING).
Chet Haase21cd1382010-09-01 17:42:29 -0700726 */
727 private void runChangeTransition(final ViewGroup parent, View newView, final int changeReason) {
Chet Haase5d6d7b92010-10-04 16:47:19 -0700728
Chet Haase7dd4a532012-04-16 13:35:09 -0700729 Animator baseAnimator = null;
730 Animator parentAnimator = null;
731 final long duration;
732 switch (changeReason) {
733 case APPEARING:
734 baseAnimator = mChangingAppearingAnim;
735 duration = mChangingAppearingDuration;
736 parentAnimator = defaultChangeIn;
737 break;
738 case DISAPPEARING:
739 baseAnimator = mChangingDisappearingAnim;
740 duration = mChangingDisappearingDuration;
741 parentAnimator = defaultChangeOut;
742 break;
743 case CHANGING:
744 baseAnimator = mChangingAnim;
745 duration = mChangingDuration;
746 parentAnimator = defaultChange;
747 break;
748 default:
749 // Shouldn't reach here
750 duration = 0;
751 break;
752 }
Chet Haase5d6d7b92010-10-04 16:47:19 -0700753 // If the animation is null, there's nothing to do
754 if (baseAnimator == null) {
755 return;
756 }
757
Chet Haase21cd1382010-09-01 17:42:29 -0700758 // reset the inter-animation delay, in case we use it later
759 staggerDelay = 0;
760
Chet Haase06985942015-05-05 16:43:24 -0700761 final ViewTreeObserver observer = parent.getViewTreeObserver();
Chet Haase634a20a2010-09-08 16:36:57 -0700762 if (!observer.isAlive()) {
763 // If the observer's not in a good state, skip the transition
764 return;
765 }
Chet Haase21cd1382010-09-01 17:42:29 -0700766 int numChildren = parent.getChildCount();
767
768 for (int i = 0; i < numChildren; ++i) {
769 final View child = parent.getChildAt(i);
770
771 // only animate the views not being added or removed
772 if (child != newView) {
Chet Haasecca2c982011-05-20 14:34:18 -0700773 setupChangeAnimation(parent, changeReason, baseAnimator, duration, child);
Chet Haase21cd1382010-09-01 17:42:29 -0700774 }
775 }
Chet Haasecca2c982011-05-20 14:34:18 -0700776 if (mAnimateParentHierarchy) {
777 ViewGroup tempParent = parent;
778 while (tempParent != null) {
779 ViewParent parentParent = tempParent.getParent();
780 if (parentParent instanceof ViewGroup) {
Chet Haasee115ffe2011-08-11 11:32:43 -0700781 setupChangeAnimation((ViewGroup)parentParent, changeReason, parentAnimator,
Chet Haasecca2c982011-05-20 14:34:18 -0700782 duration, tempParent);
783 tempParent = (ViewGroup) parentParent;
784 } else {
785 tempParent = null;
786 }
787
788 }
789 }
790
Chet Haase21cd1382010-09-01 17:42:29 -0700791 // This is the cleanup step. When we get this rendering event, we know that all of
792 // the appropriate animations have been set up and run. Now we can clear out the
793 // layout listeners.
Chet Haase06985942015-05-05 16:43:24 -0700794 CleanupCallback callback = new CleanupCallback(layoutChangeListenerMap, parent);
795 observer.addOnPreDrawListener(callback);
796 parent.addOnAttachStateChangeListener(callback);
Chet Haase21cd1382010-09-01 17:42:29 -0700797 }
798
799 /**
Chet Haasecca2c982011-05-20 14:34:18 -0700800 * This flag controls whether CHANGE_APPEARING or CHANGE_DISAPPEARING animations will
Chet Haasee115ffe2011-08-11 11:32:43 -0700801 * cause the default changing animation to be run on the parent hierarchy as well. This allows
Chet Haasecca2c982011-05-20 14:34:18 -0700802 * containers of transitioning views to also transition, which may be necessary in situations
803 * where the containers bounds change between the before/after states and may clip their
804 * children during the transition animations. For example, layouts with wrap_content will
805 * adjust their bounds according to the dimensions of their children.
806 *
Chet Haasee115ffe2011-08-11 11:32:43 -0700807 * <p>The default changing transitions animate the bounds and scroll positions of the
808 * target views. These are the animations that will run on the parent hierarchy, not
809 * the custom animations that happen to be set on the transition. This allows custom
810 * behavior for the children of the transitioning container, but uses standard behavior
811 * of resizing/rescrolling on any changing parents.
812 *
Chet Haasecca2c982011-05-20 14:34:18 -0700813 * @param animateParentHierarchy A boolean value indicating whether the parents of
814 * transitioning views should also be animated during the transition. Default value is true.
815 */
816 public void setAnimateParentHierarchy(boolean animateParentHierarchy) {
817 mAnimateParentHierarchy = animateParentHierarchy;
818 }
819
820 /**
821 * Utility function called by runChangingTransition for both the children and the parent
822 * hierarchy.
823 */
824 private void setupChangeAnimation(final ViewGroup parent, final int changeReason,
825 Animator baseAnimator, final long duration, final View child) {
Chet Haase8a22e592011-11-10 17:03:12 -0800826
827 // If we already have a listener for this child, then we've already set up the
828 // changing animation we need. Multiple calls for a child may occur when several
829 // add/remove operations are run at once on a container; each one will trigger
830 // changes for the existing children in the container.
831 if (layoutChangeListenerMap.get(child) != null) {
832 return;
833 }
834
Chet Haase66ef1a22012-06-01 15:09:07 -0700835 // Don't animate items up from size(0,0); this is likely because the objects
836 // were offscreen/invisible or otherwise measured to be infinitely small. We don't
837 // want to see them animate into their real size; just ignore animation requests
838 // on these views
839 if (child.getWidth() == 0 && child.getHeight() == 0) {
840 return;
841 }
842
Chet Haasecca2c982011-05-20 14:34:18 -0700843 // Make a copy of the appropriate animation
844 final Animator anim = baseAnimator.clone();
845
846 // Set the target object for the animation
847 anim.setTarget(child);
848
849 // A ObjectAnimator (or AnimatorSet of them) can extract start values from
850 // its target object
851 anim.setupStartValues();
852
853 // If there's an animation running on this view already, cancel it
854 Animator currentAnimation = pendingAnimations.get(child);
855 if (currentAnimation != null) {
856 currentAnimation.cancel();
857 pendingAnimations.remove(child);
858 }
859 // Cache the animation in case we need to cancel it later
860 pendingAnimations.put(child, anim);
861
862 // For the animations which don't get started, we have to have a means of
863 // removing them from the cache, lest we leak them and their target objects.
864 // We run an animator for the default duration+100 (an arbitrary time, but one
865 // which should far surpass the delay between setting them up here and
866 // handling layout events which start them.
867 ValueAnimator pendingAnimRemover = ValueAnimator.ofFloat(0f, 1f).
868 setDuration(duration + 100);
869 pendingAnimRemover.addListener(new AnimatorListenerAdapter() {
870 @Override
871 public void onAnimationEnd(Animator animation) {
872 pendingAnimations.remove(child);
873 }
874 });
875 pendingAnimRemover.start();
876
877 // Add a listener to track layout changes on this view. If we don't get a callback,
878 // then there's nothing to animate.
879 final View.OnLayoutChangeListener listener = new View.OnLayoutChangeListener() {
880 public void onLayoutChange(View v, int left, int top, int right, int bottom,
881 int oldLeft, int oldTop, int oldRight, int oldBottom) {
882
883 // Tell the animation to extract end values from the changed object
884 anim.setupEndValues();
885 if (anim instanceof ValueAnimator) {
886 boolean valuesDiffer = false;
887 ValueAnimator valueAnim = (ValueAnimator)anim;
888 PropertyValuesHolder[] oldValues = valueAnim.getValues();
889 for (int i = 0; i < oldValues.length; ++i) {
890 PropertyValuesHolder pvh = oldValues[i];
George Mount984011f2014-08-21 14:28:01 -0700891 if (pvh.mKeyframes instanceof KeyframeSet) {
892 KeyframeSet keyframeSet = (KeyframeSet) pvh.mKeyframes;
893 if (keyframeSet.mFirstKeyframe == null ||
894 keyframeSet.mLastKeyframe == null ||
895 !keyframeSet.mFirstKeyframe.getValue().equals(
896 keyframeSet.mLastKeyframe.getValue())) {
897 valuesDiffer = true;
898 }
899 } else if (!pvh.mKeyframes.getValue(0).equals(pvh.mKeyframes.getValue(1))) {
Chet Haasecca2c982011-05-20 14:34:18 -0700900 valuesDiffer = true;
901 }
902 }
903 if (!valuesDiffer) {
904 return;
905 }
906 }
907
Chet Haase7dd4a532012-04-16 13:35:09 -0700908 long startDelay = 0;
909 switch (changeReason) {
910 case APPEARING:
911 startDelay = mChangingAppearingDelay + staggerDelay;
912 staggerDelay += mChangingAppearingStagger;
Chet Haase33d08762013-10-10 11:11:37 -0700913 if (mChangingAppearingInterpolator != sChangingAppearingInterpolator) {
914 anim.setInterpolator(mChangingAppearingInterpolator);
915 }
Chet Haase7dd4a532012-04-16 13:35:09 -0700916 break;
917 case DISAPPEARING:
918 startDelay = mChangingDisappearingDelay + staggerDelay;
919 staggerDelay += mChangingDisappearingStagger;
Chet Haase33d08762013-10-10 11:11:37 -0700920 if (mChangingDisappearingInterpolator !=
921 sChangingDisappearingInterpolator) {
922 anim.setInterpolator(mChangingDisappearingInterpolator);
923 }
Chet Haase7dd4a532012-04-16 13:35:09 -0700924 break;
925 case CHANGING:
926 startDelay = mChangingDelay + staggerDelay;
927 staggerDelay += mChangingStagger;
Chet Haase33d08762013-10-10 11:11:37 -0700928 if (mChangingInterpolator != sChangingInterpolator) {
929 anim.setInterpolator(mChangingInterpolator);
930 }
Chet Haase7dd4a532012-04-16 13:35:09 -0700931 break;
Chet Haasecca2c982011-05-20 14:34:18 -0700932 }
933 anim.setStartDelay(startDelay);
934 anim.setDuration(duration);
935
936 Animator prevAnimation = currentChangingAnimations.get(child);
937 if (prevAnimation != null) {
938 prevAnimation.cancel();
939 }
940 Animator pendingAnimation = pendingAnimations.get(child);
941 if (pendingAnimation != null) {
942 pendingAnimations.remove(child);
943 }
944 // Cache the animation in case we need to cancel it later
945 currentChangingAnimations.put(child, anim);
946
947 parent.requestTransitionStart(LayoutTransition.this);
948
949 // this only removes listeners whose views changed - must clear the
950 // other listeners later
951 child.removeOnLayoutChangeListener(this);
952 layoutChangeListenerMap.remove(child);
953 }
954 };
955 // Remove the animation from the cache when it ends
956 anim.addListener(new AnimatorListenerAdapter() {
957
958 @Override
959 public void onAnimationStart(Animator animator) {
Luca Zanolinc20fc8d2012-06-19 18:15:29 +0100960 if (hasListeners()) {
961 ArrayList<TransitionListener> listeners =
962 (ArrayList<TransitionListener>) mListeners.clone();
963 for (TransitionListener listener : listeners) {
Chet Haasecca2c982011-05-20 14:34:18 -0700964 listener.startTransition(LayoutTransition.this, parent, child,
965 changeReason == APPEARING ?
Chet Haase7dd4a532012-04-16 13:35:09 -0700966 CHANGE_APPEARING : changeReason == DISAPPEARING ?
967 CHANGE_DISAPPEARING : CHANGING);
Chet Haasecca2c982011-05-20 14:34:18 -0700968 }
969 }
970 }
971
972 @Override
973 public void onAnimationCancel(Animator animator) {
974 child.removeOnLayoutChangeListener(listener);
975 layoutChangeListenerMap.remove(child);
976 }
977
978 @Override
979 public void onAnimationEnd(Animator animator) {
980 currentChangingAnimations.remove(child);
Luca Zanolinc20fc8d2012-06-19 18:15:29 +0100981 if (hasListeners()) {
982 ArrayList<TransitionListener> listeners =
983 (ArrayList<TransitionListener>) mListeners.clone();
984 for (TransitionListener listener : listeners) {
Chet Haasecca2c982011-05-20 14:34:18 -0700985 listener.endTransition(LayoutTransition.this, parent, child,
986 changeReason == APPEARING ?
Chet Haase7dd4a532012-04-16 13:35:09 -0700987 CHANGE_APPEARING : changeReason == DISAPPEARING ?
988 CHANGE_DISAPPEARING : CHANGING);
Chet Haasecca2c982011-05-20 14:34:18 -0700989 }
990 }
991 }
992 });
993
994 child.addOnLayoutChangeListener(listener);
995 // cache the listener for later removal
996 layoutChangeListenerMap.put(child, listener);
997 }
998
999 /**
1000 * Starts the animations set up for a CHANGING transition. We separate the setup of these
1001 * animations from actually starting them, to avoid side-effects that starting the animations
1002 * may have on the properties of the affected objects. After setup, we tell the affected parent
1003 * that this transition should be started. The parent informs its ViewAncestor, which then
1004 * starts the transition after the current layout/measurement phase, just prior to drawing
1005 * the view hierarchy.
1006 *
1007 * @hide
1008 */
1009 public void startChangingAnimations() {
Chet Haased56c6952011-09-07 08:46:23 -07001010 LinkedHashMap<View, Animator> currentAnimCopy =
1011 (LinkedHashMap<View, Animator>) currentChangingAnimations.clone();
1012 for (Animator anim : currentAnimCopy.values()) {
Chet Haasecca2c982011-05-20 14:34:18 -07001013 if (anim instanceof ObjectAnimator) {
1014 ((ObjectAnimator) anim).setCurrentPlayTime(0);
1015 }
1016 anim.start();
1017 }
1018 }
1019
1020 /**
Chet Haased56c6952011-09-07 08:46:23 -07001021 * Ends the animations that are set up for a CHANGING transition. This is a variant of
1022 * startChangingAnimations() which is called when the window the transition is playing in
1023 * is not visible. We need to make sure the animations put their targets in their end states
1024 * and that the transition finishes to remove any mid-process state (such as isRunning()).
1025 *
1026 * @hide
1027 */
1028 public void endChangingAnimations() {
1029 LinkedHashMap<View, Animator> currentAnimCopy =
1030 (LinkedHashMap<View, Animator>) currentChangingAnimations.clone();
1031 for (Animator anim : currentAnimCopy.values()) {
1032 anim.start();
1033 anim.end();
1034 }
Chet Haase17cf42c2012-04-17 13:18:14 -07001035 // listeners should clean up the currentChangingAnimations list, but just in case...
1036 currentChangingAnimations.clear();
Chet Haased56c6952011-09-07 08:46:23 -07001037 }
1038
1039 /**
Chet Haase9c087442011-01-12 16:20:16 -08001040 * Returns true if animations are running which animate layout-related properties. This
1041 * essentially means that either CHANGE_APPEARING or CHANGE_DISAPPEARING animations
1042 * are running, since these animations operate on layout-related properties.
1043 *
1044 * @return true if CHANGE_APPEARING or CHANGE_DISAPPEARING animations are currently
1045 * running.
1046 */
1047 public boolean isChangingLayout() {
1048 return (currentChangingAnimations.size() > 0);
1049 }
1050
1051 /**
1052 * Returns true if any of the animations in this transition are currently running.
1053 *
1054 * @return true if any animations in the transition are running.
1055 */
1056 public boolean isRunning() {
Chet Haasee8e45d32011-03-02 17:07:35 -08001057 return (currentChangingAnimations.size() > 0 || currentAppearingAnimations.size() > 0 ||
1058 currentDisappearingAnimations.size() > 0);
Chet Haase9c087442011-01-12 16:20:16 -08001059 }
1060
1061 /**
Chet Haaseadd65772011-02-09 16:47:29 -08001062 * Cancels the currently running transition. Note that we cancel() the changing animations
1063 * but end() the visibility animations. This is because this method is currently called
1064 * in the context of starting a new transition, so we want to move things from their mid-
1065 * transition positions, but we want them to have their end-transition visibility.
1066 *
1067 * @hide
1068 */
1069 public void cancel() {
Chet Haasee8e45d32011-03-02 17:07:35 -08001070 if (currentChangingAnimations.size() > 0) {
Chet Haaseeb1d8512011-07-18 12:12:36 -07001071 LinkedHashMap<View, Animator> currentAnimCopy =
1072 (LinkedHashMap<View, Animator>) currentChangingAnimations.clone();
Chet Haasee8e45d32011-03-02 17:07:35 -08001073 for (Animator anim : currentAnimCopy.values()) {
1074 anim.cancel();
1075 }
1076 currentChangingAnimations.clear();
Chet Haaseadd65772011-02-09 16:47:29 -08001077 }
Chet Haasee8e45d32011-03-02 17:07:35 -08001078 if (currentAppearingAnimations.size() > 0) {
Chet Haaseeb1d8512011-07-18 12:12:36 -07001079 LinkedHashMap<View, Animator> currentAnimCopy =
1080 (LinkedHashMap<View, Animator>) currentAppearingAnimations.clone();
Chet Haasee8e45d32011-03-02 17:07:35 -08001081 for (Animator anim : currentAnimCopy.values()) {
1082 anim.end();
1083 }
1084 currentAppearingAnimations.clear();
Chet Haaseadd65772011-02-09 16:47:29 -08001085 }
Chet Haasee8e45d32011-03-02 17:07:35 -08001086 if (currentDisappearingAnimations.size() > 0) {
Chet Haaseeb1d8512011-07-18 12:12:36 -07001087 LinkedHashMap<View, Animator> currentAnimCopy =
1088 (LinkedHashMap<View, Animator>) currentDisappearingAnimations.clone();
Chet Haasee8e45d32011-03-02 17:07:35 -08001089 for (Animator anim : currentAnimCopy.values()) {
1090 anim.end();
1091 }
1092 currentDisappearingAnimations.clear();
1093 }
1094 }
1095
1096 /**
1097 * Cancels the specified type of transition. Note that we cancel() the changing animations
1098 * but end() the visibility animations. This is because this method is currently called
1099 * in the context of starting a new transition, so we want to move things from their mid-
1100 * transition positions, but we want them to have their end-transition visibility.
1101 *
1102 * @hide
1103 */
1104 public void cancel(int transitionType) {
1105 switch (transitionType) {
1106 case CHANGE_APPEARING:
1107 case CHANGE_DISAPPEARING:
Chet Haase7dd4a532012-04-16 13:35:09 -07001108 case CHANGING:
Chet Haasee8e45d32011-03-02 17:07:35 -08001109 if (currentChangingAnimations.size() > 0) {
Chet Haaseeb1d8512011-07-18 12:12:36 -07001110 LinkedHashMap<View, Animator> currentAnimCopy =
1111 (LinkedHashMap<View, Animator>) currentChangingAnimations.clone();
Chet Haasee8e45d32011-03-02 17:07:35 -08001112 for (Animator anim : currentAnimCopy.values()) {
1113 anim.cancel();
1114 }
1115 currentChangingAnimations.clear();
1116 }
1117 break;
1118 case APPEARING:
1119 if (currentAppearingAnimations.size() > 0) {
Chet Haaseeb1d8512011-07-18 12:12:36 -07001120 LinkedHashMap<View, Animator> currentAnimCopy =
1121 (LinkedHashMap<View, Animator>) currentAppearingAnimations.clone();
Chet Haasee8e45d32011-03-02 17:07:35 -08001122 for (Animator anim : currentAnimCopy.values()) {
1123 anim.end();
1124 }
1125 currentAppearingAnimations.clear();
1126 }
1127 break;
1128 case DISAPPEARING:
1129 if (currentDisappearingAnimations.size() > 0) {
Chet Haaseeb1d8512011-07-18 12:12:36 -07001130 LinkedHashMap<View, Animator> currentAnimCopy =
1131 (LinkedHashMap<View, Animator>) currentDisappearingAnimations.clone();
Chet Haasee8e45d32011-03-02 17:07:35 -08001132 for (Animator anim : currentAnimCopy.values()) {
1133 anim.end();
1134 }
1135 currentDisappearingAnimations.clear();
1136 }
1137 break;
1138 }
Chet Haaseadd65772011-02-09 16:47:29 -08001139 }
1140
1141 /**
Chet Haase21cd1382010-09-01 17:42:29 -07001142 * This method runs the animation that makes an added item appear.
1143 *
1144 * @param parent The ViewGroup to which the View is being added.
1145 * @param child The View being added to the ViewGroup.
1146 */
1147 private void runAppearingTransition(final ViewGroup parent, final View child) {
Chet Haasee8e45d32011-03-02 17:07:35 -08001148 Animator currentAnimation = currentDisappearingAnimations.get(child);
Chet Haasee64ea872010-12-13 16:15:53 -08001149 if (currentAnimation != null) {
1150 currentAnimation.cancel();
1151 }
Chet Haase5d6d7b92010-10-04 16:47:19 -07001152 if (mAppearingAnim == null) {
Luca Zanolinc20fc8d2012-06-19 18:15:29 +01001153 if (hasListeners()) {
1154 ArrayList<TransitionListener> listeners =
1155 (ArrayList<TransitionListener>) mListeners.clone();
1156 for (TransitionListener listener : listeners) {
Chet Haase5d6d7b92010-10-04 16:47:19 -07001157 listener.endTransition(LayoutTransition.this, parent, child, APPEARING);
1158 }
1159 }
1160 return;
1161 }
Chet Haasea18a86b2010-09-07 13:20:00 -07001162 Animator anim = mAppearingAnim.clone();
Chet Haase21cd1382010-09-01 17:42:29 -07001163 anim.setTarget(child);
1164 anim.setStartDelay(mAppearingDelay);
1165 anim.setDuration(mAppearingDuration);
Chet Haase33d08762013-10-10 11:11:37 -07001166 if (mAppearingInterpolator != sAppearingInterpolator) {
1167 anim.setInterpolator(mAppearingInterpolator);
1168 }
Chet Haasea18a86b2010-09-07 13:20:00 -07001169 if (anim instanceof ObjectAnimator) {
1170 ((ObjectAnimator) anim).setCurrentPlayTime(0);
Chet Haase21cd1382010-09-01 17:42:29 -07001171 }
Chet Haasea5531132012-02-02 13:41:44 -08001172 anim.addListener(new AnimatorListenerAdapter() {
1173 @Override
1174 public void onAnimationEnd(Animator anim) {
1175 currentAppearingAnimations.remove(child);
Luca Zanolinc20fc8d2012-06-19 18:15:29 +01001176 if (hasListeners()) {
1177 ArrayList<TransitionListener> listeners =
1178 (ArrayList<TransitionListener>) mListeners.clone();
1179 for (TransitionListener listener : listeners) {
Chet Haase21cd1382010-09-01 17:42:29 -07001180 listener.endTransition(LayoutTransition.this, parent, child, APPEARING);
1181 }
1182 }
Chet Haasea5531132012-02-02 13:41:44 -08001183 }
1184 });
Chet Haasee8e45d32011-03-02 17:07:35 -08001185 currentAppearingAnimations.put(child, anim);
Chet Haase21cd1382010-09-01 17:42:29 -07001186 anim.start();
1187 }
1188
1189 /**
1190 * This method runs the animation that makes a removed item disappear.
1191 *
1192 * @param parent The ViewGroup from which the View is being removed.
1193 * @param child The View being removed from the ViewGroup.
1194 */
1195 private void runDisappearingTransition(final ViewGroup parent, final View child) {
Chet Haasee8e45d32011-03-02 17:07:35 -08001196 Animator currentAnimation = currentAppearingAnimations.get(child);
Chet Haasee64ea872010-12-13 16:15:53 -08001197 if (currentAnimation != null) {
1198 currentAnimation.cancel();
1199 }
Chet Haase5d6d7b92010-10-04 16:47:19 -07001200 if (mDisappearingAnim == null) {
Luca Zanolinc20fc8d2012-06-19 18:15:29 +01001201 if (hasListeners()) {
1202 ArrayList<TransitionListener> listeners =
1203 (ArrayList<TransitionListener>) mListeners.clone();
1204 for (TransitionListener listener : listeners) {
Chet Haase5d6d7b92010-10-04 16:47:19 -07001205 listener.endTransition(LayoutTransition.this, parent, child, DISAPPEARING);
1206 }
1207 }
1208 return;
1209 }
Chet Haasea18a86b2010-09-07 13:20:00 -07001210 Animator anim = mDisappearingAnim.clone();
Chet Haase21cd1382010-09-01 17:42:29 -07001211 anim.setStartDelay(mDisappearingDelay);
1212 anim.setDuration(mDisappearingDuration);
Chet Haase33d08762013-10-10 11:11:37 -07001213 if (mDisappearingInterpolator != sDisappearingInterpolator) {
1214 anim.setInterpolator(mDisappearingInterpolator);
1215 }
Chet Haase21cd1382010-09-01 17:42:29 -07001216 anim.setTarget(child);
Chet Haasea5531132012-02-02 13:41:44 -08001217 final float preAnimAlpha = child.getAlpha();
1218 anim.addListener(new AnimatorListenerAdapter() {
1219 @Override
1220 public void onAnimationEnd(Animator anim) {
1221 currentDisappearingAnimations.remove(child);
1222 child.setAlpha(preAnimAlpha);
Luca Zanolinc20fc8d2012-06-19 18:15:29 +01001223 if (hasListeners()) {
1224 ArrayList<TransitionListener> listeners =
1225 (ArrayList<TransitionListener>) mListeners.clone();
1226 for (TransitionListener listener : listeners) {
Chet Haase21cd1382010-09-01 17:42:29 -07001227 listener.endTransition(LayoutTransition.this, parent, child, DISAPPEARING);
1228 }
1229 }
Chet Haasea5531132012-02-02 13:41:44 -08001230 }
1231 });
Chet Haasea18a86b2010-09-07 13:20:00 -07001232 if (anim instanceof ObjectAnimator) {
1233 ((ObjectAnimator) anim).setCurrentPlayTime(0);
Chet Haase21cd1382010-09-01 17:42:29 -07001234 }
Chet Haasee8e45d32011-03-02 17:07:35 -08001235 currentDisappearingAnimations.put(child, anim);
Chet Haase21cd1382010-09-01 17:42:29 -07001236 anim.start();
1237 }
1238
1239 /**
1240 * This method is called by ViewGroup when a child view is about to be added to the
1241 * container. This callback starts the process of a transition; we grab the starting
1242 * values, listen for changes to all of the children of the container, and start appropriate
1243 * animations.
1244 *
1245 * @param parent The ViewGroup to which the View is being added.
1246 * @param child The View being added to the ViewGroup.
Chet Haase0d299362012-01-26 10:51:48 -08001247 * @param changesLayout Whether the removal will cause changes in the layout of other views
1248 * in the container. INVISIBLE views becoming VISIBLE will not cause changes and thus will not
1249 * affect CHANGE_APPEARING or CHANGE_DISAPPEARING animations.
Chet Haase21cd1382010-09-01 17:42:29 -07001250 */
Chet Haase0d299362012-01-26 10:51:48 -08001251 private void addChild(ViewGroup parent, View child, boolean changesLayout) {
Chet Haaseab3a7762012-05-23 13:28:22 -07001252 if (parent.getWindowVisibility() != View.VISIBLE) {
1253 return;
1254 }
Chet Haase7dd4a532012-04-16 13:35:09 -07001255 if ((mTransitionTypes & FLAG_APPEARING) == FLAG_APPEARING) {
1256 // Want disappearing animations to finish up before proceeding
1257 cancel(DISAPPEARING);
1258 }
1259 if (changesLayout && (mTransitionTypes & FLAG_CHANGE_APPEARING) == FLAG_CHANGE_APPEARING) {
Chet Haase0d299362012-01-26 10:51:48 -08001260 // Also, cancel changing animations so that we start fresh ones from current locations
1261 cancel(CHANGE_APPEARING);
Chet Haase7dd4a532012-04-16 13:35:09 -07001262 cancel(CHANGING);
Chet Haase0d299362012-01-26 10:51:48 -08001263 }
Luca Zanolinc20fc8d2012-06-19 18:15:29 +01001264 if (hasListeners() && (mTransitionTypes & FLAG_APPEARING) == FLAG_APPEARING) {
1265 ArrayList<TransitionListener> listeners =
1266 (ArrayList<TransitionListener>) mListeners.clone();
1267 for (TransitionListener listener : listeners) {
Chet Haase21cd1382010-09-01 17:42:29 -07001268 listener.startTransition(this, parent, child, APPEARING);
1269 }
1270 }
Chet Haase7dd4a532012-04-16 13:35:09 -07001271 if (changesLayout && (mTransitionTypes & FLAG_CHANGE_APPEARING) == FLAG_CHANGE_APPEARING) {
Chet Haase0d299362012-01-26 10:51:48 -08001272 runChangeTransition(parent, child, APPEARING);
1273 }
Chet Haase7dd4a532012-04-16 13:35:09 -07001274 if ((mTransitionTypes & FLAG_APPEARING) == FLAG_APPEARING) {
1275 runAppearingTransition(parent, child);
1276 }
1277 }
1278
Luca Zanolinc20fc8d2012-06-19 18:15:29 +01001279 private boolean hasListeners() {
1280 return mListeners != null && mListeners.size() > 0;
1281 }
1282
Chet Haase7dd4a532012-04-16 13:35:09 -07001283 /**
1284 * This method is called by ViewGroup when there is a call to layout() on the container
1285 * with this LayoutTransition. If the CHANGING transition is enabled and if there is no other
1286 * transition currently running on the container, then this call runs a CHANGING transition.
1287 * The transition does not start immediately; it just sets up the mechanism to run if any
1288 * of the children of the container change their layout parameters (similar to
1289 * the CHANGE_APPEARING and CHANGE_DISAPPEARING transitions).
1290 *
1291 * @param parent The ViewGroup whose layout() method has been called.
1292 *
1293 * @hide
1294 */
1295 public void layoutChange(ViewGroup parent) {
Chet Haaseab3a7762012-05-23 13:28:22 -07001296 if (parent.getWindowVisibility() != View.VISIBLE) {
1297 return;
1298 }
Chet Haase7dd4a532012-04-16 13:35:09 -07001299 if ((mTransitionTypes & FLAG_CHANGING) == FLAG_CHANGING && !isRunning()) {
1300 // This method is called for all calls to layout() in the container, including
1301 // those caused by add/remove/hide/show events, which will already have set up
1302 // transition animations. Avoid setting up CHANGING animations in this case; only
1303 // do so when there is not a transition already running on the container.
1304 runChangeTransition(parent, null, CHANGING);
1305 }
Chet Haase21cd1382010-09-01 17:42:29 -07001306 }
1307
1308 /**
Chet Haase5e25c2c2010-09-16 11:15:56 -07001309 * This method is called by ViewGroup when a child view is about to be added to the
1310 * container. This callback starts the process of a transition; we grab the starting
1311 * values, listen for changes to all of the children of the container, and start appropriate
1312 * animations.
1313 *
1314 * @param parent The ViewGroup to which the View is being added.
1315 * @param child The View being added to the ViewGroup.
1316 */
Chet Haase0d299362012-01-26 10:51:48 -08001317 public void addChild(ViewGroup parent, View child) {
1318 addChild(parent, child, true);
1319 }
1320
1321 /**
1322 * @deprecated Use {@link #showChild(android.view.ViewGroup, android.view.View, int)}.
1323 */
1324 @Deprecated
Chet Haase5e25c2c2010-09-16 11:15:56 -07001325 public void showChild(ViewGroup parent, View child) {
Chet Haase0d299362012-01-26 10:51:48 -08001326 addChild(parent, child, true);
1327 }
1328
1329 /**
1330 * This method is called by ViewGroup when a child view is about to be made visible in the
1331 * container. This callback starts the process of a transition; we grab the starting
1332 * values, listen for changes to all of the children of the container, and start appropriate
1333 * animations.
1334 *
1335 * @param parent The ViewGroup in which the View is being made visible.
1336 * @param child The View being made visible.
1337 * @param oldVisibility The previous visibility value of the child View, either
1338 * {@link View#GONE} or {@link View#INVISIBLE}.
1339 */
1340 public void showChild(ViewGroup parent, View child, int oldVisibility) {
1341 addChild(parent, child, oldVisibility == View.GONE);
Chet Haase5e25c2c2010-09-16 11:15:56 -07001342 }
1343
1344 /**
Chet Haase21cd1382010-09-01 17:42:29 -07001345 * This method is called by ViewGroup when a child view is about to be removed from the
1346 * container. This callback starts the process of a transition; we grab the starting
1347 * values, listen for changes to all of the children of the container, and start appropriate
1348 * animations.
1349 *
1350 * @param parent The ViewGroup from which the View is being removed.
1351 * @param child The View being removed from the ViewGroup.
Chet Haase0d299362012-01-26 10:51:48 -08001352 * @param changesLayout Whether the removal will cause changes in the layout of other views
1353 * in the container. Views becoming INVISIBLE will not cause changes and thus will not
1354 * affect CHANGE_APPEARING or CHANGE_DISAPPEARING animations.
Chet Haase21cd1382010-09-01 17:42:29 -07001355 */
Chet Haase0d299362012-01-26 10:51:48 -08001356 private void removeChild(ViewGroup parent, View child, boolean changesLayout) {
Chet Haaseab3a7762012-05-23 13:28:22 -07001357 if (parent.getWindowVisibility() != View.VISIBLE) {
1358 return;
1359 }
Chet Haase7dd4a532012-04-16 13:35:09 -07001360 if ((mTransitionTypes & FLAG_DISAPPEARING) == FLAG_DISAPPEARING) {
1361 // Want appearing animations to finish up before proceeding
1362 cancel(APPEARING);
1363 }
1364 if (changesLayout &&
1365 (mTransitionTypes & FLAG_CHANGE_DISAPPEARING) == FLAG_CHANGE_DISAPPEARING) {
Chet Haase0d299362012-01-26 10:51:48 -08001366 // Also, cancel changing animations so that we start fresh ones from current locations
1367 cancel(CHANGE_DISAPPEARING);
Chet Haase7dd4a532012-04-16 13:35:09 -07001368 cancel(CHANGING);
Chet Haase0d299362012-01-26 10:51:48 -08001369 }
Luca Zanolinc20fc8d2012-06-19 18:15:29 +01001370 if (hasListeners() && (mTransitionTypes & FLAG_DISAPPEARING) == FLAG_DISAPPEARING) {
1371 ArrayList<TransitionListener> listeners = (ArrayList<TransitionListener>) mListeners
1372 .clone();
1373 for (TransitionListener listener : listeners) {
Chet Haase21cd1382010-09-01 17:42:29 -07001374 listener.startTransition(this, parent, child, DISAPPEARING);
1375 }
1376 }
Chet Haase7dd4a532012-04-16 13:35:09 -07001377 if (changesLayout &&
1378 (mTransitionTypes & FLAG_CHANGE_DISAPPEARING) == FLAG_CHANGE_DISAPPEARING) {
Chet Haase0d299362012-01-26 10:51:48 -08001379 runChangeTransition(parent, child, DISAPPEARING);
1380 }
Chet Haase7dd4a532012-04-16 13:35:09 -07001381 if ((mTransitionTypes & FLAG_DISAPPEARING) == FLAG_DISAPPEARING) {
1382 runDisappearingTransition(parent, child);
1383 }
Chet Haase21cd1382010-09-01 17:42:29 -07001384 }
1385
1386 /**
Chet Haase5e25c2c2010-09-16 11:15:56 -07001387 * This method is called by ViewGroup when a child view is about to be removed from the
1388 * container. This callback starts the process of a transition; we grab the starting
1389 * values, listen for changes to all of the children of the container, and start appropriate
1390 * animations.
1391 *
1392 * @param parent The ViewGroup from which the View is being removed.
1393 * @param child The View being removed from the ViewGroup.
1394 */
Chet Haase0d299362012-01-26 10:51:48 -08001395 public void removeChild(ViewGroup parent, View child) {
1396 removeChild(parent, child, true);
1397 }
1398
1399 /**
1400 * @deprecated Use {@link #hideChild(android.view.ViewGroup, android.view.View, int)}.
1401 */
1402 @Deprecated
Chet Haase5e25c2c2010-09-16 11:15:56 -07001403 public void hideChild(ViewGroup parent, View child) {
Chet Haase0d299362012-01-26 10:51:48 -08001404 removeChild(parent, child, true);
1405 }
1406
1407 /**
1408 * This method is called by ViewGroup when a child view is about to be hidden in
1409 * container. This callback starts the process of a transition; we grab the starting
1410 * values, listen for changes to all of the children of the container, and start appropriate
1411 * animations.
1412 *
1413 * @param parent The parent ViewGroup of the View being hidden.
1414 * @param child The View being hidden.
1415 * @param newVisibility The new visibility value of the child View, either
1416 * {@link View#GONE} or {@link View#INVISIBLE}.
1417 */
1418 public void hideChild(ViewGroup parent, View child, int newVisibility) {
1419 removeChild(parent, child, newVisibility == View.GONE);
Chet Haase5e25c2c2010-09-16 11:15:56 -07001420 }
1421
1422 /**
Chet Haase21cd1382010-09-01 17:42:29 -07001423 * Add a listener that will be called when the bounds of the view change due to
1424 * layout processing.
1425 *
1426 * @param listener The listener that will be called when layout bounds change.
1427 */
1428 public void addTransitionListener(TransitionListener listener) {
1429 if (mListeners == null) {
1430 mListeners = new ArrayList<TransitionListener>();
1431 }
1432 mListeners.add(listener);
1433 }
1434
1435 /**
1436 * Remove a listener for layout changes.
1437 *
1438 * @param listener The listener for layout bounds change.
1439 */
1440 public void removeTransitionListener(TransitionListener listener) {
1441 if (mListeners == null) {
1442 return;
1443 }
1444 mListeners.remove(listener);
1445 }
1446
1447 /**
1448 * Gets the current list of listeners for layout changes.
1449 * @return
1450 */
1451 public List<TransitionListener> getTransitionListeners() {
1452 return mListeners;
1453 }
1454
1455 /**
1456 * This interface is used for listening to starting and ending events for transitions.
1457 */
1458 public interface TransitionListener {
1459
1460 /**
Chet Haase9c087442011-01-12 16:20:16 -08001461 * This event is sent to listeners when any type of transition animation begins.
Chet Haase21cd1382010-09-01 17:42:29 -07001462 *
1463 * @param transition The LayoutTransition sending out the event.
1464 * @param container The ViewGroup on which the transition is playing.
Chet Haase9c087442011-01-12 16:20:16 -08001465 * @param view The View object being affected by the transition animation.
1466 * @param transitionType The type of transition that is beginning,
1467 * {@link android.animation.LayoutTransition#APPEARING},
1468 * {@link android.animation.LayoutTransition#DISAPPEARING},
1469 * {@link android.animation.LayoutTransition#CHANGE_APPEARING}, or
1470 * {@link android.animation.LayoutTransition#CHANGE_DISAPPEARING}.
Chet Haase21cd1382010-09-01 17:42:29 -07001471 */
1472 public void startTransition(LayoutTransition transition, ViewGroup container,
1473 View view, int transitionType);
1474
1475 /**
Chet Haase9c087442011-01-12 16:20:16 -08001476 * This event is sent to listeners when any type of transition animation ends.
Chet Haase21cd1382010-09-01 17:42:29 -07001477 *
1478 * @param transition The LayoutTransition sending out the event.
1479 * @param container The ViewGroup on which the transition is playing.
Chet Haase9c087442011-01-12 16:20:16 -08001480 * @param view The View object being affected by the transition animation.
1481 * @param transitionType The type of transition that is ending,
1482 * {@link android.animation.LayoutTransition#APPEARING},
1483 * {@link android.animation.LayoutTransition#DISAPPEARING},
1484 * {@link android.animation.LayoutTransition#CHANGE_APPEARING}, or
1485 * {@link android.animation.LayoutTransition#CHANGE_DISAPPEARING}.
Chet Haase21cd1382010-09-01 17:42:29 -07001486 */
1487 public void endTransition(LayoutTransition transition, ViewGroup container,
1488 View view, int transitionType);
1489 }
1490
Chet Haase06985942015-05-05 16:43:24 -07001491 /**
1492 * Utility class to clean up listeners after animations are setup. Cleanup happens
1493 * when either the OnPreDrawListener method is called or when the parent is detached,
1494 * whichever comes first.
1495 */
1496 private static final class CleanupCallback implements ViewTreeObserver.OnPreDrawListener,
1497 View.OnAttachStateChangeListener {
1498
1499 final Map<View, View.OnLayoutChangeListener> layoutChangeListenerMap;
1500 final ViewGroup parent;
1501
1502 CleanupCallback(Map<View, View.OnLayoutChangeListener> listenerMap, ViewGroup parent) {
1503 this.layoutChangeListenerMap = listenerMap;
1504 this.parent = parent;
1505 }
1506
1507 private void cleanup() {
1508 parent.getViewTreeObserver().removeOnPreDrawListener(this);
1509 parent.removeOnAttachStateChangeListener(this);
1510 int count = layoutChangeListenerMap.size();
1511 if (count > 0) {
1512 Collection<View> views = layoutChangeListenerMap.keySet();
1513 for (View view : views) {
1514 View.OnLayoutChangeListener listener = layoutChangeListenerMap.get(view);
1515 view.removeOnLayoutChangeListener(listener);
1516 }
1517 layoutChangeListenerMap.clear();
1518 }
1519 }
1520
1521 @Override
1522 public void onViewAttachedToWindow(View v) {
1523 }
1524
1525 @Override
1526 public void onViewDetachedFromWindow(View v) {
1527 cleanup();
1528 }
1529
1530 @Override
1531 public boolean onPreDraw() {
1532 cleanup();
1533 return true;
1534 }
1535 };
1536
Chet Haaseabb7d662011-07-18 13:07:34 -07001537}