blob: 5b3813d9c3af330ecb7ed6792bc0cc91edcd7feb [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
Mathew Inwood61beb332018-08-01 14:42:25 +010019import android.annotation.UnsupportedAppUsage;
Chet Haase21cd1382010-09-01 17:42:29 -070020import android.view.View;
21import android.view.ViewGroup;
Chet Haasecca2c982011-05-20 14:34:18 -070022import android.view.ViewParent;
Chet Haase21cd1382010-09-01 17:42:29 -070023import android.view.ViewTreeObserver;
24import android.view.animation.AccelerateDecelerateInterpolator;
25import android.view.animation.DecelerateInterpolator;
Chet Haase21cd1382010-09-01 17:42:29 -070026
27import java.util.ArrayList;
Chet Haase1a76dcd2011-10-06 11:16:40 -070028import java.util.Collection;
Chet Haase21cd1382010-09-01 17:42:29 -070029import java.util.HashMap;
Chet Haaseeb1d8512011-07-18 12:12:36 -070030import java.util.LinkedHashMap;
Chet Haase21cd1382010-09-01 17:42:29 -070031import java.util.List;
Chet Haase06985942015-05-05 16:43:24 -070032import java.util.Map;
Chet Haase21cd1382010-09-01 17:42:29 -070033
34/**
35 * This class enables automatic animations on layout changes in ViewGroup objects. To enable
36 * transitions for a layout container, create a LayoutTransition object and set it on any
37 * ViewGroup by calling {@link ViewGroup#setLayoutTransition(LayoutTransition)}. This will cause
38 * default animations to run whenever items are added to or removed from that container. To specify
Chet Haasea18a86b2010-09-07 13:20:00 -070039 * custom animations, use the {@link LayoutTransition#setAnimator(int, Animator)
40 * setAnimator()} method.
Chet Haase21cd1382010-09-01 17:42:29 -070041 *
Chet Haasedaf98e92011-01-10 14:10:36 -080042 * <p>One of the core concepts of these transition animations is that there are two types of
Chet Haase21cd1382010-09-01 17:42:29 -070043 * changes that cause the transition and four different animations that run because of
44 * those changes. The changes that trigger the transition are items being added to a container
45 * (referred to as an "appearing" transition) or removed from a container (also known as
Chet Haasedaf98e92011-01-10 14:10:36 -080046 * "disappearing"). Setting the visibility of views (between GONE and VISIBLE) will trigger
47 * the same add/remove logic. The animations that run due to those events are one that animates
Chet Haase21cd1382010-09-01 17:42:29 -070048 * items being added, one that animates items being removed, and two that animate the other
49 * items in the container that change due to the add/remove occurrence. Users of
50 * the transition may want different animations for the changing items depending on whether
Chet Haasedaf98e92011-01-10 14:10:36 -080051 * they are changing due to an appearing or disappearing event, so there is one animation for
Chet Haase21cd1382010-09-01 17:42:29 -070052 * each of these variations of the changing event. Most of the API of this class is concerned
53 * with setting up the basic properties of the animations used in these four situations,
54 * or with setting up custom animations for any or all of the four.</p>
55 *
Chet Haasec54ed962011-05-06 14:13:05 -070056 * <p>By default, the DISAPPEARING animation begins immediately, as does the CHANGE_APPEARING
57 * animation. The other animations begin after a delay that is set to the default duration
58 * of the animations. This behavior facilitates a sequence of animations in transitions as
59 * follows: when an item is being added to a layout, the other children of that container will
60 * move first (thus creating space for the new item), then the appearing animation will run to
61 * animate the item being added. Conversely, when an item is removed from a container, the
62 * animation to remove it will run first, then the animations of the other children in the
63 * layout will run (closing the gap created in the layout when the item was removed). If this
64 * default choreography behavior is not desired, the {@link #setDuration(int, long)} and
65 * {@link #setStartDelay(int, long)} of any or all of the animations can be changed as
Kevin Hufnagle52323292016-09-19 12:02:33 -070066 * appropriate. Keep in mind, however, that if you start an APPEARING animation before a
67 * DISAPPEARING animation is completed, the DISAPPEARING animation stops, and any effects from
68 * the DISAPPEARING animation are reverted. If you instead start a DISAPPEARING animation
69 * before an APPEARING animation is completed, a similar set of effects occurs for the
70 * APPEARING animation.</p>
Chet Haasec54ed962011-05-06 14:13:05 -070071 *
Chet Haase21cd1382010-09-01 17:42:29 -070072 * <p>The animations specified for the transition, both the defaults and any custom animations
73 * set on the transition object, are templates only. That is, these animations exist to hold the
74 * basic animation properties, such as the duration, start delay, and properties being animated.
75 * But the actual target object, as well as the start and end values for those properties, are
76 * set automatically in the process of setting up the transition each time it runs. Each of the
77 * animations is cloned from the original copy and the clone is then populated with the dynamic
78 * values of the target being animated (such as one of the items in a layout container that is
79 * moving as a result of the layout event) as well as the values that are changing (such as the
80 * position and size of that object). The actual values that are pushed to each animation
81 * depends on what properties are specified for the animation. For example, the default
Chet Haasecca2c982011-05-20 14:34:18 -070082 * CHANGE_APPEARING animation animates the <code>left</code>, <code>top</code>, <code>right</code>,
83 * <code>bottom</code>, <code>scrollX</code>, and <code>scrollY</code> properties.
84 * Values for these properties are updated with the pre- and post-layout
Chet Haase21cd1382010-09-01 17:42:29 -070085 * values when the transition begins. Custom animations will be similarly populated with
Chet Haasea18a86b2010-09-07 13:20:00 -070086 * the target and values being animated, assuming they use ObjectAnimator objects with
Chet Haase21cd1382010-09-01 17:42:29 -070087 * property names that are known on the target object.</p>
Chet Haasedaf98e92011-01-10 14:10:36 -080088 *
89 * <p>This class, and the associated XML flag for containers, animateLayoutChanges="true",
90 * provides a simple utility meant for automating changes in straightforward situations.
91 * Using LayoutTransition at multiple levels of a nested view hierarchy may not work due to the
92 * interrelationship of the various levels of layout. Also, a container that is being scrolled
93 * at the same time as items are being added or removed is probably not a good candidate for
94 * this utility, because the before/after locations calculated by LayoutTransition
95 * may not match the actual locations when the animations finish due to the container
96 * being scrolled as the animations are running. You can work around that
97 * particular issue by disabling the 'changing' animations by setting the CHANGE_APPEARING
98 * and CHANGE_DISAPPEARING animations to null, and setting the startDelay of the
99 * other animations appropriately.</p>
Chet Haase21cd1382010-09-01 17:42:29 -0700100 */
101public class LayoutTransition {
102
103 /**
104 * A flag indicating the animation that runs on those items that are changing
105 * due to a new item appearing in the container.
106 */
107 public static final int CHANGE_APPEARING = 0;
108
109 /**
110 * A flag indicating the animation that runs on those items that are changing
Chet Haase9e90a992011-01-04 16:23:21 -0800111 * due to an item disappearing from the container.
Chet Haase21cd1382010-09-01 17:42:29 -0700112 */
113 public static final int CHANGE_DISAPPEARING = 1;
114
115 /**
Chet Haase9e90a992011-01-04 16:23:21 -0800116 * A flag indicating the animation that runs on those items that are appearing
117 * in the container.
Chet Haase21cd1382010-09-01 17:42:29 -0700118 */
119 public static final int APPEARING = 2;
120
121 /**
Chet Haase9e90a992011-01-04 16:23:21 -0800122 * A flag indicating the animation that runs on those items that are disappearing
123 * from the container.
Chet Haase21cd1382010-09-01 17:42:29 -0700124 */
125 public static final int DISAPPEARING = 3;
126
127 /**
Chet Haase7dd4a532012-04-16 13:35:09 -0700128 * A flag indicating the animation that runs on those items that are changing
129 * due to a layout change not caused by items being added to or removed
130 * from the container. This transition type is not enabled by default; it can be
131 * enabled via {@link #enableTransitionType(int)}.
132 */
133 public static final int CHANGING = 4;
134
135 /**
136 * Private bit fields used to set the collection of enabled transition types for
137 * mTransitionTypes.
138 */
139 private static final int FLAG_APPEARING = 0x01;
140 private static final int FLAG_DISAPPEARING = 0x02;
141 private static final int FLAG_CHANGE_APPEARING = 0x04;
142 private static final int FLAG_CHANGE_DISAPPEARING = 0x08;
143 private static final int FLAG_CHANGING = 0x10;
144
145 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700146 * These variables hold the animations that are currently used to run the transition effects.
147 * These animations are set to defaults, but can be changed to custom animations by
Chet Haasea18a86b2010-09-07 13:20:00 -0700148 * calls to setAnimator().
Chet Haase21cd1382010-09-01 17:42:29 -0700149 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700150 private Animator mDisappearingAnim = null;
151 private Animator mAppearingAnim = null;
152 private Animator mChangingAppearingAnim = null;
153 private Animator mChangingDisappearingAnim = null;
Chet Haase7dd4a532012-04-16 13:35:09 -0700154 private Animator mChangingAnim = null;
Chet Haase21cd1382010-09-01 17:42:29 -0700155
156 /**
157 * These are the default animations, defined in the constructor, that will be used
158 * unless the user specifies custom animations.
159 */
Chet Haase7dd4a532012-04-16 13:35:09 -0700160 private static ObjectAnimator defaultChange;
Chet Haasea18a86b2010-09-07 13:20:00 -0700161 private static ObjectAnimator defaultChangeIn;
162 private static ObjectAnimator defaultChangeOut;
163 private static ObjectAnimator defaultFadeIn;
164 private static ObjectAnimator defaultFadeOut;
Chet Haase21cd1382010-09-01 17:42:29 -0700165
166 /**
167 * The default duration used by all animations.
168 */
169 private static long DEFAULT_DURATION = 300;
170
171 /**
Chet Haase7dd4a532012-04-16 13:35:09 -0700172 * The durations of the different animations
Chet Haase21cd1382010-09-01 17:42:29 -0700173 */
174 private long mChangingAppearingDuration = DEFAULT_DURATION;
175 private long mChangingDisappearingDuration = DEFAULT_DURATION;
Chet Haase7dd4a532012-04-16 13:35:09 -0700176 private long mChangingDuration = DEFAULT_DURATION;
Chet Haase21cd1382010-09-01 17:42:29 -0700177 private long mAppearingDuration = DEFAULT_DURATION;
178 private long mDisappearingDuration = DEFAULT_DURATION;
179
180 /**
Chet Haase7dd4a532012-04-16 13:35:09 -0700181 * The start delays of the different animations. Note that the default behavior of
Chet Haase21cd1382010-09-01 17:42:29 -0700182 * the appearing item is the default duration, since it should wait for the items to move
183 * before fading it. Same for the changing animation when disappearing; it waits for the item
184 * to fade out before moving the other items.
185 */
186 private long mAppearingDelay = DEFAULT_DURATION;
187 private long mDisappearingDelay = 0;
188 private long mChangingAppearingDelay = 0;
189 private long mChangingDisappearingDelay = DEFAULT_DURATION;
Chet Haase7dd4a532012-04-16 13:35:09 -0700190 private long mChangingDelay = 0;
Chet Haase21cd1382010-09-01 17:42:29 -0700191
192 /**
Chet Haase7dd4a532012-04-16 13:35:09 -0700193 * The inter-animation delays used on the changing animations
Chet Haase21cd1382010-09-01 17:42:29 -0700194 */
195 private long mChangingAppearingStagger = 0;
196 private long mChangingDisappearingStagger = 0;
Chet Haase7dd4a532012-04-16 13:35:09 -0700197 private long mChangingStagger = 0;
Chet Haase21cd1382010-09-01 17:42:29 -0700198
199 /**
Chet Haase33d08762013-10-10 11:11:37 -0700200 * Static interpolators - these are stateless and can be shared across the instances
201 */
202 private static TimeInterpolator ACCEL_DECEL_INTERPOLATOR =
203 new AccelerateDecelerateInterpolator();
204 private static TimeInterpolator DECEL_INTERPOLATOR = new DecelerateInterpolator();
205 private static TimeInterpolator sAppearingInterpolator = ACCEL_DECEL_INTERPOLATOR;
206 private static TimeInterpolator sDisappearingInterpolator = ACCEL_DECEL_INTERPOLATOR;
207 private static TimeInterpolator sChangingAppearingInterpolator = DECEL_INTERPOLATOR;
208 private static TimeInterpolator sChangingDisappearingInterpolator = DECEL_INTERPOLATOR;
209 private static TimeInterpolator sChangingInterpolator = DECEL_INTERPOLATOR;
210
211 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700212 * The default interpolators used for the animations
213 */
Chet Haase33d08762013-10-10 11:11:37 -0700214 private TimeInterpolator mAppearingInterpolator = sAppearingInterpolator;
215 private TimeInterpolator mDisappearingInterpolator = sDisappearingInterpolator;
216 private TimeInterpolator mChangingAppearingInterpolator = sChangingAppearingInterpolator;
217 private TimeInterpolator mChangingDisappearingInterpolator = sChangingDisappearingInterpolator;
218 private TimeInterpolator mChangingInterpolator = sChangingInterpolator;
Chet Haase21cd1382010-09-01 17:42:29 -0700219
220 /**
Chet Haasee64ea872010-12-13 16:15:53 -0800221 * These hashmaps are used to store the animations that are currently running as part of
Chet Haase21cd1382010-09-01 17:42:29 -0700222 * the transition. The reason for this is that a further layout event should cause
223 * existing animations to stop where they are prior to starting new animations. So
224 * we cache all of the current animations in this map for possible cancellation on
Chet Haaseeb1d8512011-07-18 12:12:36 -0700225 * another layout event. LinkedHashMaps are used to preserve the order in which animations
226 * are inserted, so that we process events (such as setting up start values) in the same order.
Chet Haase21cd1382010-09-01 17:42:29 -0700227 */
Chet Haaseeb1d8512011-07-18 12:12:36 -0700228 private final HashMap<View, Animator> pendingAnimations =
Chet Haasee8e45d32011-03-02 17:07:35 -0800229 new HashMap<View, Animator>();
Chet Haaseeb1d8512011-07-18 12:12:36 -0700230 private final LinkedHashMap<View, Animator> currentChangingAnimations =
231 new LinkedHashMap<View, Animator>();
232 private final LinkedHashMap<View, Animator> currentAppearingAnimations =
233 new LinkedHashMap<View, Animator>();
234 private final LinkedHashMap<View, Animator> currentDisappearingAnimations =
235 new LinkedHashMap<View, Animator>();
Chet Haase21cd1382010-09-01 17:42:29 -0700236
237 /**
238 * This hashmap is used to track the listeners that have been added to the children of
239 * a container. When a layout change occurs, an animation is created for each View, so that
240 * the pre-layout values can be cached in that animation. Then a listener is added to the
241 * view to see whether the layout changes the bounds of that view. If so, the animation
242 * is set with the final values and then run. If not, the animation is not started. When
243 * the process of setting up and running all appropriate animations is done, we need to
244 * remove these listeners and clear out the map.
245 */
Ben Komalocbda9102010-12-16 15:55:27 -0800246 private final HashMap<View, View.OnLayoutChangeListener> layoutChangeListenerMap =
Chet Haase21cd1382010-09-01 17:42:29 -0700247 new HashMap<View, View.OnLayoutChangeListener>();
248
249 /**
250 * Used to track the current delay being assigned to successive animations as they are
251 * started. This value is incremented for each new animation, then zeroed before the next
252 * transition begins.
253 */
254 private long staggerDelay;
255
256 /**
Chet Haase7dd4a532012-04-16 13:35:09 -0700257 * These are the types of transition animations that the LayoutTransition is reacting
258 * to. By default, appearing/disappearing and the change animations related to them are
259 * enabled (not CHANGING).
260 */
261 private int mTransitionTypes = FLAG_CHANGE_APPEARING | FLAG_CHANGE_DISAPPEARING |
262 FLAG_APPEARING | FLAG_DISAPPEARING;
263 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700264 * The set of listeners that should be notified when APPEARING/DISAPPEARING transitions
265 * start and end.
266 */
267 private ArrayList<TransitionListener> mListeners;
268
Chet Haasecca2c982011-05-20 14:34:18 -0700269 /**
270 * Controls whether changing animations automatically animate the parent hierarchy as well.
271 * This behavior prevents artifacts when wrap_content layouts snap to the end state as the
272 * transition begins, causing visual glitches and clipping.
273 * Default value is true.
274 */
275 private boolean mAnimateParentHierarchy = true;
276
Chet Haase21cd1382010-09-01 17:42:29 -0700277
278 /**
279 * Constructs a LayoutTransition object. By default, the object will listen to layout
280 * events on any ViewGroup that it is set on and will run default animations for each
281 * type of layout event.
282 */
283 public LayoutTransition() {
284 if (defaultChangeIn == null) {
285 // "left" is just a placeholder; we'll put real properties/values in when needed
Chet Haase2794eb32010-10-12 16:29:28 -0700286 PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 1);
287 PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 1);
288 PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", 0, 1);
289 PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom", 0, 1);
Chet Haasecca2c982011-05-20 14:34:18 -0700290 PropertyValuesHolder pvhScrollX = PropertyValuesHolder.ofInt("scrollX", 0, 1);
291 PropertyValuesHolder pvhScrollY = PropertyValuesHolder.ofInt("scrollY", 0, 1);
Chet Haase622e05c2011-07-15 15:24:18 -0700292 defaultChangeIn = ObjectAnimator.ofPropertyValuesHolder((Object)null,
Chet Haasecca2c982011-05-20 14:34:18 -0700293 pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScrollX, pvhScrollY);
Chet Haase2794eb32010-10-12 16:29:28 -0700294 defaultChangeIn.setDuration(DEFAULT_DURATION);
Chet Haase21cd1382010-09-01 17:42:29 -0700295 defaultChangeIn.setStartDelay(mChangingAppearingDelay);
296 defaultChangeIn.setInterpolator(mChangingAppearingInterpolator);
297 defaultChangeOut = defaultChangeIn.clone();
298 defaultChangeOut.setStartDelay(mChangingDisappearingDelay);
299 defaultChangeOut.setInterpolator(mChangingDisappearingInterpolator);
Chet Haase7dd4a532012-04-16 13:35:09 -0700300 defaultChange = defaultChangeIn.clone();
301 defaultChange.setStartDelay(mChangingDelay);
302 defaultChange.setInterpolator(mChangingInterpolator);
Chet Haasecca2c982011-05-20 14:34:18 -0700303
Chet Haase622e05c2011-07-15 15:24:18 -0700304 defaultFadeIn = ObjectAnimator.ofFloat(null, "alpha", 0f, 1f);
Chet Haase2794eb32010-10-12 16:29:28 -0700305 defaultFadeIn.setDuration(DEFAULT_DURATION);
Chet Haase21cd1382010-09-01 17:42:29 -0700306 defaultFadeIn.setStartDelay(mAppearingDelay);
307 defaultFadeIn.setInterpolator(mAppearingInterpolator);
Chet Haase622e05c2011-07-15 15:24:18 -0700308 defaultFadeOut = ObjectAnimator.ofFloat(null, "alpha", 1f, 0f);
Chet Haase2794eb32010-10-12 16:29:28 -0700309 defaultFadeOut.setDuration(DEFAULT_DURATION);
Chet Haase21cd1382010-09-01 17:42:29 -0700310 defaultFadeOut.setStartDelay(mDisappearingDelay);
311 defaultFadeOut.setInterpolator(mDisappearingInterpolator);
312 }
313 mChangingAppearingAnim = defaultChangeIn;
314 mChangingDisappearingAnim = defaultChangeOut;
Chet Haase7dd4a532012-04-16 13:35:09 -0700315 mChangingAnim = defaultChange;
Chet Haase21cd1382010-09-01 17:42:29 -0700316 mAppearingAnim = defaultFadeIn;
317 mDisappearingAnim = defaultFadeOut;
318 }
319
320 /**
321 * Sets the duration to be used by all animations of this transition object. If you want to
322 * set the duration of just one of the animations in particular, use the
323 * {@link #setDuration(int, long)} method.
324 *
325 * @param duration The length of time, in milliseconds, that the transition animations
326 * should last.
327 */
328 public void setDuration(long duration) {
329 mChangingAppearingDuration = duration;
330 mChangingDisappearingDuration = duration;
Chet Haase7dd4a532012-04-16 13:35:09 -0700331 mChangingDuration = duration;
Chet Haase21cd1382010-09-01 17:42:29 -0700332 mAppearingDuration = duration;
333 mDisappearingDuration = duration;
334 }
335
336 /**
Chet Haase7dd4a532012-04-16 13:35:09 -0700337 * Enables the specified transitionType for this LayoutTransition object.
338 * By default, a LayoutTransition listens for changes in children being
339 * added/remove/hidden/shown in the container, and runs the animations associated with
340 * those events. That is, all transition types besides {@link #CHANGING} are enabled by default.
341 * You can also enable {@link #CHANGING} animations by calling this method with the
342 * {@link #CHANGING} transitionType.
343 *
344 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
345 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}.
346 */
347 public void enableTransitionType(int transitionType) {
348 switch (transitionType) {
349 case APPEARING:
350 mTransitionTypes |= FLAG_APPEARING;
351 break;
352 case DISAPPEARING:
353 mTransitionTypes |= FLAG_DISAPPEARING;
354 break;
355 case CHANGE_APPEARING:
356 mTransitionTypes |= FLAG_CHANGE_APPEARING;
357 break;
358 case CHANGE_DISAPPEARING:
359 mTransitionTypes |= FLAG_CHANGE_DISAPPEARING;
360 break;
361 case CHANGING:
362 mTransitionTypes |= FLAG_CHANGING;
363 break;
364 }
365 }
366
367 /**
368 * Disables the specified transitionType for this LayoutTransition object.
369 * By default, all transition types except {@link #CHANGING} are enabled.
370 *
371 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
372 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}.
373 */
374 public void disableTransitionType(int transitionType) {
375 switch (transitionType) {
376 case APPEARING:
377 mTransitionTypes &= ~FLAG_APPEARING;
378 break;
379 case DISAPPEARING:
380 mTransitionTypes &= ~FLAG_DISAPPEARING;
381 break;
382 case CHANGE_APPEARING:
383 mTransitionTypes &= ~FLAG_CHANGE_APPEARING;
384 break;
385 case CHANGE_DISAPPEARING:
386 mTransitionTypes &= ~FLAG_CHANGE_DISAPPEARING;
387 break;
388 case CHANGING:
389 mTransitionTypes &= ~FLAG_CHANGING;
390 break;
391 }
392 }
393
394 /**
395 * Returns whether the specified transitionType is enabled for this LayoutTransition object.
396 * By default, all transition types except {@link #CHANGING} are enabled.
397 *
398 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
399 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}.
400 * @return true if the specified transitionType is currently enabled, false otherwise.
401 */
402 public boolean isTransitionTypeEnabled(int transitionType) {
403 switch (transitionType) {
404 case APPEARING:
405 return (mTransitionTypes & FLAG_APPEARING) == FLAG_APPEARING;
406 case DISAPPEARING:
407 return (mTransitionTypes & FLAG_DISAPPEARING) == FLAG_DISAPPEARING;
408 case CHANGE_APPEARING:
409 return (mTransitionTypes & FLAG_CHANGE_APPEARING) == FLAG_CHANGE_APPEARING;
410 case CHANGE_DISAPPEARING:
411 return (mTransitionTypes & FLAG_CHANGE_DISAPPEARING) == FLAG_CHANGE_DISAPPEARING;
412 case CHANGING:
413 return (mTransitionTypes & FLAG_CHANGING) == FLAG_CHANGING;
414 }
415 return false;
416 }
417
418 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700419 * Sets the start delay on one of the animation objects used by this transition. The
420 * <code>transitionType</code> parameter determines the animation whose start delay
421 * is being set.
422 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700423 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
424 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines
425 * the animation whose start delay is being set.
Chet Haase21cd1382010-09-01 17:42:29 -0700426 * @param delay The length of time, in milliseconds, to delay before starting the animation.
Chet Haasea18a86b2010-09-07 13:20:00 -0700427 * @see Animator#setStartDelay(long)
Chet Haase21cd1382010-09-01 17:42:29 -0700428 */
429 public void setStartDelay(int transitionType, long delay) {
430 switch (transitionType) {
431 case CHANGE_APPEARING:
432 mChangingAppearingDelay = delay;
433 break;
434 case CHANGE_DISAPPEARING:
435 mChangingDisappearingDelay = delay;
436 break;
Chet Haase7dd4a532012-04-16 13:35:09 -0700437 case CHANGING:
438 mChangingDelay = delay;
439 break;
Chet Haase21cd1382010-09-01 17:42:29 -0700440 case APPEARING:
441 mAppearingDelay = delay;
442 break;
443 case DISAPPEARING:
444 mDisappearingDelay = delay;
445 break;
446 }
447 }
448
449 /**
450 * Gets the start delay on one of the animation objects used by this transition. The
451 * <code>transitionType</code> parameter determines the animation whose start delay
452 * is returned.
453 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700454 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
455 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines
456 * the animation whose start delay is returned.
Chet Haase21cd1382010-09-01 17:42:29 -0700457 * @return long The start delay of the specified animation.
Chet Haasea18a86b2010-09-07 13:20:00 -0700458 * @see Animator#getStartDelay()
Chet Haase21cd1382010-09-01 17:42:29 -0700459 */
460 public long getStartDelay(int transitionType) {
461 switch (transitionType) {
462 case CHANGE_APPEARING:
Rajdeep Duacf06d732012-01-02 08:45:48 +0530463 return mChangingAppearingDelay;
Chet Haase21cd1382010-09-01 17:42:29 -0700464 case CHANGE_DISAPPEARING:
Rajdeep Duacf06d732012-01-02 08:45:48 +0530465 return mChangingDisappearingDelay;
Chet Haase7dd4a532012-04-16 13:35:09 -0700466 case CHANGING:
467 return mChangingDelay;
Chet Haase21cd1382010-09-01 17:42:29 -0700468 case APPEARING:
Rajdeep Duacf06d732012-01-02 08:45:48 +0530469 return mAppearingDelay;
Chet Haase21cd1382010-09-01 17:42:29 -0700470 case DISAPPEARING:
Rajdeep Duacf06d732012-01-02 08:45:48 +0530471 return mDisappearingDelay;
Chet Haase21cd1382010-09-01 17:42:29 -0700472 }
473 // shouldn't reach here
474 return 0;
475 }
476
477 /**
478 * Sets the duration on one of the animation objects used by this transition. The
479 * <code>transitionType</code> parameter determines the animation whose duration
480 * is being set.
481 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700482 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
483 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines
484 * the animation whose duration is being set.
Chet Haase21cd1382010-09-01 17:42:29 -0700485 * @param duration The length of time, in milliseconds, that the specified animation should run.
Chet Haasea18a86b2010-09-07 13:20:00 -0700486 * @see Animator#setDuration(long)
Chet Haase21cd1382010-09-01 17:42:29 -0700487 */
488 public void setDuration(int transitionType, long duration) {
489 switch (transitionType) {
490 case CHANGE_APPEARING:
491 mChangingAppearingDuration = duration;
492 break;
493 case CHANGE_DISAPPEARING:
494 mChangingDisappearingDuration = duration;
495 break;
Chet Haase7dd4a532012-04-16 13:35:09 -0700496 case CHANGING:
497 mChangingDuration = duration;
498 break;
Chet Haase21cd1382010-09-01 17:42:29 -0700499 case APPEARING:
500 mAppearingDuration = duration;
501 break;
502 case DISAPPEARING:
503 mDisappearingDuration = duration;
504 break;
505 }
506 }
507
508 /**
509 * Gets the duration on one of the animation objects used by this transition. The
510 * <code>transitionType</code> parameter determines the animation whose duration
511 * is returned.
512 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700513 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
514 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines
515 * the animation whose duration is returned.
Chet Haase21cd1382010-09-01 17:42:29 -0700516 * @return long The duration of the specified animation.
Chet Haasea18a86b2010-09-07 13:20:00 -0700517 * @see Animator#getDuration()
Chet Haase21cd1382010-09-01 17:42:29 -0700518 */
519 public long getDuration(int transitionType) {
520 switch (transitionType) {
521 case CHANGE_APPEARING:
522 return mChangingAppearingDuration;
523 case CHANGE_DISAPPEARING:
524 return mChangingDisappearingDuration;
Chet Haase7dd4a532012-04-16 13:35:09 -0700525 case CHANGING:
526 return mChangingDuration;
Chet Haase21cd1382010-09-01 17:42:29 -0700527 case APPEARING:
528 return mAppearingDuration;
529 case DISAPPEARING:
530 return mDisappearingDuration;
531 }
532 // shouldn't reach here
533 return 0;
534 }
535
536 /**
537 * Sets the length of time to delay between starting each animation during one of the
Chet Haase7dd4a532012-04-16 13:35:09 -0700538 * change animations.
Chet Haase21cd1382010-09-01 17:42:29 -0700539 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700540 * @param transitionType A value of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING}, or
541 * {@link #CHANGING}.
Chet Haase21cd1382010-09-01 17:42:29 -0700542 * @param duration The length of time, in milliseconds, to delay before launching the next
543 * animation in the sequence.
544 */
545 public void setStagger(int transitionType, long duration) {
546 switch (transitionType) {
547 case CHANGE_APPEARING:
548 mChangingAppearingStagger = duration;
549 break;
550 case CHANGE_DISAPPEARING:
551 mChangingDisappearingStagger = duration;
552 break;
Chet Haase7dd4a532012-04-16 13:35:09 -0700553 case CHANGING:
554 mChangingStagger = duration;
555 break;
Chet Haase21cd1382010-09-01 17:42:29 -0700556 // noop other cases
557 }
558 }
559
560 /**
Chet Haase7dd4a532012-04-16 13:35:09 -0700561 * Gets the length of time to delay between starting each animation during one of the
562 * change animations.
Chet Haase21cd1382010-09-01 17:42:29 -0700563 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700564 * @param transitionType A value of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING}, or
565 * {@link #CHANGING}.
Chet Haase21cd1382010-09-01 17:42:29 -0700566 * @return long The length of time, in milliseconds, to delay before launching the next
567 * animation in the sequence.
568 */
569 public long getStagger(int transitionType) {
570 switch (transitionType) {
571 case CHANGE_APPEARING:
572 return mChangingAppearingStagger;
573 case CHANGE_DISAPPEARING:
574 return mChangingDisappearingStagger;
Chet Haase7dd4a532012-04-16 13:35:09 -0700575 case CHANGING:
576 return mChangingStagger;
Chet Haase21cd1382010-09-01 17:42:29 -0700577 }
578 // shouldn't reach here
579 return 0;
580 }
581
582 /**
583 * Sets the interpolator on one of the animation objects used by this transition. The
584 * <code>transitionType</code> parameter determines the animation whose interpolator
585 * is being set.
586 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700587 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
588 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines
589 * the animation whose interpolator is being set.
Chet Haase21cd1382010-09-01 17:42:29 -0700590 * @param interpolator The interpolator that the specified animation should use.
Chet Haasee0ee2e92010-10-07 09:06:18 -0700591 * @see Animator#setInterpolator(TimeInterpolator)
Chet Haase21cd1382010-09-01 17:42:29 -0700592 */
Chet Haasee0ee2e92010-10-07 09:06:18 -0700593 public void setInterpolator(int transitionType, TimeInterpolator interpolator) {
Chet Haase21cd1382010-09-01 17:42:29 -0700594 switch (transitionType) {
595 case CHANGE_APPEARING:
596 mChangingAppearingInterpolator = interpolator;
597 break;
598 case CHANGE_DISAPPEARING:
599 mChangingDisappearingInterpolator = interpolator;
600 break;
Chet Haase7dd4a532012-04-16 13:35:09 -0700601 case CHANGING:
602 mChangingInterpolator = interpolator;
603 break;
Chet Haase21cd1382010-09-01 17:42:29 -0700604 case APPEARING:
605 mAppearingInterpolator = interpolator;
606 break;
607 case DISAPPEARING:
608 mDisappearingInterpolator = interpolator;
609 break;
610 }
611 }
612
613 /**
614 * Gets the interpolator on one of the animation objects used by this transition. The
615 * <code>transitionType</code> parameter determines the animation whose interpolator
616 * is returned.
617 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700618 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
619 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines
620 * the animation whose interpolator is being returned.
Chet Haasee0ee2e92010-10-07 09:06:18 -0700621 * @return TimeInterpolator The interpolator that the specified animation uses.
622 * @see Animator#setInterpolator(TimeInterpolator)
Chet Haase21cd1382010-09-01 17:42:29 -0700623 */
Chet Haasee0ee2e92010-10-07 09:06:18 -0700624 public TimeInterpolator getInterpolator(int transitionType) {
Chet Haase21cd1382010-09-01 17:42:29 -0700625 switch (transitionType) {
626 case CHANGE_APPEARING:
627 return mChangingAppearingInterpolator;
628 case CHANGE_DISAPPEARING:
629 return mChangingDisappearingInterpolator;
Chet Haase7dd4a532012-04-16 13:35:09 -0700630 case CHANGING:
631 return mChangingInterpolator;
Chet Haase21cd1382010-09-01 17:42:29 -0700632 case APPEARING:
633 return mAppearingInterpolator;
634 case DISAPPEARING:
635 return mDisappearingInterpolator;
636 }
637 // shouldn't reach here
638 return null;
639 }
640
641 /**
642 * Sets the animation used during one of the transition types that may run. Any
Chet Haasea18a86b2010-09-07 13:20:00 -0700643 * Animator object can be used, but to be most useful in the context of layout
644 * transitions, the animation should either be a ObjectAnimator or a AnimatorSet
645 * of animations including PropertyAnimators. Also, these ObjectAnimator objects
Chet Haase21cd1382010-09-01 17:42:29 -0700646 * should be able to get and set values on their target objects automatically. For
Chet Haasea18a86b2010-09-07 13:20:00 -0700647 * example, a ObjectAnimator that animates the property "left" is able to set and get the
Chet Haase21cd1382010-09-01 17:42:29 -0700648 * <code>left</code> property from the View objects being animated by the layout
649 * transition. The transition works by setting target objects and properties
650 * dynamically, according to the pre- and post-layoout values of those objects, so
651 * having animations that can handle those properties appropriately will work best
652 * for custom animation. The dynamic setting of values is only the case for the
653 * CHANGE animations; the APPEARING and DISAPPEARING animations are simply run with
654 * the values they have.
655 *
656 * <p>It is also worth noting that any and all animations (and their underlying
657 * PropertyValuesHolder objects) will have their start and end values set according
658 * to the pre- and post-layout values. So, for example, a custom animation on "alpha"
659 * as the CHANGE_APPEARING animation will inherit the real value of alpha on the target
660 * object (presumably 1) as its starting and ending value when the animation begins.
661 * Animations which need to use values at the beginning and end that may not match the
662 * values queried when the transition begins may need to use a different mechanism
Chet Haasea18a86b2010-09-07 13:20:00 -0700663 * than a standard ObjectAnimator object.</p>
Chet Haase21cd1382010-09-01 17:42:29 -0700664 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700665 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
666 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the
667 * animation whose animator is being set.
Chet Haase5d6d7b92010-10-04 16:47:19 -0700668 * @param animator The animation being assigned. A value of <code>null</code> means that no
669 * animation will be run for the specified transitionType.
Chet Haase21cd1382010-09-01 17:42:29 -0700670 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700671 public void setAnimator(int transitionType, Animator animator) {
Chet Haase21cd1382010-09-01 17:42:29 -0700672 switch (transitionType) {
673 case CHANGE_APPEARING:
Chet Haase5d6d7b92010-10-04 16:47:19 -0700674 mChangingAppearingAnim = animator;
Chet Haase21cd1382010-09-01 17:42:29 -0700675 break;
676 case CHANGE_DISAPPEARING:
Chet Haase5d6d7b92010-10-04 16:47:19 -0700677 mChangingDisappearingAnim = animator;
Chet Haase21cd1382010-09-01 17:42:29 -0700678 break;
Chet Haase7dd4a532012-04-16 13:35:09 -0700679 case CHANGING:
680 mChangingAnim = animator;
681 break;
Chet Haase21cd1382010-09-01 17:42:29 -0700682 case APPEARING:
Chet Haase5d6d7b92010-10-04 16:47:19 -0700683 mAppearingAnim = animator;
Chet Haase21cd1382010-09-01 17:42:29 -0700684 break;
685 case DISAPPEARING:
Chet Haase5d6d7b92010-10-04 16:47:19 -0700686 mDisappearingAnim = animator;
Chet Haase21cd1382010-09-01 17:42:29 -0700687 break;
688 }
689 }
690
691 /**
692 * Gets the animation used during one of the transition types that may run.
693 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700694 * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
695 * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines
696 * the animation whose animator is being returned.
Chet Haasea18a86b2010-09-07 13:20:00 -0700697 * @return Animator The animation being used for the given transition type.
698 * @see #setAnimator(int, Animator)
Chet Haase21cd1382010-09-01 17:42:29 -0700699 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700700 public Animator getAnimator(int transitionType) {
Chet Haase21cd1382010-09-01 17:42:29 -0700701 switch (transitionType) {
702 case CHANGE_APPEARING:
703 return mChangingAppearingAnim;
704 case CHANGE_DISAPPEARING:
705 return mChangingDisappearingAnim;
Chet Haase7dd4a532012-04-16 13:35:09 -0700706 case CHANGING:
707 return mChangingAnim;
Chet Haase21cd1382010-09-01 17:42:29 -0700708 case APPEARING:
709 return mAppearingAnim;
710 case DISAPPEARING:
711 return mDisappearingAnim;
712 }
713 // shouldn't reach here
714 return null;
715 }
716
717 /**
Chet Haaseeb1d8512011-07-18 12:12:36 -0700718 * This function sets up animations on all of the views that change during layout.
Chet Haase21cd1382010-09-01 17:42:29 -0700719 * For every child in the parent, we create a change animation of the appropriate
Chet Haase7dd4a532012-04-16 13:35:09 -0700720 * type (appearing, disappearing, or changing) and ask it to populate its start values from its
Chet Haase21cd1382010-09-01 17:42:29 -0700721 * target view. We add layout listeners to all child views and listen for changes. For
722 * those views that change, we populate the end values for those animations and start them.
723 * Animations are not run on unchanging views.
724 *
Chet Haase7dd4a532012-04-16 13:35:09 -0700725 * @param parent The container which is undergoing a change.
726 * @param newView The view being added to or removed from the parent. May be null if the
727 * changeReason is CHANGING.
728 * @param changeReason A value of APPEARING, DISAPPEARING, or CHANGING, indicating whether the
729 * transition is occurring because an item is being added to or removed from the parent, or
730 * if it is running in response to a layout operation (that is, if the value is CHANGING).
Chet Haase21cd1382010-09-01 17:42:29 -0700731 */
732 private void runChangeTransition(final ViewGroup parent, View newView, final int changeReason) {
Chet Haase5d6d7b92010-10-04 16:47:19 -0700733
Chet Haase7dd4a532012-04-16 13:35:09 -0700734 Animator baseAnimator = null;
735 Animator parentAnimator = null;
736 final long duration;
737 switch (changeReason) {
738 case APPEARING:
739 baseAnimator = mChangingAppearingAnim;
740 duration = mChangingAppearingDuration;
741 parentAnimator = defaultChangeIn;
742 break;
743 case DISAPPEARING:
744 baseAnimator = mChangingDisappearingAnim;
745 duration = mChangingDisappearingDuration;
746 parentAnimator = defaultChangeOut;
747 break;
748 case CHANGING:
749 baseAnimator = mChangingAnim;
750 duration = mChangingDuration;
751 parentAnimator = defaultChange;
752 break;
753 default:
754 // Shouldn't reach here
755 duration = 0;
756 break;
757 }
Chet Haase5d6d7b92010-10-04 16:47:19 -0700758 // If the animation is null, there's nothing to do
759 if (baseAnimator == null) {
760 return;
761 }
762
Chet Haase21cd1382010-09-01 17:42:29 -0700763 // reset the inter-animation delay, in case we use it later
764 staggerDelay = 0;
765
Chet Haase06985942015-05-05 16:43:24 -0700766 final ViewTreeObserver observer = parent.getViewTreeObserver();
Chet Haase634a20a2010-09-08 16:36:57 -0700767 if (!observer.isAlive()) {
768 // If the observer's not in a good state, skip the transition
769 return;
770 }
Chet Haase21cd1382010-09-01 17:42:29 -0700771 int numChildren = parent.getChildCount();
772
773 for (int i = 0; i < numChildren; ++i) {
774 final View child = parent.getChildAt(i);
775
776 // only animate the views not being added or removed
777 if (child != newView) {
Chet Haasecca2c982011-05-20 14:34:18 -0700778 setupChangeAnimation(parent, changeReason, baseAnimator, duration, child);
Chet Haase21cd1382010-09-01 17:42:29 -0700779 }
780 }
Chet Haasecca2c982011-05-20 14:34:18 -0700781 if (mAnimateParentHierarchy) {
782 ViewGroup tempParent = parent;
783 while (tempParent != null) {
784 ViewParent parentParent = tempParent.getParent();
785 if (parentParent instanceof ViewGroup) {
Chet Haasee115ffe2011-08-11 11:32:43 -0700786 setupChangeAnimation((ViewGroup)parentParent, changeReason, parentAnimator,
Chet Haasecca2c982011-05-20 14:34:18 -0700787 duration, tempParent);
788 tempParent = (ViewGroup) parentParent;
789 } else {
790 tempParent = null;
791 }
792
793 }
794 }
795
Chet Haase21cd1382010-09-01 17:42:29 -0700796 // This is the cleanup step. When we get this rendering event, we know that all of
797 // the appropriate animations have been set up and run. Now we can clear out the
798 // layout listeners.
Chet Haase06985942015-05-05 16:43:24 -0700799 CleanupCallback callback = new CleanupCallback(layoutChangeListenerMap, parent);
800 observer.addOnPreDrawListener(callback);
801 parent.addOnAttachStateChangeListener(callback);
Chet Haase21cd1382010-09-01 17:42:29 -0700802 }
803
804 /**
Chet Haasecca2c982011-05-20 14:34:18 -0700805 * This flag controls whether CHANGE_APPEARING or CHANGE_DISAPPEARING animations will
Chet Haasee115ffe2011-08-11 11:32:43 -0700806 * cause the default changing animation to be run on the parent hierarchy as well. This allows
Chet Haasecca2c982011-05-20 14:34:18 -0700807 * containers of transitioning views to also transition, which may be necessary in situations
808 * where the containers bounds change between the before/after states and may clip their
809 * children during the transition animations. For example, layouts with wrap_content will
810 * adjust their bounds according to the dimensions of their children.
811 *
Chet Haasee115ffe2011-08-11 11:32:43 -0700812 * <p>The default changing transitions animate the bounds and scroll positions of the
813 * target views. These are the animations that will run on the parent hierarchy, not
814 * the custom animations that happen to be set on the transition. This allows custom
815 * behavior for the children of the transitioning container, but uses standard behavior
816 * of resizing/rescrolling on any changing parents.
817 *
Chet Haasecca2c982011-05-20 14:34:18 -0700818 * @param animateParentHierarchy A boolean value indicating whether the parents of
819 * transitioning views should also be animated during the transition. Default value is true.
820 */
821 public void setAnimateParentHierarchy(boolean animateParentHierarchy) {
822 mAnimateParentHierarchy = animateParentHierarchy;
823 }
824
825 /**
826 * Utility function called by runChangingTransition for both the children and the parent
827 * hierarchy.
828 */
829 private void setupChangeAnimation(final ViewGroup parent, final int changeReason,
830 Animator baseAnimator, final long duration, final View child) {
Chet Haase8a22e592011-11-10 17:03:12 -0800831
832 // If we already have a listener for this child, then we've already set up the
833 // changing animation we need. Multiple calls for a child may occur when several
834 // add/remove operations are run at once on a container; each one will trigger
835 // changes for the existing children in the container.
836 if (layoutChangeListenerMap.get(child) != null) {
837 return;
838 }
839
Chet Haase66ef1a22012-06-01 15:09:07 -0700840 // Don't animate items up from size(0,0); this is likely because the objects
841 // were offscreen/invisible or otherwise measured to be infinitely small. We don't
842 // want to see them animate into their real size; just ignore animation requests
843 // on these views
844 if (child.getWidth() == 0 && child.getHeight() == 0) {
845 return;
846 }
847
Chet Haasecca2c982011-05-20 14:34:18 -0700848 // Make a copy of the appropriate animation
849 final Animator anim = baseAnimator.clone();
850
851 // Set the target object for the animation
852 anim.setTarget(child);
853
854 // A ObjectAnimator (or AnimatorSet of them) can extract start values from
855 // its target object
856 anim.setupStartValues();
857
858 // If there's an animation running on this view already, cancel it
859 Animator currentAnimation = pendingAnimations.get(child);
860 if (currentAnimation != null) {
861 currentAnimation.cancel();
862 pendingAnimations.remove(child);
863 }
864 // Cache the animation in case we need to cancel it later
865 pendingAnimations.put(child, anim);
866
867 // For the animations which don't get started, we have to have a means of
868 // removing them from the cache, lest we leak them and their target objects.
869 // We run an animator for the default duration+100 (an arbitrary time, but one
870 // which should far surpass the delay between setting them up here and
871 // handling layout events which start them.
872 ValueAnimator pendingAnimRemover = ValueAnimator.ofFloat(0f, 1f).
873 setDuration(duration + 100);
874 pendingAnimRemover.addListener(new AnimatorListenerAdapter() {
875 @Override
876 public void onAnimationEnd(Animator animation) {
877 pendingAnimations.remove(child);
878 }
879 });
880 pendingAnimRemover.start();
881
882 // Add a listener to track layout changes on this view. If we don't get a callback,
883 // then there's nothing to animate.
884 final View.OnLayoutChangeListener listener = new View.OnLayoutChangeListener() {
885 public void onLayoutChange(View v, int left, int top, int right, int bottom,
886 int oldLeft, int oldTop, int oldRight, int oldBottom) {
887
888 // Tell the animation to extract end values from the changed object
889 anim.setupEndValues();
890 if (anim instanceof ValueAnimator) {
891 boolean valuesDiffer = false;
892 ValueAnimator valueAnim = (ValueAnimator)anim;
893 PropertyValuesHolder[] oldValues = valueAnim.getValues();
894 for (int i = 0; i < oldValues.length; ++i) {
895 PropertyValuesHolder pvh = oldValues[i];
George Mount984011f2014-08-21 14:28:01 -0700896 if (pvh.mKeyframes instanceof KeyframeSet) {
897 KeyframeSet keyframeSet = (KeyframeSet) pvh.mKeyframes;
898 if (keyframeSet.mFirstKeyframe == null ||
899 keyframeSet.mLastKeyframe == null ||
900 !keyframeSet.mFirstKeyframe.getValue().equals(
901 keyframeSet.mLastKeyframe.getValue())) {
902 valuesDiffer = true;
903 }
904 } else if (!pvh.mKeyframes.getValue(0).equals(pvh.mKeyframes.getValue(1))) {
Chet Haasecca2c982011-05-20 14:34:18 -0700905 valuesDiffer = true;
906 }
907 }
908 if (!valuesDiffer) {
909 return;
910 }
911 }
912
Chet Haase7dd4a532012-04-16 13:35:09 -0700913 long startDelay = 0;
914 switch (changeReason) {
915 case APPEARING:
916 startDelay = mChangingAppearingDelay + staggerDelay;
917 staggerDelay += mChangingAppearingStagger;
Chet Haase33d08762013-10-10 11:11:37 -0700918 if (mChangingAppearingInterpolator != sChangingAppearingInterpolator) {
919 anim.setInterpolator(mChangingAppearingInterpolator);
920 }
Chet Haase7dd4a532012-04-16 13:35:09 -0700921 break;
922 case DISAPPEARING:
923 startDelay = mChangingDisappearingDelay + staggerDelay;
924 staggerDelay += mChangingDisappearingStagger;
Chet Haase33d08762013-10-10 11:11:37 -0700925 if (mChangingDisappearingInterpolator !=
926 sChangingDisappearingInterpolator) {
927 anim.setInterpolator(mChangingDisappearingInterpolator);
928 }
Chet Haase7dd4a532012-04-16 13:35:09 -0700929 break;
930 case CHANGING:
931 startDelay = mChangingDelay + staggerDelay;
932 staggerDelay += mChangingStagger;
Chet Haase33d08762013-10-10 11:11:37 -0700933 if (mChangingInterpolator != sChangingInterpolator) {
934 anim.setInterpolator(mChangingInterpolator);
935 }
Chet Haase7dd4a532012-04-16 13:35:09 -0700936 break;
Chet Haasecca2c982011-05-20 14:34:18 -0700937 }
938 anim.setStartDelay(startDelay);
939 anim.setDuration(duration);
940
941 Animator prevAnimation = currentChangingAnimations.get(child);
942 if (prevAnimation != null) {
943 prevAnimation.cancel();
944 }
945 Animator pendingAnimation = pendingAnimations.get(child);
946 if (pendingAnimation != null) {
947 pendingAnimations.remove(child);
948 }
949 // Cache the animation in case we need to cancel it later
950 currentChangingAnimations.put(child, anim);
951
952 parent.requestTransitionStart(LayoutTransition.this);
953
954 // this only removes listeners whose views changed - must clear the
955 // other listeners later
956 child.removeOnLayoutChangeListener(this);
957 layoutChangeListenerMap.remove(child);
958 }
959 };
960 // Remove the animation from the cache when it ends
961 anim.addListener(new AnimatorListenerAdapter() {
962
963 @Override
964 public void onAnimationStart(Animator animator) {
Luca Zanolinc20fc8d2012-06-19 18:15:29 +0100965 if (hasListeners()) {
966 ArrayList<TransitionListener> listeners =
967 (ArrayList<TransitionListener>) mListeners.clone();
968 for (TransitionListener listener : listeners) {
Chet Haasecca2c982011-05-20 14:34:18 -0700969 listener.startTransition(LayoutTransition.this, parent, child,
970 changeReason == APPEARING ?
Chet Haase7dd4a532012-04-16 13:35:09 -0700971 CHANGE_APPEARING : changeReason == DISAPPEARING ?
972 CHANGE_DISAPPEARING : CHANGING);
Chet Haasecca2c982011-05-20 14:34:18 -0700973 }
974 }
975 }
976
977 @Override
978 public void onAnimationCancel(Animator animator) {
979 child.removeOnLayoutChangeListener(listener);
980 layoutChangeListenerMap.remove(child);
981 }
982
983 @Override
984 public void onAnimationEnd(Animator animator) {
985 currentChangingAnimations.remove(child);
Luca Zanolinc20fc8d2012-06-19 18:15:29 +0100986 if (hasListeners()) {
987 ArrayList<TransitionListener> listeners =
988 (ArrayList<TransitionListener>) mListeners.clone();
989 for (TransitionListener listener : listeners) {
Chet Haasecca2c982011-05-20 14:34:18 -0700990 listener.endTransition(LayoutTransition.this, parent, child,
991 changeReason == APPEARING ?
Chet Haase7dd4a532012-04-16 13:35:09 -0700992 CHANGE_APPEARING : changeReason == DISAPPEARING ?
993 CHANGE_DISAPPEARING : CHANGING);
Chet Haasecca2c982011-05-20 14:34:18 -0700994 }
995 }
996 }
997 });
998
999 child.addOnLayoutChangeListener(listener);
1000 // cache the listener for later removal
1001 layoutChangeListenerMap.put(child, listener);
1002 }
1003
1004 /**
1005 * Starts the animations set up for a CHANGING transition. We separate the setup of these
1006 * animations from actually starting them, to avoid side-effects that starting the animations
1007 * may have on the properties of the affected objects. After setup, we tell the affected parent
1008 * that this transition should be started. The parent informs its ViewAncestor, which then
1009 * starts the transition after the current layout/measurement phase, just prior to drawing
1010 * the view hierarchy.
1011 *
1012 * @hide
1013 */
1014 public void startChangingAnimations() {
Chet Haased56c6952011-09-07 08:46:23 -07001015 LinkedHashMap<View, Animator> currentAnimCopy =
1016 (LinkedHashMap<View, Animator>) currentChangingAnimations.clone();
1017 for (Animator anim : currentAnimCopy.values()) {
Chet Haasecca2c982011-05-20 14:34:18 -07001018 if (anim instanceof ObjectAnimator) {
1019 ((ObjectAnimator) anim).setCurrentPlayTime(0);
1020 }
1021 anim.start();
1022 }
1023 }
1024
1025 /**
Chet Haased56c6952011-09-07 08:46:23 -07001026 * Ends the animations that are set up for a CHANGING transition. This is a variant of
1027 * startChangingAnimations() which is called when the window the transition is playing in
1028 * is not visible. We need to make sure the animations put their targets in their end states
1029 * and that the transition finishes to remove any mid-process state (such as isRunning()).
1030 *
1031 * @hide
1032 */
1033 public void endChangingAnimations() {
1034 LinkedHashMap<View, Animator> currentAnimCopy =
1035 (LinkedHashMap<View, Animator>) currentChangingAnimations.clone();
1036 for (Animator anim : currentAnimCopy.values()) {
1037 anim.start();
1038 anim.end();
1039 }
Chet Haase17cf42c2012-04-17 13:18:14 -07001040 // listeners should clean up the currentChangingAnimations list, but just in case...
1041 currentChangingAnimations.clear();
Chet Haased56c6952011-09-07 08:46:23 -07001042 }
1043
1044 /**
Chet Haase9c087442011-01-12 16:20:16 -08001045 * Returns true if animations are running which animate layout-related properties. This
1046 * essentially means that either CHANGE_APPEARING or CHANGE_DISAPPEARING animations
1047 * are running, since these animations operate on layout-related properties.
1048 *
1049 * @return true if CHANGE_APPEARING or CHANGE_DISAPPEARING animations are currently
1050 * running.
1051 */
1052 public boolean isChangingLayout() {
1053 return (currentChangingAnimations.size() > 0);
1054 }
1055
1056 /**
1057 * Returns true if any of the animations in this transition are currently running.
1058 *
1059 * @return true if any animations in the transition are running.
1060 */
1061 public boolean isRunning() {
Chet Haasee8e45d32011-03-02 17:07:35 -08001062 return (currentChangingAnimations.size() > 0 || currentAppearingAnimations.size() > 0 ||
1063 currentDisappearingAnimations.size() > 0);
Chet Haase9c087442011-01-12 16:20:16 -08001064 }
1065
1066 /**
Chet Haaseadd65772011-02-09 16:47:29 -08001067 * Cancels the currently running transition. Note that we cancel() the changing animations
1068 * but end() the visibility animations. This is because this method is currently called
1069 * in the context of starting a new transition, so we want to move things from their mid-
1070 * transition positions, but we want them to have their end-transition visibility.
1071 *
1072 * @hide
1073 */
Mathew Inwood61beb332018-08-01 14:42:25 +01001074 @UnsupportedAppUsage
Chet Haaseadd65772011-02-09 16:47:29 -08001075 public void cancel() {
Chet Haasee8e45d32011-03-02 17:07:35 -08001076 if (currentChangingAnimations.size() > 0) {
Chet Haaseeb1d8512011-07-18 12:12:36 -07001077 LinkedHashMap<View, Animator> currentAnimCopy =
1078 (LinkedHashMap<View, Animator>) currentChangingAnimations.clone();
Chet Haasee8e45d32011-03-02 17:07:35 -08001079 for (Animator anim : currentAnimCopy.values()) {
1080 anim.cancel();
1081 }
1082 currentChangingAnimations.clear();
Chet Haaseadd65772011-02-09 16:47:29 -08001083 }
Chet Haasee8e45d32011-03-02 17:07:35 -08001084 if (currentAppearingAnimations.size() > 0) {
Chet Haaseeb1d8512011-07-18 12:12:36 -07001085 LinkedHashMap<View, Animator> currentAnimCopy =
1086 (LinkedHashMap<View, Animator>) currentAppearingAnimations.clone();
Chet Haasee8e45d32011-03-02 17:07:35 -08001087 for (Animator anim : currentAnimCopy.values()) {
1088 anim.end();
1089 }
1090 currentAppearingAnimations.clear();
Chet Haaseadd65772011-02-09 16:47:29 -08001091 }
Chet Haasee8e45d32011-03-02 17:07:35 -08001092 if (currentDisappearingAnimations.size() > 0) {
Chet Haaseeb1d8512011-07-18 12:12:36 -07001093 LinkedHashMap<View, Animator> currentAnimCopy =
1094 (LinkedHashMap<View, Animator>) currentDisappearingAnimations.clone();
Chet Haasee8e45d32011-03-02 17:07:35 -08001095 for (Animator anim : currentAnimCopy.values()) {
1096 anim.end();
1097 }
1098 currentDisappearingAnimations.clear();
1099 }
1100 }
1101
1102 /**
1103 * Cancels the specified type of transition. Note that we cancel() the changing animations
1104 * but end() the visibility animations. This is because this method is currently called
1105 * in the context of starting a new transition, so we want to move things from their mid-
1106 * transition positions, but we want them to have their end-transition visibility.
1107 *
1108 * @hide
1109 */
Mathew Inwood61beb332018-08-01 14:42:25 +01001110 @UnsupportedAppUsage
Chet Haasee8e45d32011-03-02 17:07:35 -08001111 public void cancel(int transitionType) {
1112 switch (transitionType) {
1113 case CHANGE_APPEARING:
1114 case CHANGE_DISAPPEARING:
Chet Haase7dd4a532012-04-16 13:35:09 -07001115 case CHANGING:
Chet Haasee8e45d32011-03-02 17:07:35 -08001116 if (currentChangingAnimations.size() > 0) {
Chet Haaseeb1d8512011-07-18 12:12:36 -07001117 LinkedHashMap<View, Animator> currentAnimCopy =
1118 (LinkedHashMap<View, Animator>) currentChangingAnimations.clone();
Chet Haasee8e45d32011-03-02 17:07:35 -08001119 for (Animator anim : currentAnimCopy.values()) {
1120 anim.cancel();
1121 }
1122 currentChangingAnimations.clear();
1123 }
1124 break;
1125 case APPEARING:
1126 if (currentAppearingAnimations.size() > 0) {
Chet Haaseeb1d8512011-07-18 12:12:36 -07001127 LinkedHashMap<View, Animator> currentAnimCopy =
1128 (LinkedHashMap<View, Animator>) currentAppearingAnimations.clone();
Chet Haasee8e45d32011-03-02 17:07:35 -08001129 for (Animator anim : currentAnimCopy.values()) {
1130 anim.end();
1131 }
1132 currentAppearingAnimations.clear();
1133 }
1134 break;
1135 case DISAPPEARING:
1136 if (currentDisappearingAnimations.size() > 0) {
Chet Haaseeb1d8512011-07-18 12:12:36 -07001137 LinkedHashMap<View, Animator> currentAnimCopy =
1138 (LinkedHashMap<View, Animator>) currentDisappearingAnimations.clone();
Chet Haasee8e45d32011-03-02 17:07:35 -08001139 for (Animator anim : currentAnimCopy.values()) {
1140 anim.end();
1141 }
1142 currentDisappearingAnimations.clear();
1143 }
1144 break;
1145 }
Chet Haaseadd65772011-02-09 16:47:29 -08001146 }
1147
1148 /**
Chet Haase21cd1382010-09-01 17:42:29 -07001149 * This method runs the animation that makes an added item appear.
1150 *
1151 * @param parent The ViewGroup to which the View is being added.
1152 * @param child The View being added to the ViewGroup.
1153 */
1154 private void runAppearingTransition(final ViewGroup parent, final View child) {
Chet Haasee8e45d32011-03-02 17:07:35 -08001155 Animator currentAnimation = currentDisappearingAnimations.get(child);
Chet Haasee64ea872010-12-13 16:15:53 -08001156 if (currentAnimation != null) {
1157 currentAnimation.cancel();
1158 }
Chet Haase5d6d7b92010-10-04 16:47:19 -07001159 if (mAppearingAnim == null) {
Luca Zanolinc20fc8d2012-06-19 18:15:29 +01001160 if (hasListeners()) {
1161 ArrayList<TransitionListener> listeners =
1162 (ArrayList<TransitionListener>) mListeners.clone();
1163 for (TransitionListener listener : listeners) {
Chet Haase5d6d7b92010-10-04 16:47:19 -07001164 listener.endTransition(LayoutTransition.this, parent, child, APPEARING);
1165 }
1166 }
1167 return;
1168 }
Chet Haasea18a86b2010-09-07 13:20:00 -07001169 Animator anim = mAppearingAnim.clone();
Chet Haase21cd1382010-09-01 17:42:29 -07001170 anim.setTarget(child);
1171 anim.setStartDelay(mAppearingDelay);
1172 anim.setDuration(mAppearingDuration);
Chet Haase33d08762013-10-10 11:11:37 -07001173 if (mAppearingInterpolator != sAppearingInterpolator) {
1174 anim.setInterpolator(mAppearingInterpolator);
1175 }
Chet Haasea18a86b2010-09-07 13:20:00 -07001176 if (anim instanceof ObjectAnimator) {
1177 ((ObjectAnimator) anim).setCurrentPlayTime(0);
Chet Haase21cd1382010-09-01 17:42:29 -07001178 }
Chet Haasea5531132012-02-02 13:41:44 -08001179 anim.addListener(new AnimatorListenerAdapter() {
1180 @Override
1181 public void onAnimationEnd(Animator anim) {
1182 currentAppearingAnimations.remove(child);
Luca Zanolinc20fc8d2012-06-19 18:15:29 +01001183 if (hasListeners()) {
1184 ArrayList<TransitionListener> listeners =
1185 (ArrayList<TransitionListener>) mListeners.clone();
1186 for (TransitionListener listener : listeners) {
Chet Haase21cd1382010-09-01 17:42:29 -07001187 listener.endTransition(LayoutTransition.this, parent, child, APPEARING);
1188 }
1189 }
Chet Haasea5531132012-02-02 13:41:44 -08001190 }
1191 });
Chet Haasee8e45d32011-03-02 17:07:35 -08001192 currentAppearingAnimations.put(child, anim);
Chet Haase21cd1382010-09-01 17:42:29 -07001193 anim.start();
1194 }
1195
1196 /**
1197 * This method runs the animation that makes a removed item disappear.
1198 *
1199 * @param parent The ViewGroup from which the View is being removed.
1200 * @param child The View being removed from the ViewGroup.
1201 */
1202 private void runDisappearingTransition(final ViewGroup parent, final View child) {
Chet Haasee8e45d32011-03-02 17:07:35 -08001203 Animator currentAnimation = currentAppearingAnimations.get(child);
Chet Haasee64ea872010-12-13 16:15:53 -08001204 if (currentAnimation != null) {
1205 currentAnimation.cancel();
1206 }
Chet Haase5d6d7b92010-10-04 16:47:19 -07001207 if (mDisappearingAnim == null) {
Luca Zanolinc20fc8d2012-06-19 18:15:29 +01001208 if (hasListeners()) {
1209 ArrayList<TransitionListener> listeners =
1210 (ArrayList<TransitionListener>) mListeners.clone();
1211 for (TransitionListener listener : listeners) {
Chet Haase5d6d7b92010-10-04 16:47:19 -07001212 listener.endTransition(LayoutTransition.this, parent, child, DISAPPEARING);
1213 }
1214 }
1215 return;
1216 }
Chet Haasea18a86b2010-09-07 13:20:00 -07001217 Animator anim = mDisappearingAnim.clone();
Chet Haase21cd1382010-09-01 17:42:29 -07001218 anim.setStartDelay(mDisappearingDelay);
1219 anim.setDuration(mDisappearingDuration);
Chet Haase33d08762013-10-10 11:11:37 -07001220 if (mDisappearingInterpolator != sDisappearingInterpolator) {
1221 anim.setInterpolator(mDisappearingInterpolator);
1222 }
Chet Haase21cd1382010-09-01 17:42:29 -07001223 anim.setTarget(child);
Chet Haasea5531132012-02-02 13:41:44 -08001224 final float preAnimAlpha = child.getAlpha();
1225 anim.addListener(new AnimatorListenerAdapter() {
1226 @Override
1227 public void onAnimationEnd(Animator anim) {
1228 currentDisappearingAnimations.remove(child);
1229 child.setAlpha(preAnimAlpha);
Luca Zanolinc20fc8d2012-06-19 18:15:29 +01001230 if (hasListeners()) {
1231 ArrayList<TransitionListener> listeners =
1232 (ArrayList<TransitionListener>) mListeners.clone();
1233 for (TransitionListener listener : listeners) {
Chet Haase21cd1382010-09-01 17:42:29 -07001234 listener.endTransition(LayoutTransition.this, parent, child, DISAPPEARING);
1235 }
1236 }
Chet Haasea5531132012-02-02 13:41:44 -08001237 }
1238 });
Chet Haasea18a86b2010-09-07 13:20:00 -07001239 if (anim instanceof ObjectAnimator) {
1240 ((ObjectAnimator) anim).setCurrentPlayTime(0);
Chet Haase21cd1382010-09-01 17:42:29 -07001241 }
Chet Haasee8e45d32011-03-02 17:07:35 -08001242 currentDisappearingAnimations.put(child, anim);
Chet Haase21cd1382010-09-01 17:42:29 -07001243 anim.start();
1244 }
1245
1246 /**
1247 * This method is called by ViewGroup when a child view is about to be added to the
1248 * container. This callback starts the process of a transition; we grab the starting
1249 * values, listen for changes to all of the children of the container, and start appropriate
1250 * animations.
1251 *
1252 * @param parent The ViewGroup to which the View is being added.
1253 * @param child The View being added to the ViewGroup.
Chet Haase0d299362012-01-26 10:51:48 -08001254 * @param changesLayout Whether the removal will cause changes in the layout of other views
1255 * in the container. INVISIBLE views becoming VISIBLE will not cause changes and thus will not
1256 * affect CHANGE_APPEARING or CHANGE_DISAPPEARING animations.
Chet Haase21cd1382010-09-01 17:42:29 -07001257 */
Chet Haase0d299362012-01-26 10:51:48 -08001258 private void addChild(ViewGroup parent, View child, boolean changesLayout) {
Chet Haaseab3a7762012-05-23 13:28:22 -07001259 if (parent.getWindowVisibility() != View.VISIBLE) {
1260 return;
1261 }
Chet Haase7dd4a532012-04-16 13:35:09 -07001262 if ((mTransitionTypes & FLAG_APPEARING) == FLAG_APPEARING) {
1263 // Want disappearing animations to finish up before proceeding
1264 cancel(DISAPPEARING);
1265 }
1266 if (changesLayout && (mTransitionTypes & FLAG_CHANGE_APPEARING) == FLAG_CHANGE_APPEARING) {
Chet Haase0d299362012-01-26 10:51:48 -08001267 // Also, cancel changing animations so that we start fresh ones from current locations
1268 cancel(CHANGE_APPEARING);
Chet Haase7dd4a532012-04-16 13:35:09 -07001269 cancel(CHANGING);
Chet Haase0d299362012-01-26 10:51:48 -08001270 }
Luca Zanolinc20fc8d2012-06-19 18:15:29 +01001271 if (hasListeners() && (mTransitionTypes & FLAG_APPEARING) == FLAG_APPEARING) {
1272 ArrayList<TransitionListener> listeners =
1273 (ArrayList<TransitionListener>) mListeners.clone();
1274 for (TransitionListener listener : listeners) {
Chet Haase21cd1382010-09-01 17:42:29 -07001275 listener.startTransition(this, parent, child, APPEARING);
1276 }
1277 }
Chet Haase7dd4a532012-04-16 13:35:09 -07001278 if (changesLayout && (mTransitionTypes & FLAG_CHANGE_APPEARING) == FLAG_CHANGE_APPEARING) {
Chet Haase0d299362012-01-26 10:51:48 -08001279 runChangeTransition(parent, child, APPEARING);
1280 }
Chet Haase7dd4a532012-04-16 13:35:09 -07001281 if ((mTransitionTypes & FLAG_APPEARING) == FLAG_APPEARING) {
1282 runAppearingTransition(parent, child);
1283 }
1284 }
1285
Luca Zanolinc20fc8d2012-06-19 18:15:29 +01001286 private boolean hasListeners() {
1287 return mListeners != null && mListeners.size() > 0;
1288 }
1289
Chet Haase7dd4a532012-04-16 13:35:09 -07001290 /**
1291 * This method is called by ViewGroup when there is a call to layout() on the container
1292 * with this LayoutTransition. If the CHANGING transition is enabled and if there is no other
1293 * transition currently running on the container, then this call runs a CHANGING transition.
1294 * The transition does not start immediately; it just sets up the mechanism to run if any
1295 * of the children of the container change their layout parameters (similar to
1296 * the CHANGE_APPEARING and CHANGE_DISAPPEARING transitions).
1297 *
1298 * @param parent The ViewGroup whose layout() method has been called.
1299 *
1300 * @hide
1301 */
1302 public void layoutChange(ViewGroup parent) {
Chet Haaseab3a7762012-05-23 13:28:22 -07001303 if (parent.getWindowVisibility() != View.VISIBLE) {
1304 return;
1305 }
Chet Haase7dd4a532012-04-16 13:35:09 -07001306 if ((mTransitionTypes & FLAG_CHANGING) == FLAG_CHANGING && !isRunning()) {
1307 // This method is called for all calls to layout() in the container, including
1308 // those caused by add/remove/hide/show events, which will already have set up
1309 // transition animations. Avoid setting up CHANGING animations in this case; only
1310 // do so when there is not a transition already running on the container.
1311 runChangeTransition(parent, null, CHANGING);
1312 }
Chet Haase21cd1382010-09-01 17:42:29 -07001313 }
1314
1315 /**
Chet Haase5e25c2c2010-09-16 11:15:56 -07001316 * This method is called by ViewGroup when a child view is about to be added to the
1317 * container. This callback starts the process of a transition; we grab the starting
1318 * values, listen for changes to all of the children of the container, and start appropriate
1319 * animations.
1320 *
1321 * @param parent The ViewGroup to which the View is being added.
1322 * @param child The View being added to the ViewGroup.
1323 */
Chet Haase0d299362012-01-26 10:51:48 -08001324 public void addChild(ViewGroup parent, View child) {
1325 addChild(parent, child, true);
1326 }
1327
1328 /**
1329 * @deprecated Use {@link #showChild(android.view.ViewGroup, android.view.View, int)}.
1330 */
1331 @Deprecated
Chet Haase5e25c2c2010-09-16 11:15:56 -07001332 public void showChild(ViewGroup parent, View child) {
Chet Haase0d299362012-01-26 10:51:48 -08001333 addChild(parent, child, true);
1334 }
1335
1336 /**
1337 * This method is called by ViewGroup when a child view is about to be made visible in the
1338 * container. This callback starts the process of a transition; we grab the starting
1339 * values, listen for changes to all of the children of the container, and start appropriate
1340 * animations.
1341 *
1342 * @param parent The ViewGroup in which the View is being made visible.
1343 * @param child The View being made visible.
1344 * @param oldVisibility The previous visibility value of the child View, either
1345 * {@link View#GONE} or {@link View#INVISIBLE}.
1346 */
1347 public void showChild(ViewGroup parent, View child, int oldVisibility) {
1348 addChild(parent, child, oldVisibility == View.GONE);
Chet Haase5e25c2c2010-09-16 11:15:56 -07001349 }
1350
1351 /**
Chet Haase21cd1382010-09-01 17:42:29 -07001352 * This method is called by ViewGroup when a child view is about to be removed from the
1353 * container. This callback starts the process of a transition; we grab the starting
1354 * values, listen for changes to all of the children of the container, and start appropriate
1355 * animations.
1356 *
1357 * @param parent The ViewGroup from which the View is being removed.
1358 * @param child The View being removed from the ViewGroup.
Chet Haase0d299362012-01-26 10:51:48 -08001359 * @param changesLayout Whether the removal will cause changes in the layout of other views
1360 * in the container. Views becoming INVISIBLE will not cause changes and thus will not
1361 * affect CHANGE_APPEARING or CHANGE_DISAPPEARING animations.
Chet Haase21cd1382010-09-01 17:42:29 -07001362 */
Chet Haase0d299362012-01-26 10:51:48 -08001363 private void removeChild(ViewGroup parent, View child, boolean changesLayout) {
Chet Haaseab3a7762012-05-23 13:28:22 -07001364 if (parent.getWindowVisibility() != View.VISIBLE) {
1365 return;
1366 }
Chet Haase7dd4a532012-04-16 13:35:09 -07001367 if ((mTransitionTypes & FLAG_DISAPPEARING) == FLAG_DISAPPEARING) {
1368 // Want appearing animations to finish up before proceeding
1369 cancel(APPEARING);
1370 }
1371 if (changesLayout &&
1372 (mTransitionTypes & FLAG_CHANGE_DISAPPEARING) == FLAG_CHANGE_DISAPPEARING) {
Chet Haase0d299362012-01-26 10:51:48 -08001373 // Also, cancel changing animations so that we start fresh ones from current locations
1374 cancel(CHANGE_DISAPPEARING);
Chet Haase7dd4a532012-04-16 13:35:09 -07001375 cancel(CHANGING);
Chet Haase0d299362012-01-26 10:51:48 -08001376 }
Luca Zanolinc20fc8d2012-06-19 18:15:29 +01001377 if (hasListeners() && (mTransitionTypes & FLAG_DISAPPEARING) == FLAG_DISAPPEARING) {
1378 ArrayList<TransitionListener> listeners = (ArrayList<TransitionListener>) mListeners
1379 .clone();
1380 for (TransitionListener listener : listeners) {
Chet Haase21cd1382010-09-01 17:42:29 -07001381 listener.startTransition(this, parent, child, DISAPPEARING);
1382 }
1383 }
Chet Haase7dd4a532012-04-16 13:35:09 -07001384 if (changesLayout &&
1385 (mTransitionTypes & FLAG_CHANGE_DISAPPEARING) == FLAG_CHANGE_DISAPPEARING) {
Chet Haase0d299362012-01-26 10:51:48 -08001386 runChangeTransition(parent, child, DISAPPEARING);
1387 }
Chet Haase7dd4a532012-04-16 13:35:09 -07001388 if ((mTransitionTypes & FLAG_DISAPPEARING) == FLAG_DISAPPEARING) {
1389 runDisappearingTransition(parent, child);
1390 }
Chet Haase21cd1382010-09-01 17:42:29 -07001391 }
1392
1393 /**
Chet Haase5e25c2c2010-09-16 11:15:56 -07001394 * This method is called by ViewGroup when a child view is about to be removed from the
1395 * container. This callback starts the process of a transition; we grab the starting
1396 * values, listen for changes to all of the children of the container, and start appropriate
1397 * animations.
1398 *
1399 * @param parent The ViewGroup from which the View is being removed.
1400 * @param child The View being removed from the ViewGroup.
1401 */
Chet Haase0d299362012-01-26 10:51:48 -08001402 public void removeChild(ViewGroup parent, View child) {
1403 removeChild(parent, child, true);
1404 }
1405
1406 /**
1407 * @deprecated Use {@link #hideChild(android.view.ViewGroup, android.view.View, int)}.
1408 */
1409 @Deprecated
Chet Haase5e25c2c2010-09-16 11:15:56 -07001410 public void hideChild(ViewGroup parent, View child) {
Chet Haase0d299362012-01-26 10:51:48 -08001411 removeChild(parent, child, true);
1412 }
1413
1414 /**
1415 * This method is called by ViewGroup when a child view is about to be hidden in
1416 * container. This callback starts the process of a transition; we grab the starting
1417 * values, listen for changes to all of the children of the container, and start appropriate
1418 * animations.
1419 *
1420 * @param parent The parent ViewGroup of the View being hidden.
1421 * @param child The View being hidden.
1422 * @param newVisibility The new visibility value of the child View, either
1423 * {@link View#GONE} or {@link View#INVISIBLE}.
1424 */
1425 public void hideChild(ViewGroup parent, View child, int newVisibility) {
1426 removeChild(parent, child, newVisibility == View.GONE);
Chet Haase5e25c2c2010-09-16 11:15:56 -07001427 }
1428
1429 /**
Chet Haase21cd1382010-09-01 17:42:29 -07001430 * Add a listener that will be called when the bounds of the view change due to
1431 * layout processing.
1432 *
1433 * @param listener The listener that will be called when layout bounds change.
1434 */
1435 public void addTransitionListener(TransitionListener listener) {
1436 if (mListeners == null) {
1437 mListeners = new ArrayList<TransitionListener>();
1438 }
1439 mListeners.add(listener);
1440 }
1441
1442 /**
1443 * Remove a listener for layout changes.
1444 *
1445 * @param listener The listener for layout bounds change.
1446 */
1447 public void removeTransitionListener(TransitionListener listener) {
1448 if (mListeners == null) {
1449 return;
1450 }
1451 mListeners.remove(listener);
1452 }
1453
1454 /**
1455 * Gets the current list of listeners for layout changes.
1456 * @return
1457 */
1458 public List<TransitionListener> getTransitionListeners() {
1459 return mListeners;
1460 }
1461
1462 /**
1463 * This interface is used for listening to starting and ending events for transitions.
1464 */
1465 public interface TransitionListener {
1466
1467 /**
Chet Haase9c087442011-01-12 16:20:16 -08001468 * This event is sent to listeners when any type of transition animation begins.
Chet Haase21cd1382010-09-01 17:42:29 -07001469 *
1470 * @param transition The LayoutTransition sending out the event.
1471 * @param container The ViewGroup on which the transition is playing.
Chet Haase9c087442011-01-12 16:20:16 -08001472 * @param view The View object being affected by the transition animation.
1473 * @param transitionType The type of transition that is beginning,
1474 * {@link android.animation.LayoutTransition#APPEARING},
1475 * {@link android.animation.LayoutTransition#DISAPPEARING},
1476 * {@link android.animation.LayoutTransition#CHANGE_APPEARING}, or
1477 * {@link android.animation.LayoutTransition#CHANGE_DISAPPEARING}.
Chet Haase21cd1382010-09-01 17:42:29 -07001478 */
1479 public void startTransition(LayoutTransition transition, ViewGroup container,
1480 View view, int transitionType);
1481
1482 /**
Chet Haase9c087442011-01-12 16:20:16 -08001483 * This event is sent to listeners when any type of transition animation ends.
Chet Haase21cd1382010-09-01 17:42:29 -07001484 *
1485 * @param transition The LayoutTransition sending out the event.
1486 * @param container The ViewGroup on which the transition is playing.
Chet Haase9c087442011-01-12 16:20:16 -08001487 * @param view The View object being affected by the transition animation.
1488 * @param transitionType The type of transition that is ending,
1489 * {@link android.animation.LayoutTransition#APPEARING},
1490 * {@link android.animation.LayoutTransition#DISAPPEARING},
1491 * {@link android.animation.LayoutTransition#CHANGE_APPEARING}, or
1492 * {@link android.animation.LayoutTransition#CHANGE_DISAPPEARING}.
Chet Haase21cd1382010-09-01 17:42:29 -07001493 */
1494 public void endTransition(LayoutTransition transition, ViewGroup container,
1495 View view, int transitionType);
1496 }
1497
Chet Haase06985942015-05-05 16:43:24 -07001498 /**
1499 * Utility class to clean up listeners after animations are setup. Cleanup happens
1500 * when either the OnPreDrawListener method is called or when the parent is detached,
1501 * whichever comes first.
1502 */
1503 private static final class CleanupCallback implements ViewTreeObserver.OnPreDrawListener,
1504 View.OnAttachStateChangeListener {
1505
1506 final Map<View, View.OnLayoutChangeListener> layoutChangeListenerMap;
1507 final ViewGroup parent;
1508
1509 CleanupCallback(Map<View, View.OnLayoutChangeListener> listenerMap, ViewGroup parent) {
1510 this.layoutChangeListenerMap = listenerMap;
1511 this.parent = parent;
1512 }
1513
1514 private void cleanup() {
1515 parent.getViewTreeObserver().removeOnPreDrawListener(this);
1516 parent.removeOnAttachStateChangeListener(this);
1517 int count = layoutChangeListenerMap.size();
1518 if (count > 0) {
1519 Collection<View> views = layoutChangeListenerMap.keySet();
1520 for (View view : views) {
1521 View.OnLayoutChangeListener listener = layoutChangeListenerMap.get(view);
1522 view.removeOnLayoutChangeListener(listener);
1523 }
1524 layoutChangeListenerMap.clear();
1525 }
1526 }
1527
1528 @Override
1529 public void onViewAttachedToWindow(View v) {
1530 }
1531
1532 @Override
1533 public void onViewDetachedFromWindow(View v) {
1534 cleanup();
1535 }
1536
1537 @Override
1538 public boolean onPreDraw() {
1539 cleanup();
1540 return true;
1541 }
1542 };
1543
Chet Haaseabb7d662011-07-18 13:07:34 -07001544}