• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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  */
16 
17 package android.transition;
18 
19 import android.animation.Animator;
20 import android.animation.Animator.AnimatorListener;
21 import android.animation.Animator.AnimatorPauseListener;
22 import android.annotation.IntDef;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.content.Context;
26 import android.content.res.TypedArray;
27 import android.util.AttributeSet;
28 import android.view.View;
29 import android.view.ViewGroup;
30 import android.view.ViewGroupOverlay;
31 
32 import com.android.internal.R;
33 
34 import java.lang.annotation.Retention;
35 import java.lang.annotation.RetentionPolicy;
36 
37 /**
38  * This transition tracks changes to the visibility of target views in the
39  * start and end scenes. Visibility is determined not just by the
40  * {@link View#setVisibility(int)} state of views, but also whether
41  * views exist in the current view hierarchy. The class is intended to be a
42  * utility for subclasses such as {@link Fade}, which use this visibility
43  * information to determine the specific animations to run when visibility
44  * changes occur. Subclasses should implement one or both of the methods
45  * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)},
46  * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)} or
47  * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)},
48  * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}.
49  */
50 public abstract class Visibility extends Transition {
51 
52     static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
53     private static final String PROPNAME_PARENT = "android:visibility:parent";
54     private static final String PROPNAME_SCREEN_LOCATION = "android:visibility:screenLocation";
55 
56     /** @hide */
57     @Retention(RetentionPolicy.SOURCE)
58     @IntDef(flag = true, value = {
59             MODE_IN,
60             MODE_OUT,
61             Fade.IN,
62             Fade.OUT
63     })
64     @interface VisibilityMode {}
65 
66     /**
67      * Mode used in {@link #setMode(int)} to make the transition
68      * operate on targets that are appearing. Maybe be combined with
69      * {@link #MODE_OUT} to target Visibility changes both in and out.
70      */
71     public static final int MODE_IN = 0x1;
72 
73     /**
74      * Mode used in {@link #setMode(int)} to make the transition
75      * operate on targets that are disappearing. Maybe be combined with
76      * {@link #MODE_IN} to target Visibility changes both in and out.
77      */
78     public static final int MODE_OUT = 0x2;
79 
80     private static final String[] sTransitionProperties = {
81             PROPNAME_VISIBILITY,
82             PROPNAME_PARENT,
83     };
84 
85     private static class VisibilityInfo {
86         boolean visibilityChange;
87         boolean fadeIn;
88         int startVisibility;
89         int endVisibility;
90         ViewGroup startParent;
91         ViewGroup endParent;
92     }
93 
94     private int mMode = MODE_IN | MODE_OUT;
95     private boolean mSuppressLayout = true;
96 
Visibility()97     public Visibility() {}
98 
Visibility(Context context, AttributeSet attrs)99     public Visibility(Context context, AttributeSet attrs) {
100         super(context, attrs);
101         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VisibilityTransition);
102         int mode = a.getInt(R.styleable.VisibilityTransition_transitionVisibilityMode, 0);
103         a.recycle();
104         if (mode != 0) {
105             setMode(mode);
106         }
107     }
108 
109     /**
110      * This tells the Visibility transition to suppress layout during the transition and release
111      * the suppression after the transition.
112      * @hide
113      */
setSuppressLayout(boolean suppress)114     public void setSuppressLayout(boolean suppress) {
115         this.mSuppressLayout = suppress;
116     }
117 
118     /**
119      * Changes the transition to support appearing and/or disappearing Views, depending
120      * on <code>mode</code>.
121      *
122      * @param mode The behavior supported by this transition, a combination of
123      *             {@link #MODE_IN} and {@link #MODE_OUT}.
124      * @attr ref android.R.styleable#VisibilityTransition_transitionVisibilityMode
125      */
setMode(@isibilityMode int mode)126     public void setMode(@VisibilityMode int mode) {
127         if ((mode & ~(MODE_IN | MODE_OUT)) != 0) {
128             throw new IllegalArgumentException("Only MODE_IN and MODE_OUT flags are allowed");
129         }
130         mMode = mode;
131     }
132 
133     /**
134      * Returns whether appearing and/or disappearing Views are supported.
135      *
136      * Returns whether appearing and/or disappearing Views are supported. A combination of
137      *         {@link #MODE_IN} and {@link #MODE_OUT}.
138      * @attr ref android.R.styleable#VisibilityTransition_transitionVisibilityMode
139      */
140     @VisibilityMode
getMode()141     public int getMode() {
142         return mMode;
143     }
144 
145     @Override
getTransitionProperties()146     public String[] getTransitionProperties() {
147         return sTransitionProperties;
148     }
149 
captureValues(TransitionValues transitionValues)150     private void captureValues(TransitionValues transitionValues) {
151         int visibility = transitionValues.view.getVisibility();
152         transitionValues.values.put(PROPNAME_VISIBILITY, visibility);
153         transitionValues.values.put(PROPNAME_PARENT, transitionValues.view.getParent());
154         int[] loc = new int[2];
155         transitionValues.view.getLocationOnScreen(loc);
156         transitionValues.values.put(PROPNAME_SCREEN_LOCATION, loc);
157     }
158 
159     @Override
captureStartValues(TransitionValues transitionValues)160     public void captureStartValues(TransitionValues transitionValues) {
161         captureValues(transitionValues);
162     }
163 
164     @Override
captureEndValues(TransitionValues transitionValues)165     public void captureEndValues(TransitionValues transitionValues) {
166         captureValues(transitionValues);
167     }
168 
169     /**
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
174      * Transition#createAnimator(ViewGroup, TransitionValues, TransitionValues)}
175      * 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      */
isVisible(TransitionValues values)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 
getVisibilityChangeInfo(TransitionValues startValues, TransitionValues endValues)194     private static VisibilityInfo getVisibilityChangeInfo(TransitionValues startValues,
195             TransitionValues endValues) {
196         final VisibilityInfo visInfo = new VisibilityInfo();
197         visInfo.visibilityChange = false;
198         visInfo.fadeIn = false;
199         if (startValues != null && startValues.values.containsKey(PROPNAME_VISIBILITY)) {
200             visInfo.startVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY);
201             visInfo.startParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT);
202         } else {
203             visInfo.startVisibility = -1;
204             visInfo.startParent = null;
205         }
206         if (endValues != null && endValues.values.containsKey(PROPNAME_VISIBILITY)) {
207             visInfo.endVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY);
208             visInfo.endParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT);
209         } else {
210             visInfo.endVisibility = -1;
211             visInfo.endParent = null;
212         }
213         if (startValues != null && endValues != null) {
214             if (visInfo.startVisibility == visInfo.endVisibility &&
215                     visInfo.startParent == visInfo.endParent) {
216                 return visInfo;
217             } else {
218                 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;
225                     }
226                     // no visibilityChange if going between INVISIBLE and GONE
227                 } 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;
234                     }
235                 }
236             }
237         } else if (startValues == null && visInfo.endVisibility == View.VISIBLE) {
238             visInfo.fadeIn = true;
239             visInfo.visibilityChange = true;
240         } else if (endValues == null && visInfo.startVisibility == View.VISIBLE) {
241             visInfo.fadeIn = false;
242             visInfo.visibilityChange = true;
243         }
244         return visInfo;
245     }
246 
247     @Nullable
248     @Override
createAnimator(@onNull ViewGroup sceneRoot, @Nullable TransitionValues startValues, @Nullable TransitionValues endValues)249     public Animator createAnimator(@NonNull ViewGroup sceneRoot,
250             @Nullable TransitionValues startValues,
251             @Nullable TransitionValues endValues) {
252         VisibilityInfo visInfo = getVisibilityChangeInfo(startValues, endValues);
253         if (visInfo.visibilityChange
254                 && (visInfo.startParent != null || visInfo.endParent != null)) {
255             if (visInfo.fadeIn) {
256                 return onAppear(sceneRoot, startValues, visInfo.startVisibility,
257                         endValues, visInfo.endVisibility);
258             } else {
259                 return onDisappear(sceneRoot, startValues, visInfo.startVisibility,
260                         endValues, visInfo.endVisibility
261                 );
262             }
263         }
264         return null;
265     }
266 
267     /**
268      * The default implementation of this method calls
269      * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}.
270      * Subclasses should override this method or
271      * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}.
272      * if they need to create an Animator when targets appear.
273      * The method should only be called by the Visibility class; it is
274      * not intended to be called from external classes.
275      *
276      * @param sceneRoot The root of the transition hierarchy
277      * @param startValues The target values in the start scene
278      * @param startVisibility The target visibility in the start scene
279      * @param endValues The target values in the end scene
280      * @param endVisibility The target visibility in the end scene
281      * @return An Animator to be started at the appropriate time in the
282      * overall transition for this scene change. A null value means no animation
283      * should be run.
284      */
onAppear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility, TransitionValues endValues, int endVisibility)285     public Animator onAppear(ViewGroup sceneRoot,
286             TransitionValues startValues, int startVisibility,
287             TransitionValues endValues, int endVisibility) {
288         if ((mMode & MODE_IN) != MODE_IN || endValues == null) {
289             return null;
290         }
291         if (startValues == null) {
292             VisibilityInfo parentVisibilityInfo = null;
293             View endParent = (View) endValues.view.getParent();
294             TransitionValues startParentValues = getMatchedTransitionValues(endParent,
295                                                                             false);
296             TransitionValues endParentValues = getTransitionValues(endParent, false);
297             parentVisibilityInfo =
298                 getVisibilityChangeInfo(startParentValues, endParentValues);
299             if (parentVisibilityInfo.visibilityChange) {
300                 return null;
301             }
302         }
303         return onAppear(sceneRoot, endValues.view, startValues, endValues);
304     }
305 
306     /**
307      * The default implementation of this method returns a null Animator. Subclasses should
308      * override this method to make targets appear with the desired transition. The
309      * method should only be called from
310      * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)}.
311      *
312      * @param sceneRoot The root of the transition hierarchy
313      * @param view The View to make appear. This will be in the target scene's View hierarchy and
314      *             will be VISIBLE.
315      * @param startValues The target values in the start scene
316      * @param endValues The target values in the end scene
317      * @return An Animator to be started at the appropriate time in the
318      * overall transition for this scene change. A null value means no animation
319      * should be run.
320      */
onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues)321     public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
322             TransitionValues endValues) {
323         return null;
324     }
325 
326     /**
327      * Subclasses should override this method or
328      * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}
329      * if they need to create an Animator when targets disappear.
330      * The method should only be called by the Visibility class; it is
331      * not intended to be called from external classes.
332      * <p>
333      * The default implementation of this method attempts to find a View to use to call
334      * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)},
335      * based on the situation of the View in the View hierarchy. For example,
336      * if a View was simply removed from its parent, then the View will be added
337      * into a {@link android.view.ViewGroupOverlay} and passed as the <code>view</code>
338      * parameter in {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}.
339      * If a visible View is changed to be {@link View#GONE} or {@link View#INVISIBLE},
340      * then it can be used as the <code>view</code> and the visibility will be changed
341      * to {@link View#VISIBLE} for the duration of the animation. However, if a View
342      * is in a hierarchy which is also altering its visibility, the situation can be
343      * more complicated. In general, if a view that is no longer in the hierarchy in
344      * the end scene still has a parent (so its parent hierarchy was removed, but it
345      * was not removed from its parent), then it will be left alone to avoid side-effects from
346      * improperly removing it from its parent. The only exception to this is if
347      * the previous {@link Scene} was {@link Scene#getSceneForLayout(ViewGroup, int,
348      * android.content.Context) created from a layout resource file}, then it is considered
349      * safe to un-parent the starting scene view in order to make it disappear.</p>
350      *
351      * @param sceneRoot The root of the transition hierarchy
352      * @param startValues The target values in the start scene
353      * @param startVisibility The target visibility in the start scene
354      * @param endValues The target values in the end scene
355      * @param endVisibility The target visibility in the end scene
356      * @return An Animator to be started at the appropriate time in the
357      * overall transition for this scene change. A null value means no animation
358      * should be run.
359      */
onDisappear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility, TransitionValues endValues, int endVisibility)360     public Animator onDisappear(ViewGroup sceneRoot,
361             TransitionValues startValues, int startVisibility,
362             TransitionValues endValues, int endVisibility) {
363         if ((mMode & MODE_OUT) != MODE_OUT) {
364             return null;
365         }
366 
367         if (startValues == null) {
368             // startValues(and startView) will never be null for disappear transition.
369             return null;
370         }
371 
372         final View startView = startValues.view;
373         final View endView = (endValues != null) ? endValues.view : null;
374         View overlayView = null;
375         View viewToKeep = null;
376         boolean reusingOverlayView = false;
377 
378         View savedOverlayView = (View) startView.getTag(R.id.transition_overlay_view_tag);
379         if (savedOverlayView != null) {
380             // we've already created overlay for the start view.
381             // it means that we are applying two visibility
382             // transitions for the same view
383             overlayView = savedOverlayView;
384             reusingOverlayView = true;
385         } else {
386             boolean needOverlayForStartView = false;
387 
388             if (endView == null || endView.getParent() == null) {
389                 if (endView != null) {
390                     // endView was removed from its parent - add it to the overlay
391                     overlayView = endView;
392                 } else {
393                     needOverlayForStartView = true;
394                 }
395             } else {
396                 // visibility change
397                 if (endVisibility == View.INVISIBLE) {
398                     viewToKeep = endView;
399                 } else {
400                     // Becoming GONE
401                     if (startView == endView) {
402                         viewToKeep = endView;
403                     } else {
404                         needOverlayForStartView = true;
405                     }
406                 }
407             }
408 
409             if (needOverlayForStartView) {
410                 // endView does not exist. Use startView only under certain
411                 // conditions, because placing a view in an overlay necessitates
412                 // it being removed from its current parent
413                 if (startView.getParent() == null) {
414                     // no parent - safe to use
415                     overlayView = startView;
416                 } else if (startView.getParent() instanceof View) {
417                     View startParent = (View) startView.getParent();
418                     TransitionValues startParentValues = getTransitionValues(startParent, true);
419                     TransitionValues endParentValues = getMatchedTransitionValues(startParent,
420                             true);
421                     VisibilityInfo parentVisibilityInfo =
422                             getVisibilityChangeInfo(startParentValues, endParentValues);
423                     if (!parentVisibilityInfo.visibilityChange) {
424                         overlayView = TransitionUtils.copyViewImage(sceneRoot, startView,
425                                 startParent);
426                     } else {
427                         int id = startParent.getId();
428                         if (startParent.getParent() == null && id != View.NO_ID
429                                 && sceneRoot.findViewById(id) != null && mCanRemoveViews) {
430                             // no parent, but its parent is unparented  but the parent
431                             // hierarchy has been replaced by a new hierarchy with the same id
432                             // and it is safe to un-parent startView
433                             overlayView = startView;
434                         } else {
435                             // TODO: Handle this case as well
436                         }
437                     }
438                 }
439             }
440         }
441 
442         if (overlayView != null) {
443             // TODO: Need to do this for general case of adding to overlay
444             final ViewGroupOverlay overlay;
445             if (!reusingOverlayView) {
446                 overlay = sceneRoot.getOverlay();
447                 int[] screenLoc = (int[]) startValues.values.get(PROPNAME_SCREEN_LOCATION);
448                 int screenX = screenLoc[0];
449                 int screenY = screenLoc[1];
450                 int[] loc = new int[2];
451                 sceneRoot.getLocationOnScreen(loc);
452                 overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft());
453                 overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop());
454                 overlay.add(overlayView);
455             } else {
456                 overlay = null;
457             }
458             Animator animator = onDisappear(sceneRoot, overlayView, startValues, endValues);
459             if (!reusingOverlayView) {
460                 if (animator == null) {
461                     overlay.remove(overlayView);
462                 } else {
463                     startView.setTagInternal(R.id.transition_overlay_view_tag, overlayView);
464                     final View finalOverlayView = overlayView;
465                     addListener(new TransitionListenerAdapter() {
466 
467                         @Override
468                         public void onTransitionPause(Transition transition) {
469                             overlay.remove(finalOverlayView);
470                         }
471 
472                         @Override
473                         public void onTransitionResume(Transition transition) {
474                             if (finalOverlayView.getParent() == null) {
475                                 overlay.add(finalOverlayView);
476                             } else {
477                                 cancel();
478                             }
479                         }
480 
481                         @Override
482                         public void onTransitionEnd(Transition transition) {
483                             startView.setTagInternal(R.id.transition_overlay_view_tag, null);
484                             overlay.remove(finalOverlayView);
485                             transition.removeListener(this);
486                         }
487                     });
488                 }
489             }
490             return animator;
491         }
492 
493         if (viewToKeep != null) {
494             int originalVisibility = viewToKeep.getVisibility();
495             viewToKeep.setTransitionVisibility(View.VISIBLE);
496             Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues);
497             if (animator != null) {
498                 DisappearListener disappearListener = new DisappearListener(viewToKeep,
499                         endVisibility, mSuppressLayout);
500                 animator.addListener(disappearListener);
501                 animator.addPauseListener(disappearListener);
502                 addListener(disappearListener);
503             } else {
504                 viewToKeep.setTransitionVisibility(originalVisibility);
505             }
506             return animator;
507         }
508         return null;
509     }
510 
511     @Override
isTransitionRequired(TransitionValues startValues, TransitionValues newValues)512     public boolean isTransitionRequired(TransitionValues startValues, TransitionValues newValues) {
513         if (startValues == null && newValues == null) {
514             return false;
515         }
516         if (startValues != null && newValues != null &&
517                 newValues.values.containsKey(PROPNAME_VISIBILITY) !=
518                         startValues.values.containsKey(PROPNAME_VISIBILITY)) {
519             // The transition wasn't targeted in either the start or end, so it couldn't
520             // have changed.
521             return false;
522         }
523         VisibilityInfo changeInfo = getVisibilityChangeInfo(startValues, newValues);
524         return changeInfo.visibilityChange && (changeInfo.startVisibility == View.VISIBLE ||
525                 changeInfo.endVisibility == View.VISIBLE);
526     }
527 
528     /**
529      * The default implementation of this method returns a null Animator. Subclasses should
530      * override this method to make targets disappear with the desired transition. The
531      * method should only be called from
532      * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)}.
533      *
534      * @param sceneRoot The root of the transition hierarchy
535      * @param view The View to make disappear. This will be in the target scene's View
536      *             hierarchy or in an {@link android.view.ViewGroupOverlay} and will be
537      *             VISIBLE.
538      * @param startValues The target values in the start scene
539      * @param endValues The target values in the end scene
540      * @return An Animator to be started at the appropriate time in the
541      * overall transition for this scene change. A null value means no animation
542      * should be run.
543      */
onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues)544     public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
545             TransitionValues endValues) {
546         return null;
547     }
548 
549     private static class DisappearListener
550             extends TransitionListenerAdapter implements AnimatorListener, AnimatorPauseListener {
551         private final View mView;
552         private final int mFinalVisibility;
553         private final ViewGroup mParent;
554         private final boolean mSuppressLayout;
555 
556         private boolean mLayoutSuppressed;
557         boolean mCanceled = false;
558 
DisappearListener(View view, int finalVisibility, boolean suppressLayout)559         public DisappearListener(View view, int finalVisibility, boolean suppressLayout) {
560             this.mView = view;
561             this.mFinalVisibility = finalVisibility;
562             this.mParent = (ViewGroup) view.getParent();
563             this.mSuppressLayout = suppressLayout;
564             // Prevent a layout from including mView in its calculation.
565             suppressLayout(true);
566         }
567 
568         @Override
onAnimationPause(Animator animation)569         public void onAnimationPause(Animator animation) {
570             if (!mCanceled) {
571                 mView.setTransitionVisibility(mFinalVisibility);
572             }
573         }
574 
575         @Override
onAnimationResume(Animator animation)576         public void onAnimationResume(Animator animation) {
577             if (!mCanceled) {
578                 mView.setTransitionVisibility(View.VISIBLE);
579             }
580         }
581 
582         @Override
onAnimationCancel(Animator animation)583         public void onAnimationCancel(Animator animation) {
584             mCanceled = true;
585         }
586 
587         @Override
onAnimationRepeat(Animator animation)588         public void onAnimationRepeat(Animator animation) {
589         }
590 
591         @Override
onAnimationStart(Animator animation)592         public void onAnimationStart(Animator animation) {
593         }
594 
595         @Override
onAnimationEnd(Animator animation)596         public void onAnimationEnd(Animator animation) {
597             hideViewWhenNotCanceled();
598         }
599 
600         @Override
onTransitionEnd(Transition transition)601         public void onTransitionEnd(Transition transition) {
602             hideViewWhenNotCanceled();
603             transition.removeListener(this);
604         }
605 
606         @Override
onTransitionPause(Transition transition)607         public void onTransitionPause(Transition transition) {
608             suppressLayout(false);
609         }
610 
611         @Override
onTransitionResume(Transition transition)612         public void onTransitionResume(Transition transition) {
613             suppressLayout(true);
614         }
615 
hideViewWhenNotCanceled()616         private void hideViewWhenNotCanceled() {
617             if (!mCanceled) {
618                 // Recreate the parent's display list in case it includes mView.
619                 mView.setTransitionVisibility(mFinalVisibility);
620                 if (mParent != null) {
621                     mParent.invalidate();
622                 }
623             }
624             // Layout is allowed now that the View is in its final state
625             suppressLayout(false);
626         }
627 
suppressLayout(boolean suppress)628         private void suppressLayout(boolean suppress) {
629             if (mSuppressLayout && mLayoutSuppressed != suppress && mParent != null) {
630                 mLayoutSuppressed = suppress;
631                 mParent.suppressLayout(suppress);
632             }
633         }
634     }
635 }
636