blob: eb95a02dd75bd5ae66e7db79c835e40247158b95 [file] [log] [blame]
Chet Haasefaebd8f2012-05-18 14:17:57 -07001/*
2 * Copyright (C) 2013 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 */
Chet Haase6ebe3de2013-06-17 16:50:50 -070016
Chet Haased82c8ac2013-08-26 14:20:16 -070017package android.transition;
Chet Haasefaebd8f2012-05-18 14:17:57 -070018
19import android.animation.Animator;
George Mountfcfe5312015-05-01 14:27:45 -070020import android.animation.Animator.AnimatorListener;
21import android.animation.Animator.AnimatorPauseListener;
George Mount7764b922016-01-13 14:51:33 -080022import android.annotation.IntDef;
George Mountecd857b2014-06-19 07:51:08 -070023import android.content.Context;
24import android.content.res.TypedArray;
George Mountecd857b2014-06-19 07:51:08 -070025import android.util.AttributeSet;
Chet Haasefaebd8f2012-05-18 14:17:57 -070026import android.view.View;
27import android.view.ViewGroup;
George Mountfcfe5312015-05-01 14:27:45 -070028
George Mount7764b922016-01-13 14:51:33 -080029import com.android.internal.R;
30
31import java.lang.annotation.Retention;
32import java.lang.annotation.RetentionPolicy;
33
Chet Haasefaebd8f2012-05-18 14:17:57 -070034/**
35 * This transition tracks changes to the visibility of target views in the
36 * start and end scenes. Visibility is determined not just by the
37 * {@link View#setVisibility(int)} state of views, but also whether
38 * views exist in the current view hierarchy. The class is intended to be a
39 * utility for subclasses such as {@link Fade}, which use this visibility
40 * information to determine the specific animations to run when visibility
Chet Haase35a457a2013-08-26 07:34:12 -070041 * changes occur. Subclasses should implement one or both of the methods
Chet Haased82c8ac2013-08-26 14:20:16 -070042 * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)},
George Mountd6107a32014-03-10 16:51:16 -070043 * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)} or
44 * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)},
45 * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}.
Chet Haasefaebd8f2012-05-18 14:17:57 -070046 */
47public abstract class Visibility extends Transition {
48
George Mount6ceac2e2014-07-31 09:58:16 -070049 static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
Chet Haasefaebd8f2012-05-18 14:17:57 -070050 private static final String PROPNAME_PARENT = "android:visibility:parent";
George Mountd6107a32014-03-10 16:51:16 -070051 private static final String PROPNAME_SCREEN_LOCATION = "android:visibility:screenLocation";
52
George Mount7764b922016-01-13 14:51:33 -080053 /** @hide */
54 @Retention(RetentionPolicy.SOURCE)
55 @IntDef(flag=true, value={MODE_IN, MODE_OUT})
56 @interface VisibilityMode {}
57
George Mount18ab7992014-06-25 17:04:51 -070058 /**
59 * Mode used in {@link #setMode(int)} to make the transition
60 * operate on targets that are appearing. Maybe be combined with
George Mountad88e1b2014-07-18 15:41:13 -070061 * {@link #MODE_OUT} to target Visibility changes both in and out.
George Mount18ab7992014-06-25 17:04:51 -070062 */
George Mountad88e1b2014-07-18 15:41:13 -070063 public static final int MODE_IN = 0x1;
George Mount18ab7992014-06-25 17:04:51 -070064
65 /**
66 * Mode used in {@link #setMode(int)} to make the transition
67 * operate on targets that are disappearing. Maybe be combined with
George Mountad88e1b2014-07-18 15:41:13 -070068 * {@link #MODE_IN} to target Visibility changes both in and out.
George Mount18ab7992014-06-25 17:04:51 -070069 */
George Mountad88e1b2014-07-18 15:41:13 -070070 public static final int MODE_OUT = 0x2;
George Mount18ab7992014-06-25 17:04:51 -070071
Chet Haaseaf78bdd2013-08-27 16:06:26 -070072 private static final String[] sTransitionProperties = {
Chet Haase199acdf2013-07-24 18:40:55 -070073 PROPNAME_VISIBILITY,
George Mount20559072014-11-13 14:44:28 -080074 PROPNAME_PARENT,
Chet Haase199acdf2013-07-24 18:40:55 -070075 };
Chet Haasefaebd8f2012-05-18 14:17:57 -070076
Chet Haase6ebe3de2013-06-17 16:50:50 -070077 private static class VisibilityInfo {
78 boolean visibilityChange;
79 boolean fadeIn;
80 int startVisibility;
81 int endVisibility;
Chet Haase35a457a2013-08-26 07:34:12 -070082 ViewGroup startParent;
83 ViewGroup endParent;
Chet Haase6ebe3de2013-06-17 16:50:50 -070084 }
85
George Mountad88e1b2014-07-18 15:41:13 -070086 private int mMode = MODE_IN | MODE_OUT;
George Mount18ab7992014-06-25 17:04:51 -070087
George Mountb5ef7f82014-07-09 14:55:03 -070088 private int mForcedStartVisibility = -1;
89 private int mForcedEndVisibility = -1;
90
George Mountecd857b2014-06-19 07:51:08 -070091 public Visibility() {}
92
93 public Visibility(Context context, AttributeSet attrs) {
94 super(context, attrs);
95 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VisibilityTransition);
Craig Mautner3b2cd1d2014-08-25 14:25:54 -070096 int mode = a.getInt(R.styleable.VisibilityTransition_transitionVisibilityMode, 0);
George Mountecd857b2014-06-19 07:51:08 -070097 a.recycle();
98 if (mode != 0) {
99 setMode(mode);
100 }
101 }
102
George Mount18ab7992014-06-25 17:04:51 -0700103 /**
104 * Changes the transition to support appearing and/or disappearing Views, depending
105 * on <code>mode</code>.
106 *
107 * @param mode The behavior supported by this transition, a combination of
George Mountad88e1b2014-07-18 15:41:13 -0700108 * {@link #MODE_IN} and {@link #MODE_OUT}.
Craig Mautner3b2cd1d2014-08-25 14:25:54 -0700109 * @attr ref android.R.styleable#VisibilityTransition_transitionVisibilityMode
George Mount18ab7992014-06-25 17:04:51 -0700110 */
George Mount7764b922016-01-13 14:51:33 -0800111 public void setMode(@VisibilityMode int mode) {
George Mountad88e1b2014-07-18 15:41:13 -0700112 if ((mode & ~(MODE_IN | MODE_OUT)) != 0) {
113 throw new IllegalArgumentException("Only MODE_IN and MODE_OUT flags are allowed");
George Mount18ab7992014-06-25 17:04:51 -0700114 }
115 mMode = mode;
116 }
117
George Mountecd857b2014-06-19 07:51:08 -0700118 /**
119 * Returns whether appearing and/or disappearing Views are supported.
120 *
121 * Returns whether appearing and/or disappearing Views are supported. A combination of
George Mountad88e1b2014-07-18 15:41:13 -0700122 * {@link #MODE_IN} and {@link #MODE_OUT}.
Craig Mautner3b2cd1d2014-08-25 14:25:54 -0700123 * @attr ref android.R.styleable#VisibilityTransition_transitionVisibilityMode
George Mountecd857b2014-06-19 07:51:08 -0700124 */
George Mount7764b922016-01-13 14:51:33 -0800125 @VisibilityMode
George Mountecd857b2014-06-19 07:51:08 -0700126 public int getMode() {
127 return mMode;
128 }
129
Chet Haasefaebd8f2012-05-18 14:17:57 -0700130 @Override
Chet Haase199acdf2013-07-24 18:40:55 -0700131 public String[] getTransitionProperties() {
132 return sTransitionProperties;
133 }
134
George Mountb5ef7f82014-07-09 14:55:03 -0700135 private void captureValues(TransitionValues transitionValues, int forcedVisibility) {
136 int visibility;
137 if (forcedVisibility != -1) {
138 visibility = forcedVisibility;
139 } else {
140 visibility = transitionValues.view.getVisibility();
141 }
Chet Haased82c8ac2013-08-26 14:20:16 -0700142 transitionValues.values.put(PROPNAME_VISIBILITY, visibility);
143 transitionValues.values.put(PROPNAME_PARENT, transitionValues.view.getParent());
George Mountd6107a32014-03-10 16:51:16 -0700144 int[] loc = new int[2];
145 transitionValues.view.getLocationOnScreen(loc);
146 transitionValues.values.put(PROPNAME_SCREEN_LOCATION, loc);
Chet Haased82c8ac2013-08-26 14:20:16 -0700147 }
148
Chet Haase199acdf2013-07-24 18:40:55 -0700149 @Override
Chet Haased82c8ac2013-08-26 14:20:16 -0700150 public void captureStartValues(TransitionValues transitionValues) {
George Mountb5ef7f82014-07-09 14:55:03 -0700151 captureValues(transitionValues, mForcedStartVisibility);
Chet Haased82c8ac2013-08-26 14:20:16 -0700152 }
153
154 @Override
155 public void captureEndValues(TransitionValues transitionValues) {
George Mountb5ef7f82014-07-09 14:55:03 -0700156 captureValues(transitionValues, mForcedEndVisibility);
157 }
158
159 /** @hide */
160 @Override
161 public void forceVisibility(int visibility, boolean isStartValue) {
162 if (isStartValue) {
163 mForcedStartVisibility = visibility;
164 } else {
165 mForcedEndVisibility = visibility;
166 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700167 }
168
Chet Haase199acdf2013-07-24 18:40:55 -0700169 /**
170 * Returns whether the view is 'visible' according to the given values
171 * object. This is determined by testing the same properties in the values
172 * object that are used to determine whether the object is appearing or
173 * disappearing in the {@link
Chet Haased82c8ac2013-08-26 14:20:16 -0700174 * Transition#createAnimator(ViewGroup, TransitionValues, TransitionValues)}
Chet Haase199acdf2013-07-24 18:40:55 -0700175 * method. This method can be called by, for example, subclasses that want
176 * to know whether the object is visible in the same way that Visibility
177 * determines it for the actual animation.
178 *
179 * @param values The TransitionValues object that holds the information by
180 * which visibility is determined.
181 * @return True if the view reference by <code>values</code> is visible,
182 * false otherwise.
183 */
184 public boolean isVisible(TransitionValues values) {
185 if (values == null) {
186 return false;
187 }
188 int visibility = (Integer) values.values.get(PROPNAME_VISIBILITY);
189 View parent = (View) values.values.get(PROPNAME_PARENT);
190
191 return visibility == View.VISIBLE && parent != null;
192 }
193
Todd Volkert0653f2592015-02-12 15:27:57 -0800194 private static VisibilityInfo getVisibilityChangeInfo(TransitionValues startValues,
Chet Haasefaebd8f2012-05-18 14:17:57 -0700195 TransitionValues endValues) {
Chet Haase7660d122013-09-13 13:29:31 -0700196 final VisibilityInfo visInfo = new VisibilityInfo();
Chet Haase6ebe3de2013-06-17 16:50:50 -0700197 visInfo.visibilityChange = false;
198 visInfo.fadeIn = false;
George Mount31a21722014-03-24 17:44:36 -0700199 if (startValues != null && startValues.values.containsKey(PROPNAME_VISIBILITY)) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700200 visInfo.startVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY);
Chet Haase35a457a2013-08-26 07:34:12 -0700201 visInfo.startParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700202 } else {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700203 visInfo.startVisibility = -1;
204 visInfo.startParent = null;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700205 }
George Mount31a21722014-03-24 17:44:36 -0700206 if (endValues != null && endValues.values.containsKey(PROPNAME_VISIBILITY)) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700207 visInfo.endVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY);
Chet Haase35a457a2013-08-26 07:34:12 -0700208 visInfo.endParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700209 } else {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700210 visInfo.endVisibility = -1;
211 visInfo.endParent = null;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700212 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700213 if (startValues != null && endValues != null) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700214 if (visInfo.startVisibility == visInfo.endVisibility &&
215 visInfo.startParent == visInfo.endParent) {
216 return visInfo;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700217 } else {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700218 if (visInfo.startVisibility != visInfo.endVisibility) {
219 if (visInfo.startVisibility == View.VISIBLE) {
220 visInfo.fadeIn = false;
221 visInfo.visibilityChange = true;
222 } else if (visInfo.endVisibility == View.VISIBLE) {
223 visInfo.fadeIn = true;
224 visInfo.visibilityChange = true;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700225 }
226 // no visibilityChange if going between INVISIBLE and GONE
Chet Haase6ebe3de2013-06-17 16:50:50 -0700227 } else if (visInfo.startParent != visInfo.endParent) {
228 if (visInfo.endParent == null) {
229 visInfo.fadeIn = false;
230 visInfo.visibilityChange = true;
231 } else if (visInfo.startParent == null) {
232 visInfo.fadeIn = true;
233 visInfo.visibilityChange = true;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700234 }
235 }
236 }
George Mount79b27812014-09-12 16:39:04 -0700237 } else if (startValues == null && visInfo.endVisibility == View.VISIBLE) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700238 visInfo.fadeIn = true;
239 visInfo.visibilityChange = true;
George Mount79b27812014-09-12 16:39:04 -0700240 } else if (endValues == null && visInfo.startVisibility == View.VISIBLE) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700241 visInfo.fadeIn = false;
242 visInfo.visibilityChange = true;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700243 }
Chet Haase6ebe3de2013-06-17 16:50:50 -0700244 return visInfo;
245 }
246
247 @Override
Chet Haased82c8ac2013-08-26 14:20:16 -0700248 public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
Chet Haase6ebe3de2013-06-17 16:50:50 -0700249 TransitionValues endValues) {
250 VisibilityInfo visInfo = getVisibilityChangeInfo(startValues, endValues);
George Mount30da61d2014-05-09 13:17:52 -0700251 if (visInfo.visibilityChange
252 && (visInfo.startParent != null || visInfo.endParent != null)) {
253 if (visInfo.fadeIn) {
254 return onAppear(sceneRoot, startValues, visInfo.startVisibility,
255 endValues, visInfo.endVisibility);
256 } else {
257 return onDisappear(sceneRoot, startValues, visInfo.startVisibility,
258 endValues, visInfo.endVisibility
259 );
Chet Haasefaebd8f2012-05-18 14:17:57 -0700260 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700261 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700262 return null;
263 }
264
265 /**
George Mountd6107a32014-03-10 16:51:16 -0700266 * The default implementation of this method calls
267 * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}.
268 * Subclasses should override this method or
269 * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}.
270 * if they need to create an Animator when targets appear.
Chet Haased82c8ac2013-08-26 14:20:16 -0700271 * The method should only be called by the Visibility class; it is
272 * not intended to be called from external classes.
Chet Haasefaebd8f2012-05-18 14:17:57 -0700273 *
Chet Haased82c8ac2013-08-26 14:20:16 -0700274 * @param sceneRoot The root of the transition hierarchy
275 * @param startValues The target values in the start scene
276 * @param startVisibility The target visibility in the start scene
277 * @param endValues The target values in the end scene
278 * @param endVisibility The target visibility in the end scene
279 * @return An Animator to be started at the appropriate time in the
280 * overall transition for this scene change. A null value means no animation
281 * should be run.
Chet Haasefaebd8f2012-05-18 14:17:57 -0700282 */
Chet Haased82c8ac2013-08-26 14:20:16 -0700283 public Animator onAppear(ViewGroup sceneRoot,
Chet Haase6ebe3de2013-06-17 16:50:50 -0700284 TransitionValues startValues, int startVisibility,
285 TransitionValues endValues, int endVisibility) {
George Mountad88e1b2014-07-18 15:41:13 -0700286 if ((mMode & MODE_IN) != MODE_IN || endValues == null) {
George Mount18ab7992014-06-25 17:04:51 -0700287 return null;
288 }
Craig Stoute988ee12014-09-10 15:53:29 -0700289 if (startValues == null) {
290 VisibilityInfo parentVisibilityInfo = null;
291 View endParent = (View) endValues.view.getParent();
292 TransitionValues startParentValues = getMatchedTransitionValues(endParent,
293 false);
294 TransitionValues endParentValues = getTransitionValues(endParent, false);
295 parentVisibilityInfo =
296 getVisibilityChangeInfo(startParentValues, endParentValues);
297 if (parentVisibilityInfo.visibilityChange) {
298 return null;
299 }
300 }
George Mountfcfe5312015-05-01 14:27:45 -0700301 final boolean isForcedVisibility = mForcedStartVisibility != -1 ||
302 mForcedEndVisibility != -1;
303 if (isForcedVisibility) {
304 // Make sure that we reverse the effect of onDisappear's setTransitionAlpha(0)
305 endValues.view.setTransitionAlpha(1);
306 }
George Mountd6107a32014-03-10 16:51:16 -0700307 return onAppear(sceneRoot, endValues.view, startValues, endValues);
308 }
309
310 /**
311 * The default implementation of this method returns a null Animator. Subclasses should
312 * override this method to make targets appear with the desired transition. The
313 * method should only be called from
314 * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)}.
315 *
316 * @param sceneRoot The root of the transition hierarchy
317 * @param view The View to make appear. This will be in the target scene's View hierarchy and
318 * will be VISIBLE.
319 * @param startValues The target values in the start scene
320 * @param endValues The target values in the end scene
321 * @return An Animator to be started at the appropriate time in the
322 * overall transition for this scene change. A null value means no animation
323 * should be run.
324 */
325 public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
326 TransitionValues endValues) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700327 return null;
328 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700329
330 /**
George Mountd6107a32014-03-10 16:51:16 -0700331 * Subclasses should override this method or
332 * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}
333 * if they need to create an Animator when targets disappear.
Chet Haased82c8ac2013-08-26 14:20:16 -0700334 * The method should only be called by the Visibility class; it is
335 * not intended to be called from external classes.
George Mountd6107a32014-03-10 16:51:16 -0700336 * <p>
337 * The default implementation of this method attempts to find a View to use to call
338 * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)},
339 * based on the situation of the View in the View hierarchy. For example,
340 * if a View was simply removed from its parent, then the View will be added
341 * into a {@link android.view.ViewGroupOverlay} and passed as the <code>view</code>
342 * parameter in {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}.
343 * If a visible View is changed to be {@link View#GONE} or {@link View#INVISIBLE},
344 * then it can be used as the <code>view</code> and the visibility will be changed
345 * to {@link View#VISIBLE} for the duration of the animation. However, if a View
346 * is in a hierarchy which is also altering its visibility, the situation can be
347 * more complicated. In general, if a view that is no longer in the hierarchy in
348 * the end scene still has a parent (so its parent hierarchy was removed, but it
349 * was not removed from its parent), then it will be left alone to avoid side-effects from
350 * improperly removing it from its parent. The only exception to this is if
351 * the previous {@link Scene} was {@link Scene#getSceneForLayout(ViewGroup, int,
352 * android.content.Context) created from a layout resource file}, then it is considered
353 * safe to un-parent the starting scene view in order to make it disappear.</p>
Chet Haased82c8ac2013-08-26 14:20:16 -0700354 *
355 * @param sceneRoot The root of the transition hierarchy
356 * @param startValues The target values in the start scene
357 * @param startVisibility The target visibility in the start scene
358 * @param endValues The target values in the end scene
359 * @param endVisibility The target visibility in the end scene
360 * @return An Animator to be started at the appropriate time in the
361 * overall transition for this scene change. A null value means no animation
362 * should be run.
Chet Haasefaebd8f2012-05-18 14:17:57 -0700363 */
Chet Haased82c8ac2013-08-26 14:20:16 -0700364 public Animator onDisappear(ViewGroup sceneRoot,
Chet Haase6ebe3de2013-06-17 16:50:50 -0700365 TransitionValues startValues, int startVisibility,
366 TransitionValues endValues, int endVisibility) {
George Mountad88e1b2014-07-18 15:41:13 -0700367 if ((mMode & MODE_OUT) != MODE_OUT) {
George Mount18ab7992014-06-25 17:04:51 -0700368 return null;
369 }
370
George Mountd6107a32014-03-10 16:51:16 -0700371 View startView = (startValues != null) ? startValues.view : null;
372 View endView = (endValues != null) ? endValues.view : null;
373 View overlayView = null;
374 View viewToKeep = null;
375 if (endView == null || endView.getParent() == null) {
376 if (endView != null) {
377 // endView was removed from its parent - add it to the overlay
378 overlayView = endView;
379 } else if (startView != null) {
380 // endView does not exist. Use startView only under certain
381 // conditions, because placing a view in an overlay necessitates
382 // it being removed from its current parent
383 if (startView.getParent() == null) {
384 // no parent - safe to use
385 overlayView = startView;
George Mountd4c3c912014-06-09 12:31:34 -0700386 } else if (startView.getParent() instanceof View) {
George Mountd6107a32014-03-10 16:51:16 -0700387 View startParent = (View) startView.getParent();
George Mount79b27812014-09-12 16:39:04 -0700388 TransitionValues startParentValues = getTransitionValues(startParent, true);
George Mount9f1ac392014-09-07 15:04:03 -0700389 TransitionValues endParentValues = getMatchedTransitionValues(startParent,
390 true);
George Mount79b27812014-09-12 16:39:04 -0700391 VisibilityInfo parentVisibilityInfo =
392 getVisibilityChangeInfo(startParentValues, endParentValues);
393 if (!parentVisibilityInfo.visibilityChange) {
394 overlayView = TransitionUtils.copyViewImage(sceneRoot, startView,
395 startParent);
George Mountd4c3c912014-06-09 12:31:34 -0700396 } else if (startParent.getParent() == null) {
397 int id = startParent.getId();
398 if (id != View.NO_ID && sceneRoot.findViewById(id) != null
399 && mCanRemoveViews) {
400 // no parent, but its parent is unparented but the parent
401 // hierarchy has been replaced by a new hierarchy with the same id
402 // and it is safe to un-parent startView
403 overlayView = startView;
404 }
George Mountd6107a32014-03-10 16:51:16 -0700405 }
406 }
407 }
408 } else {
409 // visibility change
410 if (endVisibility == View.INVISIBLE) {
411 viewToKeep = endView;
412 } else {
413 // Becoming GONE
414 if (startView == endView) {
415 viewToKeep = endView;
416 } else {
417 overlayView = startView;
418 }
419 }
420 }
421 final int finalVisibility = endVisibility;
422 final ViewGroup finalSceneRoot = sceneRoot;
423
424 if (overlayView != null) {
425 // TODO: Need to do this for general case of adding to overlay
426 int[] screenLoc = (int[]) startValues.values.get(PROPNAME_SCREEN_LOCATION);
427 int screenX = screenLoc[0];
428 int screenY = screenLoc[1];
429 int[] loc = new int[2];
430 sceneRoot.getLocationOnScreen(loc);
431 overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft());
432 overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop());
433 sceneRoot.getOverlay().add(overlayView);
434 Animator animator = onDisappear(sceneRoot, overlayView, startValues, endValues);
435 if (animator == null) {
436 sceneRoot.getOverlay().remove(overlayView);
437 } else {
438 final View finalOverlayView = overlayView;
George Mountfcfe5312015-05-01 14:27:45 -0700439 addListener(new TransitionListenerAdapter() {
George Mountd6107a32014-03-10 16:51:16 -0700440 @Override
George Mountfcfe5312015-05-01 14:27:45 -0700441 public void onTransitionEnd(Transition transition) {
George Mountd6107a32014-03-10 16:51:16 -0700442 finalSceneRoot.getOverlay().remove(finalOverlayView);
443 }
George Mountd6107a32014-03-10 16:51:16 -0700444 });
445 }
446 return animator;
447 }
448
449 if (viewToKeep != null) {
George Mountb5ef7f82014-07-09 14:55:03 -0700450 int originalVisibility = -1;
451 final boolean isForcedVisibility = mForcedStartVisibility != -1 ||
452 mForcedEndVisibility != -1;
453 if (!isForcedVisibility) {
454 originalVisibility = viewToKeep.getVisibility();
George Mounte5a93aa2015-06-05 16:47:45 -0700455 viewToKeep.setTransitionVisibility(View.VISIBLE);
George Mountb5ef7f82014-07-09 14:55:03 -0700456 }
George Mountd6107a32014-03-10 16:51:16 -0700457 Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues);
George Mountb5ef7f82014-07-09 14:55:03 -0700458 if (animator != null) {
George Mountfcfe5312015-05-01 14:27:45 -0700459 DisappearListener disappearListener = new DisappearListener(viewToKeep,
460 finalVisibility, isForcedVisibility);
461 animator.addListener(disappearListener);
George Mount0b056a02015-06-29 10:26:10 -0700462 animator.addPauseListener(disappearListener);
George Mountfcfe5312015-05-01 14:27:45 -0700463 addListener(disappearListener);
George Mountb5ef7f82014-07-09 14:55:03 -0700464 } else if (!isForcedVisibility) {
George Mounte5a93aa2015-06-05 16:47:45 -0700465 viewToKeep.setTransitionVisibility(originalVisibility);
George Mountd6107a32014-03-10 16:51:16 -0700466 }
467 return animator;
468 }
469 return null;
470 }
471
George Mount4c20ea22014-06-17 10:14:39 -0700472 @Override
George Mountaef1ae32015-06-04 12:51:55 -0700473 public boolean isTransitionRequired(TransitionValues startValues, TransitionValues newValues) {
474 if (startValues == null && newValues == null) {
George Mount4c20ea22014-06-17 10:14:39 -0700475 return false;
476 }
George Mountaef1ae32015-06-04 12:51:55 -0700477 if (startValues != null && newValues != null &&
George Mountcba01b22014-10-22 14:39:43 -0700478 newValues.values.containsKey(PROPNAME_VISIBILITY) !=
George Mountaef1ae32015-06-04 12:51:55 -0700479 startValues.values.containsKey(PROPNAME_VISIBILITY)) {
George Mountcba01b22014-10-22 14:39:43 -0700480 // The transition wasn't targeted in either the start or end, so it couldn't
481 // have changed.
482 return false;
483 }
George Mountaef1ae32015-06-04 12:51:55 -0700484 VisibilityInfo changeInfo = getVisibilityChangeInfo(startValues, newValues);
George Mount4c20ea22014-06-17 10:14:39 -0700485 return changeInfo.visibilityChange && (changeInfo.startVisibility == View.VISIBLE ||
George Mountcba01b22014-10-22 14:39:43 -0700486 changeInfo.endVisibility == View.VISIBLE);
George Mount4c20ea22014-06-17 10:14:39 -0700487 }
488
George Mountd6107a32014-03-10 16:51:16 -0700489 /**
490 * The default implementation of this method returns a null Animator. Subclasses should
491 * override this method to make targets disappear with the desired transition. The
492 * method should only be called from
493 * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)}.
494 *
495 * @param sceneRoot The root of the transition hierarchy
496 * @param view The View to make disappear. This will be in the target scene's View
497 * hierarchy or in an {@link android.view.ViewGroupOverlay} and will be
498 * VISIBLE.
499 * @param startValues The target values in the start scene
500 * @param endValues The target values in the end scene
501 * @return An Animator to be started at the appropriate time in the
502 * overall transition for this scene change. A null value means no animation
503 * should be run.
504 */
505 public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
506 TransitionValues endValues) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700507 return null;
508 }
George Mountfcfe5312015-05-01 14:27:45 -0700509
510 private static class DisappearListener
511 extends TransitionListenerAdapter implements AnimatorListener, AnimatorPauseListener {
512 private final boolean mIsForcedVisibility;
513 private final View mView;
514 private final int mFinalVisibility;
George Mount21290a62015-06-22 13:23:06 -0700515 private final ViewGroup mParent;
George Mountfcfe5312015-05-01 14:27:45 -0700516
George Mounta7478ab2015-06-30 16:54:00 -0700517 private boolean mLayoutSuppressed;
518 private boolean mFinalVisibilitySet = false;
George Mountfcfe5312015-05-01 14:27:45 -0700519 boolean mCanceled = false;
520
521 public DisappearListener(View view, int finalVisibility, boolean isForcedVisibility) {
522 this.mView = view;
523 this.mIsForcedVisibility = isForcedVisibility;
524 this.mFinalVisibility = finalVisibility;
George Mount21290a62015-06-22 13:23:06 -0700525 this.mParent = (ViewGroup) view.getParent();
George Mounta7478ab2015-06-30 16:54:00 -0700526 // Prevent a layout from including mView in its calculation.
527 suppressLayout(true);
George Mountfcfe5312015-05-01 14:27:45 -0700528 }
529
530 @Override
531 public void onAnimationPause(Animator animation) {
532 if (!mCanceled && !mIsForcedVisibility) {
George Mounte5a93aa2015-06-05 16:47:45 -0700533 mView.setTransitionVisibility(mFinalVisibility);
George Mountfcfe5312015-05-01 14:27:45 -0700534 }
535 }
536
537 @Override
538 public void onAnimationResume(Animator animation) {
539 if (!mCanceled && !mIsForcedVisibility) {
George Mounte5a93aa2015-06-05 16:47:45 -0700540 mView.setTransitionVisibility(View.VISIBLE);
George Mountfcfe5312015-05-01 14:27:45 -0700541 }
542 }
543
544 @Override
545 public void onAnimationCancel(Animator animation) {
546 mCanceled = true;
547 }
548
549 @Override
550 public void onAnimationRepeat(Animator animation) {
George Mountfcfe5312015-05-01 14:27:45 -0700551 }
552
553 @Override
554 public void onAnimationStart(Animator animation) {
George Mountfcfe5312015-05-01 14:27:45 -0700555 }
556
557 @Override
558 public void onAnimationEnd(Animator animation) {
559 hideViewWhenNotCanceled();
560 }
561
562 @Override
563 public void onTransitionEnd(Transition transition) {
564 hideViewWhenNotCanceled();
565 }
566
George Mount21290a62015-06-22 13:23:06 -0700567 @Override
568 public void onTransitionPause(Transition transition) {
George Mounta7478ab2015-06-30 16:54:00 -0700569 suppressLayout(false);
George Mount21290a62015-06-22 13:23:06 -0700570 }
571
572 @Override
573 public void onTransitionResume(Transition transition) {
George Mounta7478ab2015-06-30 16:54:00 -0700574 suppressLayout(true);
George Mount21290a62015-06-22 13:23:06 -0700575 }
576
George Mountfcfe5312015-05-01 14:27:45 -0700577 private void hideViewWhenNotCanceled() {
George Mounta7478ab2015-06-30 16:54:00 -0700578 if (!mCanceled) {
579 if (mIsForcedVisibility) {
580 mView.setTransitionAlpha(0);
581 } else if (!mFinalVisibilitySet) {
582 // Recreate the parent's display list in case it includes mView.
583 mView.setTransitionVisibility(mFinalVisibility);
584 if (mParent != null) {
585 mParent.invalidate();
George Mount21290a62015-06-22 13:23:06 -0700586 }
George Mounta7478ab2015-06-30 16:54:00 -0700587 mFinalVisibilitySet = true;
George Mountfcfe5312015-05-01 14:27:45 -0700588 }
George Mounta7478ab2015-06-30 16:54:00 -0700589 }
590 // Layout is allowed now that the View is in its final state
591 suppressLayout(false);
592 }
593
594 private void suppressLayout(boolean suppress) {
595 if (mLayoutSuppressed != suppress && mParent != null && !mIsForcedVisibility) {
596 mLayoutSuppressed = suppress;
597 mParent.suppressLayout(suppress);
George Mountfcfe5312015-05-01 14:27:45 -0700598 }
599 }
600 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700601}