blob: af2016ccf2a4b33e5801a1817b7cd5afbaaa946d [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
46 private static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
47 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,
66 PROPNAME_PARENT,
George Mountd6107a32014-03-10 16:51:16 -070067 PROPNAME_SCREEN_LOCATION,
Chet Haase199acdf2013-07-24 18:40:55 -070068 };
Chet Haasefaebd8f2012-05-18 14:17:57 -070069
Chet Haase6ebe3de2013-06-17 16:50:50 -070070 private static class VisibilityInfo {
71 boolean visibilityChange;
72 boolean fadeIn;
73 int startVisibility;
74 int endVisibility;
Chet Haase35a457a2013-08-26 07:34:12 -070075 ViewGroup startParent;
76 ViewGroup endParent;
Chet Haase6ebe3de2013-06-17 16:50:50 -070077 }
78
George Mountad88e1b2014-07-18 15:41:13 -070079 private int mMode = MODE_IN | MODE_OUT;
George Mount18ab7992014-06-25 17:04:51 -070080
George Mountb5ef7f82014-07-09 14:55:03 -070081 private int mForcedStartVisibility = -1;
82 private int mForcedEndVisibility = -1;
83
George Mountecd857b2014-06-19 07:51:08 -070084 public Visibility() {}
85
86 public Visibility(Context context, AttributeSet attrs) {
87 super(context, attrs);
88 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VisibilityTransition);
89 int mode = a.getInt(R.styleable.VisibilityTransition_visibilityMode, 0);
90 a.recycle();
91 if (mode != 0) {
92 setMode(mode);
93 }
94 }
95
George Mount18ab7992014-06-25 17:04:51 -070096 /**
97 * Changes the transition to support appearing and/or disappearing Views, depending
98 * on <code>mode</code>.
99 *
100 * @param mode The behavior supported by this transition, a combination of
George Mountad88e1b2014-07-18 15:41:13 -0700101 * {@link #MODE_IN} and {@link #MODE_OUT}.
George Mountecd857b2014-06-19 07:51:08 -0700102 * @attr ref android.R.styleable#VisibilityTransition_visibilityMode
George Mount18ab7992014-06-25 17:04:51 -0700103 */
104 public void setMode(int mode) {
George Mountad88e1b2014-07-18 15:41:13 -0700105 if ((mode & ~(MODE_IN | MODE_OUT)) != 0) {
106 throw new IllegalArgumentException("Only MODE_IN and MODE_OUT flags are allowed");
George Mount18ab7992014-06-25 17:04:51 -0700107 }
108 mMode = mode;
109 }
110
George Mountecd857b2014-06-19 07:51:08 -0700111 /**
112 * Returns whether appearing and/or disappearing Views are supported.
113 *
114 * Returns whether appearing and/or disappearing Views are supported. A combination of
George Mountad88e1b2014-07-18 15:41:13 -0700115 * {@link #MODE_IN} and {@link #MODE_OUT}.
George Mountecd857b2014-06-19 07:51:08 -0700116 * @attr ref android.R.styleable#VisibilityTransition_visibilityMode
117 */
118 public int getMode() {
119 return mMode;
120 }
121
Chet Haasefaebd8f2012-05-18 14:17:57 -0700122 @Override
Chet Haase199acdf2013-07-24 18:40:55 -0700123 public String[] getTransitionProperties() {
124 return sTransitionProperties;
125 }
126
George Mountb5ef7f82014-07-09 14:55:03 -0700127 private void captureValues(TransitionValues transitionValues, int forcedVisibility) {
128 int visibility;
129 if (forcedVisibility != -1) {
130 visibility = forcedVisibility;
131 } else {
132 visibility = transitionValues.view.getVisibility();
133 }
Chet Haased82c8ac2013-08-26 14:20:16 -0700134 transitionValues.values.put(PROPNAME_VISIBILITY, visibility);
135 transitionValues.values.put(PROPNAME_PARENT, transitionValues.view.getParent());
George Mountd6107a32014-03-10 16:51:16 -0700136 int[] loc = new int[2];
137 transitionValues.view.getLocationOnScreen(loc);
138 transitionValues.values.put(PROPNAME_SCREEN_LOCATION, loc);
Chet Haased82c8ac2013-08-26 14:20:16 -0700139 }
140
Chet Haase199acdf2013-07-24 18:40:55 -0700141 @Override
Chet Haased82c8ac2013-08-26 14:20:16 -0700142 public void captureStartValues(TransitionValues transitionValues) {
George Mountb5ef7f82014-07-09 14:55:03 -0700143 captureValues(transitionValues, mForcedStartVisibility);
Chet Haased82c8ac2013-08-26 14:20:16 -0700144 }
145
146 @Override
147 public void captureEndValues(TransitionValues transitionValues) {
George Mountb5ef7f82014-07-09 14:55:03 -0700148 captureValues(transitionValues, mForcedEndVisibility);
149 }
150
151 /** @hide */
152 @Override
153 public void forceVisibility(int visibility, boolean isStartValue) {
154 if (isStartValue) {
155 mForcedStartVisibility = visibility;
156 } else {
157 mForcedEndVisibility = visibility;
158 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700159 }
160
Chet Haase199acdf2013-07-24 18:40:55 -0700161 /**
162 * Returns whether the view is 'visible' according to the given values
163 * object. This is determined by testing the same properties in the values
164 * object that are used to determine whether the object is appearing or
165 * disappearing in the {@link
Chet Haased82c8ac2013-08-26 14:20:16 -0700166 * Transition#createAnimator(ViewGroup, TransitionValues, TransitionValues)}
Chet Haase199acdf2013-07-24 18:40:55 -0700167 * method. This method can be called by, for example, subclasses that want
168 * to know whether the object is visible in the same way that Visibility
169 * determines it for the actual animation.
170 *
171 * @param values The TransitionValues object that holds the information by
172 * which visibility is determined.
173 * @return True if the view reference by <code>values</code> is visible,
174 * false otherwise.
175 */
176 public boolean isVisible(TransitionValues values) {
177 if (values == null) {
178 return false;
179 }
180 int visibility = (Integer) values.values.get(PROPNAME_VISIBILITY);
181 View parent = (View) values.values.get(PROPNAME_PARENT);
182
183 return visibility == View.VISIBLE && parent != null;
184 }
185
Chet Haase6ebe3de2013-06-17 16:50:50 -0700186 private VisibilityInfo getVisibilityChangeInfo(TransitionValues startValues,
Chet Haasefaebd8f2012-05-18 14:17:57 -0700187 TransitionValues endValues) {
Chet Haase7660d122013-09-13 13:29:31 -0700188 final VisibilityInfo visInfo = new VisibilityInfo();
Chet Haase6ebe3de2013-06-17 16:50:50 -0700189 visInfo.visibilityChange = false;
190 visInfo.fadeIn = false;
George Mount31a21722014-03-24 17:44:36 -0700191 if (startValues != null && startValues.values.containsKey(PROPNAME_VISIBILITY)) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700192 visInfo.startVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY);
Chet Haase35a457a2013-08-26 07:34:12 -0700193 visInfo.startParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700194 } else {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700195 visInfo.startVisibility = -1;
196 visInfo.startParent = null;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700197 }
George Mount31a21722014-03-24 17:44:36 -0700198 if (endValues != null && endValues.values.containsKey(PROPNAME_VISIBILITY)) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700199 visInfo.endVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY);
Chet Haase35a457a2013-08-26 07:34:12 -0700200 visInfo.endParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700201 } else {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700202 visInfo.endVisibility = -1;
203 visInfo.endParent = null;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700204 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700205 if (startValues != null && endValues != null) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700206 if (visInfo.startVisibility == visInfo.endVisibility &&
207 visInfo.startParent == visInfo.endParent) {
208 return visInfo;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700209 } else {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700210 if (visInfo.startVisibility != visInfo.endVisibility) {
211 if (visInfo.startVisibility == View.VISIBLE) {
212 visInfo.fadeIn = false;
213 visInfo.visibilityChange = true;
214 } else if (visInfo.endVisibility == View.VISIBLE) {
215 visInfo.fadeIn = true;
216 visInfo.visibilityChange = true;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700217 }
218 // no visibilityChange if going between INVISIBLE and GONE
Chet Haase6ebe3de2013-06-17 16:50:50 -0700219 } else if (visInfo.startParent != visInfo.endParent) {
220 if (visInfo.endParent == null) {
221 visInfo.fadeIn = false;
222 visInfo.visibilityChange = true;
223 } else if (visInfo.startParent == null) {
224 visInfo.fadeIn = true;
225 visInfo.visibilityChange = true;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700226 }
227 }
228 }
229 }
230 if (startValues == null) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700231 visInfo.fadeIn = true;
232 visInfo.visibilityChange = true;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700233 } else if (endValues == null) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700234 visInfo.fadeIn = false;
235 visInfo.visibilityChange = true;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700236 }
Chet Haase6ebe3de2013-06-17 16:50:50 -0700237 return visInfo;
238 }
239
240 @Override
Chet Haased82c8ac2013-08-26 14:20:16 -0700241 public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
Chet Haase6ebe3de2013-06-17 16:50:50 -0700242 TransitionValues endValues) {
243 VisibilityInfo visInfo = getVisibilityChangeInfo(startValues, endValues);
George Mount30da61d2014-05-09 13:17:52 -0700244 if (visInfo.visibilityChange
245 && (visInfo.startParent != null || visInfo.endParent != null)) {
246 if (visInfo.fadeIn) {
247 return onAppear(sceneRoot, startValues, visInfo.startVisibility,
248 endValues, visInfo.endVisibility);
249 } else {
250 return onDisappear(sceneRoot, startValues, visInfo.startVisibility,
251 endValues, visInfo.endVisibility
252 );
Chet Haasefaebd8f2012-05-18 14:17:57 -0700253 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700254 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700255 return null;
256 }
257
258 /**
George Mountd6107a32014-03-10 16:51:16 -0700259 * The default implementation of this method calls
260 * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}.
261 * Subclasses should override this method or
262 * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}.
263 * if they need to create an Animator when targets appear.
Chet Haased82c8ac2013-08-26 14:20:16 -0700264 * The method should only be called by the Visibility class; it is
265 * not intended to be called from external classes.
Chet Haasefaebd8f2012-05-18 14:17:57 -0700266 *
Chet Haased82c8ac2013-08-26 14:20:16 -0700267 * @param sceneRoot The root of the transition hierarchy
268 * @param startValues The target values in the start scene
269 * @param startVisibility The target visibility in the start scene
270 * @param endValues The target values in the end scene
271 * @param endVisibility The target visibility in the end scene
272 * @return An Animator to be started at the appropriate time in the
273 * overall transition for this scene change. A null value means no animation
274 * should be run.
Chet Haasefaebd8f2012-05-18 14:17:57 -0700275 */
Chet Haased82c8ac2013-08-26 14:20:16 -0700276 public Animator onAppear(ViewGroup sceneRoot,
Chet Haase6ebe3de2013-06-17 16:50:50 -0700277 TransitionValues startValues, int startVisibility,
278 TransitionValues endValues, int endVisibility) {
George Mountad88e1b2014-07-18 15:41:13 -0700279 if ((mMode & MODE_IN) != MODE_IN || endValues == null) {
George Mount18ab7992014-06-25 17:04:51 -0700280 return null;
281 }
George Mountd6107a32014-03-10 16:51:16 -0700282 return onAppear(sceneRoot, endValues.view, startValues, endValues);
283 }
284
285 /**
286 * The default implementation of this method returns a null Animator. Subclasses should
287 * override this method to make targets appear with the desired transition. The
288 * method should only be called from
289 * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)}.
290 *
291 * @param sceneRoot The root of the transition hierarchy
292 * @param view The View to make appear. This will be in the target scene's View hierarchy and
293 * will be VISIBLE.
294 * @param startValues The target values in the start scene
295 * @param endValues The target values in the end scene
296 * @return An Animator to be started at the appropriate time in the
297 * overall transition for this scene change. A null value means no animation
298 * should be run.
299 */
300 public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
301 TransitionValues endValues) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700302 return null;
303 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700304
305 /**
George Mountd6107a32014-03-10 16:51:16 -0700306 * Subclasses should override this method or
307 * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}
308 * if they need to create an Animator when targets disappear.
Chet Haased82c8ac2013-08-26 14:20:16 -0700309 * The method should only be called by the Visibility class; it is
310 * not intended to be called from external classes.
George Mountd6107a32014-03-10 16:51:16 -0700311 * <p>
312 * The default implementation of this method attempts to find a View to use to call
313 * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)},
314 * based on the situation of the View in the View hierarchy. For example,
315 * if a View was simply removed from its parent, then the View will be added
316 * into a {@link android.view.ViewGroupOverlay} and passed as the <code>view</code>
317 * parameter in {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}.
318 * If a visible View is changed to be {@link View#GONE} or {@link View#INVISIBLE},
319 * then it can be used as the <code>view</code> and the visibility will be changed
320 * to {@link View#VISIBLE} for the duration of the animation. However, if a View
321 * is in a hierarchy which is also altering its visibility, the situation can be
322 * more complicated. In general, if a view that is no longer in the hierarchy in
323 * the end scene still has a parent (so its parent hierarchy was removed, but it
324 * was not removed from its parent), then it will be left alone to avoid side-effects from
325 * improperly removing it from its parent. The only exception to this is if
326 * the previous {@link Scene} was {@link Scene#getSceneForLayout(ViewGroup, int,
327 * android.content.Context) created from a layout resource file}, then it is considered
328 * safe to un-parent the starting scene view in order to make it disappear.</p>
Chet Haased82c8ac2013-08-26 14:20:16 -0700329 *
330 * @param sceneRoot The root of the transition hierarchy
331 * @param startValues The target values in the start scene
332 * @param startVisibility The target visibility in the start scene
333 * @param endValues The target values in the end scene
334 * @param endVisibility The target visibility in the end scene
335 * @return An Animator to be started at the appropriate time in the
336 * overall transition for this scene change. A null value means no animation
337 * should be run.
Chet Haasefaebd8f2012-05-18 14:17:57 -0700338 */
Chet Haased82c8ac2013-08-26 14:20:16 -0700339 public Animator onDisappear(ViewGroup sceneRoot,
Chet Haase6ebe3de2013-06-17 16:50:50 -0700340 TransitionValues startValues, int startVisibility,
341 TransitionValues endValues, int endVisibility) {
George Mountad88e1b2014-07-18 15:41:13 -0700342 if ((mMode & MODE_OUT) != MODE_OUT) {
George Mount18ab7992014-06-25 17:04:51 -0700343 return null;
344 }
345
George Mountd6107a32014-03-10 16:51:16 -0700346 View startView = (startValues != null) ? startValues.view : null;
347 View endView = (endValues != null) ? endValues.view : null;
348 View overlayView = null;
349 View viewToKeep = null;
350 if (endView == null || endView.getParent() == null) {
351 if (endView != null) {
352 // endView was removed from its parent - add it to the overlay
353 overlayView = endView;
354 } else if (startView != null) {
355 // endView does not exist. Use startView only under certain
356 // conditions, because placing a view in an overlay necessitates
357 // it being removed from its current parent
358 if (startView.getParent() == null) {
359 // no parent - safe to use
360 overlayView = startView;
George Mountd4c3c912014-06-09 12:31:34 -0700361 } else if (startView.getParent() instanceof View) {
George Mountd6107a32014-03-10 16:51:16 -0700362 View startParent = (View) startView.getParent();
George Mountd4c3c912014-06-09 12:31:34 -0700363 if (!isValidTarget(startParent)) {
364 if (startView.isAttachedToWindow()) {
365 overlayView = copyViewImage(startView);
366 } else {
367 overlayView = startView;
368 }
369 } else if (startParent.getParent() == null) {
370 int id = startParent.getId();
371 if (id != View.NO_ID && sceneRoot.findViewById(id) != null
372 && mCanRemoveViews) {
373 // no parent, but its parent is unparented but the parent
374 // hierarchy has been replaced by a new hierarchy with the same id
375 // and it is safe to un-parent startView
376 overlayView = startView;
377 }
George Mountd6107a32014-03-10 16:51:16 -0700378 }
379 }
380 }
381 } else {
382 // visibility change
383 if (endVisibility == View.INVISIBLE) {
384 viewToKeep = endView;
385 } else {
386 // Becoming GONE
387 if (startView == endView) {
388 viewToKeep = endView;
389 } else {
390 overlayView = startView;
391 }
392 }
393 }
394 final int finalVisibility = endVisibility;
395 final ViewGroup finalSceneRoot = sceneRoot;
396
397 if (overlayView != null) {
398 // TODO: Need to do this for general case of adding to overlay
399 int[] screenLoc = (int[]) startValues.values.get(PROPNAME_SCREEN_LOCATION);
400 int screenX = screenLoc[0];
401 int screenY = screenLoc[1];
402 int[] loc = new int[2];
403 sceneRoot.getLocationOnScreen(loc);
404 overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft());
405 overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop());
406 sceneRoot.getOverlay().add(overlayView);
407 Animator animator = onDisappear(sceneRoot, overlayView, startValues, endValues);
408 if (animator == null) {
409 sceneRoot.getOverlay().remove(overlayView);
410 } else {
411 final View finalOverlayView = overlayView;
412 animator.addListener(new AnimatorListenerAdapter() {
413 @Override
414 public void onAnimationEnd(Animator animation) {
415 finalSceneRoot.getOverlay().remove(finalOverlayView);
416 }
George Mountd6107a32014-03-10 16:51:16 -0700417 });
418 }
419 return animator;
420 }
421
422 if (viewToKeep != null) {
George Mountb5ef7f82014-07-09 14:55:03 -0700423 int originalVisibility = -1;
424 final boolean isForcedVisibility = mForcedStartVisibility != -1 ||
425 mForcedEndVisibility != -1;
426 if (!isForcedVisibility) {
427 originalVisibility = viewToKeep.getVisibility();
428 viewToKeep.setVisibility(View.VISIBLE);
429 }
George Mountd6107a32014-03-10 16:51:16 -0700430 Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues);
George Mountb5ef7f82014-07-09 14:55:03 -0700431 if (animator != null) {
George Mountd6107a32014-03-10 16:51:16 -0700432 final View finalViewToKeep = viewToKeep;
433 animator.addListener(new AnimatorListenerAdapter() {
434 boolean mCanceled = false;
435
436 @Override
437 public void onAnimationPause(Animator animation) {
George Mountb5ef7f82014-07-09 14:55:03 -0700438 if (!mCanceled && !isForcedVisibility) {
George Mountd6107a32014-03-10 16:51:16 -0700439 finalViewToKeep.setVisibility(finalVisibility);
440 }
441 }
442
443 @Override
444 public void onAnimationResume(Animator animation) {
George Mountb5ef7f82014-07-09 14:55:03 -0700445 if (!mCanceled && !isForcedVisibility) {
George Mountd6107a32014-03-10 16:51:16 -0700446 finalViewToKeep.setVisibility(View.VISIBLE);
447 }
448 }
449
450 @Override
451 public void onAnimationCancel(Animator animation) {
452 mCanceled = true;
453 }
454
455 @Override
456 public void onAnimationEnd(Animator animation) {
457 if (!mCanceled) {
George Mountb5ef7f82014-07-09 14:55:03 -0700458 if (isForcedVisibility) {
459 finalViewToKeep.setTransitionAlpha(0);
460 } else {
461 finalViewToKeep.setVisibility(finalVisibility);
462 }
George Mountd6107a32014-03-10 16:51:16 -0700463 }
464 }
465 });
George Mountb5ef7f82014-07-09 14:55:03 -0700466 } else if (!isForcedVisibility) {
467 viewToKeep.setVisibility(originalVisibility);
George Mountd6107a32014-03-10 16:51:16 -0700468 }
469 return animator;
470 }
471 return null;
472 }
473
George Mountd4c3c912014-06-09 12:31:34 -0700474 private View copyViewImage(View view) {
475 int width = view.getWidth();
476 int height = view.getHeight();
477 if (width <= 0 || height <= 0) {
478 return null;
479 }
480 Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
481 Canvas canvas = new Canvas(bitmap);
482 view.draw(canvas);
483 final BitmapDrawable drawable = new BitmapDrawable(bitmap);
484
485 View overlayView = new View(view.getContext());
486 overlayView.setBackground(drawable);
487 int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
488 int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
489 overlayView.measure(widthSpec, heightSpec);
490 overlayView.layout(0, 0, width, height);
491 return overlayView;
492 }
493
George Mount4c20ea22014-06-17 10:14:39 -0700494 @Override
495 boolean areValuesChanged(TransitionValues oldValues, TransitionValues newValues) {
496 VisibilityInfo changeInfo = getVisibilityChangeInfo(oldValues, newValues);
497 if (oldValues == null && newValues == null) {
498 return false;
499 }
500 return changeInfo.visibilityChange && (changeInfo.startVisibility == View.VISIBLE ||
501 changeInfo.endVisibility == View.VISIBLE);
502 }
503
George Mountd6107a32014-03-10 16:51:16 -0700504 /**
505 * The default implementation of this method returns a null Animator. Subclasses should
506 * override this method to make targets disappear with the desired transition. The
507 * method should only be called from
508 * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)}.
509 *
510 * @param sceneRoot The root of the transition hierarchy
511 * @param view The View to make disappear. This will be in the target scene's View
512 * hierarchy or in an {@link android.view.ViewGroupOverlay} and will be
513 * VISIBLE.
514 * @param startValues The target values in the start scene
515 * @param endValues The target values in the end scene
516 * @return An Animator to be started at the appropriate time in the
517 * overall transition for this scene change. A null value means no animation
518 * should be run.
519 */
520 public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
521 TransitionValues endValues) {
Chet Haase6ebe3de2013-06-17 16:50:50 -0700522 return null;
523 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700524}