blob: f58291f489496b1aa6e3067dd5b368d1d3c029e7 [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
George Mountecd857b2014-06-19 07:51:08 -070019import com.android.internal.R;
20
Chet Haasefaebd8f2012-05-18 14:17:57 -070021import android.animation.Animator;
George Mountd6107a32014-03-10 16:51:16 -070022import android.animation.AnimatorListenerAdapter;
George Mountecd857b2014-06-19 07:51:08 -070023import android.content.Context;
24import android.content.res.TypedArray;
George Mountd4c3c912014-06-09 12:31:34 -070025import android.graphics.Bitmap;
26import android.graphics.Canvas;
27import android.graphics.drawable.BitmapDrawable;
George Mountecd857b2014-06-19 07:51:08 -070028import android.util.AttributeSet;
Chet Haasefaebd8f2012-05-18 14:17:57 -070029import android.view.View;
30import android.view.ViewGroup;
Chet Haasefaebd8f2012-05-18 14:17:57 -070031/**
32 * This transition tracks changes to the visibility of target views in the
33 * start and end scenes. Visibility is determined not just by the
34 * {@link View#setVisibility(int)} state of views, but also whether
35 * views exist in the current view hierarchy. The class is intended to be a
36 * utility for subclasses such as {@link Fade}, which use this visibility
37 * information to determine the specific animations to run when visibility
Chet Haase35a457a2013-08-26 07:34:12 -070038 * changes occur. Subclasses should implement one or both of the methods
Chet Haased82c8ac2013-08-26 14:20:16 -070039 * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)},
George Mountd6107a32014-03-10 16:51:16 -070040 * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)} or
41 * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)},
42 * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}.
Chet Haasefaebd8f2012-05-18 14:17:57 -070043 */
44public abstract class Visibility extends Transition {
45
George Mount6ceac2e2014-07-31 09:58:16 -070046 static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
Chet Haasefaebd8f2012-05-18 14:17:57 -070047 private static final String PROPNAME_PARENT = "android:visibility:parent";
George Mountd6107a32014-03-10 16:51:16 -070048 private static final String PROPNAME_SCREEN_LOCATION = "android:visibility:screenLocation";
49
George Mount18ab7992014-06-25 17:04:51 -070050 /**
51 * Mode used in {@link #setMode(int)} to make the transition
52 * operate on targets that are appearing. Maybe be combined with
George Mountad88e1b2014-07-18 15:41:13 -070053 * {@link #MODE_OUT} to target Visibility changes both in and out.
George Mount18ab7992014-06-25 17:04:51 -070054 */
George Mountad88e1b2014-07-18 15:41:13 -070055 public static final int MODE_IN = 0x1;
George Mount18ab7992014-06-25 17:04:51 -070056
57 /**
58 * Mode used in {@link #setMode(int)} to make the transition
59 * operate on targets that are disappearing. Maybe be combined with
George Mountad88e1b2014-07-18 15:41:13 -070060 * {@link #MODE_IN} to target Visibility changes both in and out.
George Mount18ab7992014-06-25 17:04:51 -070061 */
George Mountad88e1b2014-07-18 15:41:13 -070062 public static final int MODE_OUT = 0x2;
George Mount18ab7992014-06-25 17:04:51 -070063
Chet Haaseaf78bdd2013-08-27 16:06:26 -070064 private static final String[] sTransitionProperties = {
Chet Haase199acdf2013-07-24 18:40:55 -070065 PROPNAME_VISIBILITY,
Chet Haase199acdf2013-07-24 18:40:55 -070066 };
Chet Haasefaebd8f2012-05-18 14:17:57 -070067
Chet Haase6ebe3de2013-06-17 16:50:50 -070068 private static class VisibilityInfo {
69 boolean visibilityChange;
70 boolean fadeIn;
71 int startVisibility;
72 int endVisibility;
Chet Haase35a457a2013-08-26 07:34:12 -070073 ViewGroup startParent;
74 ViewGroup endParent;
Chet Haase6ebe3de2013-06-17 16:50:50 -070075 }
76
George Mountad88e1b2014-07-18 15:41:13 -070077 private int mMode = MODE_IN | MODE_OUT;
George Mount18ab7992014-06-25 17:04:51 -070078
George Mountb5ef7f82014-07-09 14:55:03 -070079 private int mForcedStartVisibility = -1;
80 private int mForcedEndVisibility = -1;
81
George Mountecd857b2014-06-19 07:51:08 -070082 public Visibility() {}
83
84 public Visibility(Context context, AttributeSet attrs) {
85 super(context, attrs);
86 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VisibilityTransition);
Craig Mautner3b2cd1d2014-08-25 14:25:54 -070087 int mode = a.getInt(R.styleable.VisibilityTransition_transitionVisibilityMode, 0);
George Mountecd857b2014-06-19 07:51:08 -070088 a.recycle();
89 if (mode != 0) {
90 setMode(mode);
91 }
92 }
93
George Mount18ab7992014-06-25 17:04:51 -070094 /**
95 * Changes the transition to support appearing and/or disappearing Views, depending
96 * on <code>mode</code>.
97 *
98 * @param mode The behavior supported by this transition, a combination of
George Mountad88e1b2014-07-18 15:41:13 -070099 * {@link #MODE_IN} and {@link #MODE_OUT}.
Craig Mautner3b2cd1d2014-08-25 14:25:54 -0700100 * @attr ref android.R.styleable#VisibilityTransition_transitionVisibilityMode
George Mount18ab7992014-06-25 17:04:51 -0700101 */
102 public void setMode(int mode) {
George Mountad88e1b2014-07-18 15:41:13 -0700103 if ((mode & ~(MODE_IN | MODE_OUT)) != 0) {
104 throw new IllegalArgumentException("Only MODE_IN and MODE_OUT flags are allowed");
George Mount18ab7992014-06-25 17:04:51 -0700105 }
106 mMode = mode;
107 }
108
George Mountecd857b2014-06-19 07:51:08 -0700109 /**
110 * Returns whether appearing and/or disappearing Views are supported.
111 *
112 * Returns whether appearing and/or disappearing Views are supported. A combination of
George Mountad88e1b2014-07-18 15:41:13 -0700113 * {@link #MODE_IN} and {@link #MODE_OUT}.
Craig Mautner3b2cd1d2014-08-25 14:25:54 -0700114 * @attr ref android.R.styleable#VisibilityTransition_transitionVisibilityMode
George Mountecd857b2014-06-19 07:51:08 -0700115 */
116 public int getMode() {
117 return mMode;
118 }
119
Chet Haasefaebd8f2012-05-18 14:17:57 -0700120 @Override
Chet Haase199acdf2013-07-24 18:40:55 -0700121 public String[] getTransitionProperties() {
122 return sTransitionProperties;
123 }
124
George Mountb5ef7f82014-07-09 14:55:03 -0700125 private void captureValues(TransitionValues transitionValues, int forcedVisibility) {
126 int visibility;
127 if (forcedVisibility != -1) {
128 visibility = forcedVisibility;
129 } else {
130 visibility = transitionValues.view.getVisibility();
131 }
Chet Haased82c8ac2013-08-26 14:20:16 -0700132 transitionValues.values.put(PROPNAME_VISIBILITY, visibility);
133 transitionValues.values.put(PROPNAME_PARENT, transitionValues.view.getParent());
George Mountd6107a32014-03-10 16:51:16 -0700134 int[] loc = new int[2];
135 transitionValues.view.getLocationOnScreen(loc);
136 transitionValues.values.put(PROPNAME_SCREEN_LOCATION, loc);
Chet Haased82c8ac2013-08-26 14:20:16 -0700137 }
138
Chet Haase199acdf2013-07-24 18:40:55 -0700139 @Override
Chet Haased82c8ac2013-08-26 14:20:16 -0700140 public void captureStartValues(TransitionValues transitionValues) {
George Mountb5ef7f82014-07-09 14:55:03 -0700141 captureValues(transitionValues, mForcedStartVisibility);
Chet Haased82c8ac2013-08-26 14:20:16 -0700142 }
143
144 @Override
145 public void captureEndValues(TransitionValues transitionValues) {
George Mountb5ef7f82014-07-09 14:55:03 -0700146 captureValues(transitionValues, mForcedEndVisibility);
147 }
148
149 /** @hide */
150 @Override
151 public void forceVisibility(int visibility, boolean isStartValue) {
152 if (isStartValue) {
153 mForcedStartVisibility = visibility;
154 } else {
155 mForcedEndVisibility = visibility;
156 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700157 }
158
Chet Haase199acdf2013-07-24 18:40:55 -0700159 /**
160 * Returns whether the view is 'visible' according to the given values
161 * object. This is determined by testing the same properties in the values
162 * object that are used to determine whether the object is appearing or
163 * disappearing in the {@link
Chet Haased82c8ac2013-08-26 14:20:16 -0700164 * Transition#createAnimator(ViewGroup, TransitionValues, TransitionValues)}
Chet Haase199acdf2013-07-24 18:40:55 -0700165 * method. This method can be called by, for example, subclasses that want
166 * to know whether the object is visible in the same way that Visibility
167 * determines it for the actual animation.
168 *
169 * @param values The TransitionValues object that holds the information by
170 * which visibility is determined.
171 * @return True if the view reference by <code>values</code> is visible,
172 * false otherwise.
173 */
174 public boolean isVisible(TransitionValues values) {
175 if (values == null) {
176 return false;
177 }
178 int visibility = (Integer) values.values.get(PROPNAME_VISIBILITY);
179 View parent = (View) values.values.get(PROPNAME_PARENT);
180
181 return visibility == View.VISIBLE && parent != null;
182 }
183
Chet Haase6ebe3de2013-06-17 16:50:50 -0700184 private VisibilityInfo getVisibilityChangeInfo(TransitionValues startValues,
Chet Haasefaebd8f2012-05-18 14:17:57 -0700185 TransitionValues endValues) {
Chet Haase7660d122013-09-13 13:29:31 -0700186 final VisibilityInfo visInfo = new VisibilityInfo();
Chet Haase6ebe3de2013-06-17 16:50:50 -0700187 visInfo.visibilityChange = false;
188 visInfo.fadeIn = false;
George Mount31a21722014-03-24 17:44:36 -0700189 if (startValues != null && startValues.values.containsKey(PROPNAME_VISIBILITY)) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700190 visInfo.startVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY);
Chet Haase35a457a2013-08-26 07:34:12 -0700191 visInfo.startParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700192 } else {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700193 visInfo.startVisibility = -1;
194 visInfo.startParent = null;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700195 }
George Mount31a21722014-03-24 17:44:36 -0700196 if (endValues != null && endValues.values.containsKey(PROPNAME_VISIBILITY)) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700197 visInfo.endVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY);
Chet Haase35a457a2013-08-26 07:34:12 -0700198 visInfo.endParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700199 } else {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700200 visInfo.endVisibility = -1;
201 visInfo.endParent = null;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700202 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700203 if (startValues != null && endValues != null) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700204 if (visInfo.startVisibility == visInfo.endVisibility &&
205 visInfo.startParent == visInfo.endParent) {
206 return visInfo;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700207 } else {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700208 if (visInfo.startVisibility != visInfo.endVisibility) {
209 if (visInfo.startVisibility == View.VISIBLE) {
210 visInfo.fadeIn = false;
211 visInfo.visibilityChange = true;
212 } else if (visInfo.endVisibility == View.VISIBLE) {
213 visInfo.fadeIn = true;
214 visInfo.visibilityChange = true;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700215 }
216 // no visibilityChange if going between INVISIBLE and GONE
Chet Haase6ebe3de2013-06-17 16:50:50 -0700217 } else if (visInfo.startParent != visInfo.endParent) {
218 if (visInfo.endParent == null) {
219 visInfo.fadeIn = false;
220 visInfo.visibilityChange = true;
221 } else if (visInfo.startParent == null) {
222 visInfo.fadeIn = true;
223 visInfo.visibilityChange = true;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700224 }
225 }
226 }
George Mount79b27812014-09-12 16:39:04 -0700227 } else if (startValues == null && visInfo.endVisibility == View.VISIBLE) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700228 visInfo.fadeIn = true;
229 visInfo.visibilityChange = true;
George Mount79b27812014-09-12 16:39:04 -0700230 } else if (endValues == null && visInfo.startVisibility == View.VISIBLE) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700231 visInfo.fadeIn = false;
232 visInfo.visibilityChange = true;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700233 }
Chet Haase6ebe3de2013-06-17 16:50:50 -0700234 return visInfo;
235 }
236
237 @Override
Chet Haased82c8ac2013-08-26 14:20:16 -0700238 public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
Chet Haase6ebe3de2013-06-17 16:50:50 -0700239 TransitionValues endValues) {
240 VisibilityInfo visInfo = getVisibilityChangeInfo(startValues, endValues);
George Mount30da61d2014-05-09 13:17:52 -0700241 if (visInfo.visibilityChange
242 && (visInfo.startParent != null || visInfo.endParent != null)) {
243 if (visInfo.fadeIn) {
244 return onAppear(sceneRoot, startValues, visInfo.startVisibility,
245 endValues, visInfo.endVisibility);
246 } else {
247 return onDisappear(sceneRoot, startValues, visInfo.startVisibility,
248 endValues, visInfo.endVisibility
249 );
Chet Haasefaebd8f2012-05-18 14:17:57 -0700250 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700251 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700252 return null;
253 }
254
255 /**
George Mountd6107a32014-03-10 16:51:16 -0700256 * The default implementation of this method calls
257 * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}.
258 * Subclasses should override this method or
259 * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}.
260 * if they need to create an Animator when targets appear.
Chet Haased82c8ac2013-08-26 14:20:16 -0700261 * The method should only be called by the Visibility class; it is
262 * not intended to be called from external classes.
Chet Haasefaebd8f2012-05-18 14:17:57 -0700263 *
Chet Haased82c8ac2013-08-26 14:20:16 -0700264 * @param sceneRoot The root of the transition hierarchy
265 * @param startValues The target values in the start scene
266 * @param startVisibility The target visibility in the start scene
267 * @param endValues The target values in the end scene
268 * @param endVisibility The target visibility in the end scene
269 * @return An Animator to be started at the appropriate time in the
270 * overall transition for this scene change. A null value means no animation
271 * should be run.
Chet Haasefaebd8f2012-05-18 14:17:57 -0700272 */
Chet Haased82c8ac2013-08-26 14:20:16 -0700273 public Animator onAppear(ViewGroup sceneRoot,
Chet Haase6ebe3de2013-06-17 16:50:50 -0700274 TransitionValues startValues, int startVisibility,
275 TransitionValues endValues, int endVisibility) {
George Mountad88e1b2014-07-18 15:41:13 -0700276 if ((mMode & MODE_IN) != MODE_IN || endValues == null) {
George Mount18ab7992014-06-25 17:04:51 -0700277 return null;
278 }
Craig Stoute988ee12014-09-10 15:53:29 -0700279 if (startValues == null) {
280 VisibilityInfo parentVisibilityInfo = null;
281 View endParent = (View) endValues.view.getParent();
282 TransitionValues startParentValues = getMatchedTransitionValues(endParent,
283 false);
284 TransitionValues endParentValues = getTransitionValues(endParent, false);
285 parentVisibilityInfo =
286 getVisibilityChangeInfo(startParentValues, endParentValues);
287 if (parentVisibilityInfo.visibilityChange) {
288 return null;
289 }
290 }
George Mountd6107a32014-03-10 16:51:16 -0700291 return onAppear(sceneRoot, endValues.view, startValues, endValues);
292 }
293
294 /**
295 * The default implementation of this method returns a null Animator. Subclasses should
296 * override this method to make targets appear with the desired transition. The
297 * method should only be called from
298 * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)}.
299 *
300 * @param sceneRoot The root of the transition hierarchy
301 * @param view The View to make appear. This will be in the target scene's View hierarchy and
302 * will be VISIBLE.
303 * @param startValues The target values in the start scene
304 * @param endValues The target values in the end scene
305 * @return An Animator to be started at the appropriate time in the
306 * overall transition for this scene change. A null value means no animation
307 * should be run.
308 */
309 public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
310 TransitionValues endValues) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700311 return null;
312 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700313
314 /**
George Mountd6107a32014-03-10 16:51:16 -0700315 * Subclasses should override this method or
316 * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}
317 * if they need to create an Animator when targets disappear.
Chet Haased82c8ac2013-08-26 14:20:16 -0700318 * The method should only be called by the Visibility class; it is
319 * not intended to be called from external classes.
George Mountd6107a32014-03-10 16:51:16 -0700320 * <p>
321 * The default implementation of this method attempts to find a View to use to call
322 * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)},
323 * based on the situation of the View in the View hierarchy. For example,
324 * if a View was simply removed from its parent, then the View will be added
325 * into a {@link android.view.ViewGroupOverlay} and passed as the <code>view</code>
326 * parameter in {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}.
327 * If a visible View is changed to be {@link View#GONE} or {@link View#INVISIBLE},
328 * then it can be used as the <code>view</code> and the visibility will be changed
329 * to {@link View#VISIBLE} for the duration of the animation. However, if a View
330 * is in a hierarchy which is also altering its visibility, the situation can be
331 * more complicated. In general, if a view that is no longer in the hierarchy in
332 * the end scene still has a parent (so its parent hierarchy was removed, but it
333 * was not removed from its parent), then it will be left alone to avoid side-effects from
334 * improperly removing it from its parent. The only exception to this is if
335 * the previous {@link Scene} was {@link Scene#getSceneForLayout(ViewGroup, int,
336 * android.content.Context) created from a layout resource file}, then it is considered
337 * safe to un-parent the starting scene view in order to make it disappear.</p>
Chet Haased82c8ac2013-08-26 14:20:16 -0700338 *
339 * @param sceneRoot The root of the transition hierarchy
340 * @param startValues The target values in the start scene
341 * @param startVisibility The target visibility in the start scene
342 * @param endValues The target values in the end scene
343 * @param endVisibility The target visibility in the end scene
344 * @return An Animator to be started at the appropriate time in the
345 * overall transition for this scene change. A null value means no animation
346 * should be run.
Chet Haasefaebd8f2012-05-18 14:17:57 -0700347 */
Chet Haased82c8ac2013-08-26 14:20:16 -0700348 public Animator onDisappear(ViewGroup sceneRoot,
Chet Haase6ebe3de2013-06-17 16:50:50 -0700349 TransitionValues startValues, int startVisibility,
350 TransitionValues endValues, int endVisibility) {
George Mountad88e1b2014-07-18 15:41:13 -0700351 if ((mMode & MODE_OUT) != MODE_OUT) {
George Mount18ab7992014-06-25 17:04:51 -0700352 return null;
353 }
354
George Mountd6107a32014-03-10 16:51:16 -0700355 View startView = (startValues != null) ? startValues.view : null;
356 View endView = (endValues != null) ? endValues.view : null;
357 View overlayView = null;
358 View viewToKeep = null;
359 if (endView == null || endView.getParent() == null) {
360 if (endView != null) {
361 // endView was removed from its parent - add it to the overlay
362 overlayView = endView;
363 } else if (startView != null) {
364 // endView does not exist. Use startView only under certain
365 // conditions, because placing a view in an overlay necessitates
366 // it being removed from its current parent
367 if (startView.getParent() == null) {
368 // no parent - safe to use
369 overlayView = startView;
George Mountd4c3c912014-06-09 12:31:34 -0700370 } else if (startView.getParent() instanceof View) {
George Mountd6107a32014-03-10 16:51:16 -0700371 View startParent = (View) startView.getParent();
George Mount79b27812014-09-12 16:39:04 -0700372 TransitionValues startParentValues = getTransitionValues(startParent, true);
George Mount9f1ac392014-09-07 15:04:03 -0700373 TransitionValues endParentValues = getMatchedTransitionValues(startParent,
374 true);
George Mount79b27812014-09-12 16:39:04 -0700375 VisibilityInfo parentVisibilityInfo =
376 getVisibilityChangeInfo(startParentValues, endParentValues);
377 if (!parentVisibilityInfo.visibilityChange) {
378 overlayView = TransitionUtils.copyViewImage(sceneRoot, startView,
379 startParent);
George Mountd4c3c912014-06-09 12:31:34 -0700380 } else if (startParent.getParent() == null) {
381 int id = startParent.getId();
382 if (id != View.NO_ID && sceneRoot.findViewById(id) != null
383 && mCanRemoveViews) {
384 // no parent, but its parent is unparented but the parent
385 // hierarchy has been replaced by a new hierarchy with the same id
386 // and it is safe to un-parent startView
387 overlayView = startView;
388 }
George Mountd6107a32014-03-10 16:51:16 -0700389 }
390 }
391 }
392 } else {
393 // visibility change
394 if (endVisibility == View.INVISIBLE) {
395 viewToKeep = endView;
396 } else {
397 // Becoming GONE
398 if (startView == endView) {
399 viewToKeep = endView;
400 } else {
401 overlayView = startView;
402 }
403 }
404 }
405 final int finalVisibility = endVisibility;
406 final ViewGroup finalSceneRoot = sceneRoot;
407
408 if (overlayView != null) {
409 // TODO: Need to do this for general case of adding to overlay
410 int[] screenLoc = (int[]) startValues.values.get(PROPNAME_SCREEN_LOCATION);
411 int screenX = screenLoc[0];
412 int screenY = screenLoc[1];
413 int[] loc = new int[2];
414 sceneRoot.getLocationOnScreen(loc);
415 overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft());
416 overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop());
417 sceneRoot.getOverlay().add(overlayView);
418 Animator animator = onDisappear(sceneRoot, overlayView, startValues, endValues);
419 if (animator == null) {
420 sceneRoot.getOverlay().remove(overlayView);
421 } else {
422 final View finalOverlayView = overlayView;
423 animator.addListener(new AnimatorListenerAdapter() {
424 @Override
425 public void onAnimationEnd(Animator animation) {
426 finalSceneRoot.getOverlay().remove(finalOverlayView);
427 }
George Mountd6107a32014-03-10 16:51:16 -0700428 });
429 }
430 return animator;
431 }
432
433 if (viewToKeep != null) {
George Mountb5ef7f82014-07-09 14:55:03 -0700434 int originalVisibility = -1;
435 final boolean isForcedVisibility = mForcedStartVisibility != -1 ||
436 mForcedEndVisibility != -1;
437 if (!isForcedVisibility) {
438 originalVisibility = viewToKeep.getVisibility();
439 viewToKeep.setVisibility(View.VISIBLE);
440 }
George Mountd6107a32014-03-10 16:51:16 -0700441 Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues);
George Mountb5ef7f82014-07-09 14:55:03 -0700442 if (animator != null) {
George Mountd6107a32014-03-10 16:51:16 -0700443 final View finalViewToKeep = viewToKeep;
444 animator.addListener(new AnimatorListenerAdapter() {
445 boolean mCanceled = false;
446
447 @Override
448 public void onAnimationPause(Animator animation) {
George Mountb5ef7f82014-07-09 14:55:03 -0700449 if (!mCanceled && !isForcedVisibility) {
George Mountd6107a32014-03-10 16:51:16 -0700450 finalViewToKeep.setVisibility(finalVisibility);
451 }
452 }
453
454 @Override
455 public void onAnimationResume(Animator animation) {
George Mountb5ef7f82014-07-09 14:55:03 -0700456 if (!mCanceled && !isForcedVisibility) {
George Mountd6107a32014-03-10 16:51:16 -0700457 finalViewToKeep.setVisibility(View.VISIBLE);
458 }
459 }
460
461 @Override
462 public void onAnimationCancel(Animator animation) {
463 mCanceled = true;
464 }
465
466 @Override
467 public void onAnimationEnd(Animator animation) {
468 if (!mCanceled) {
George Mountb5ef7f82014-07-09 14:55:03 -0700469 if (isForcedVisibility) {
470 finalViewToKeep.setTransitionAlpha(0);
471 } else {
472 finalViewToKeep.setVisibility(finalVisibility);
473 }
George Mountd6107a32014-03-10 16:51:16 -0700474 }
475 }
476 });
George Mountb5ef7f82014-07-09 14:55:03 -0700477 } else if (!isForcedVisibility) {
478 viewToKeep.setVisibility(originalVisibility);
George Mountd6107a32014-03-10 16:51:16 -0700479 }
480 return animator;
481 }
482 return null;
483 }
484
George Mount4c20ea22014-06-17 10:14:39 -0700485 @Override
486 boolean areValuesChanged(TransitionValues oldValues, TransitionValues newValues) {
487 VisibilityInfo changeInfo = getVisibilityChangeInfo(oldValues, newValues);
488 if (oldValues == null && newValues == null) {
489 return false;
490 }
491 return changeInfo.visibilityChange && (changeInfo.startVisibility == View.VISIBLE ||
492 changeInfo.endVisibility == View.VISIBLE);
493 }
494
George Mountd6107a32014-03-10 16:51:16 -0700495 /**
496 * The default implementation of this method returns a null Animator. Subclasses should
497 * override this method to make targets disappear with the desired transition. The
498 * method should only be called from
499 * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)}.
500 *
501 * @param sceneRoot The root of the transition hierarchy
502 * @param view The View to make disappear. This will be in the target scene's View
503 * hierarchy or in an {@link android.view.ViewGroupOverlay} and will be
504 * VISIBLE.
505 * @param startValues The target values in the start scene
506 * @param endValues The target values in the end scene
507 * @return An Animator to be started at the appropriate time in the
508 * overall transition for this scene change. A null value means no animation
509 * should be run.
510 */
511 public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
512 TransitionValues endValues) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700513 return null;
514 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700515}