636 lines
27 KiB
Java
636 lines
27 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2013 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package android.transition;
|
||
|
|
||
|
import android.animation.Animator;
|
||
|
import android.animation.Animator.AnimatorListener;
|
||
|
import android.animation.Animator.AnimatorPauseListener;
|
||
|
import android.annotation.IntDef;
|
||
|
import android.annotation.NonNull;
|
||
|
import android.annotation.Nullable;
|
||
|
import android.content.Context;
|
||
|
import android.content.res.TypedArray;
|
||
|
import android.util.AttributeSet;
|
||
|
import android.view.View;
|
||
|
import android.view.ViewGroup;
|
||
|
import android.view.ViewGroupOverlay;
|
||
|
|
||
|
import com.android.internal.R;
|
||
|
|
||
|
import java.lang.annotation.Retention;
|
||
|
import java.lang.annotation.RetentionPolicy;
|
||
|
|
||
|
/**
|
||
|
* This transition tracks changes to the visibility of target views in the
|
||
|
* start and end scenes. Visibility is determined not just by the
|
||
|
* {@link View#setVisibility(int)} state of views, but also whether
|
||
|
* views exist in the current view hierarchy. The class is intended to be a
|
||
|
* utility for subclasses such as {@link Fade}, which use this visibility
|
||
|
* information to determine the specific animations to run when visibility
|
||
|
* changes occur. Subclasses should implement one or both of the methods
|
||
|
* {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)},
|
||
|
* {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)} or
|
||
|
* {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)},
|
||
|
* {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}.
|
||
|
*/
|
||
|
public abstract class Visibility extends Transition {
|
||
|
|
||
|
static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
|
||
|
private static final String PROPNAME_PARENT = "android:visibility:parent";
|
||
|
private static final String PROPNAME_SCREEN_LOCATION = "android:visibility:screenLocation";
|
||
|
|
||
|
/** @hide */
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
@IntDef(flag = true, value = {
|
||
|
MODE_IN,
|
||
|
MODE_OUT,
|
||
|
Fade.IN,
|
||
|
Fade.OUT
|
||
|
})
|
||
|
@interface VisibilityMode {}
|
||
|
|
||
|
/**
|
||
|
* Mode used in {@link #setMode(int)} to make the transition
|
||
|
* operate on targets that are appearing. Maybe be combined with
|
||
|
* {@link #MODE_OUT} to target Visibility changes both in and out.
|
||
|
*/
|
||
|
public static final int MODE_IN = 0x1;
|
||
|
|
||
|
/**
|
||
|
* Mode used in {@link #setMode(int)} to make the transition
|
||
|
* operate on targets that are disappearing. Maybe be combined with
|
||
|
* {@link #MODE_IN} to target Visibility changes both in and out.
|
||
|
*/
|
||
|
public static final int MODE_OUT = 0x2;
|
||
|
|
||
|
private static final String[] sTransitionProperties = {
|
||
|
PROPNAME_VISIBILITY,
|
||
|
PROPNAME_PARENT,
|
||
|
};
|
||
|
|
||
|
private static class VisibilityInfo {
|
||
|
boolean visibilityChange;
|
||
|
boolean fadeIn;
|
||
|
int startVisibility;
|
||
|
int endVisibility;
|
||
|
ViewGroup startParent;
|
||
|
ViewGroup endParent;
|
||
|
}
|
||
|
|
||
|
private int mMode = MODE_IN | MODE_OUT;
|
||
|
private boolean mSuppressLayout = true;
|
||
|
|
||
|
public Visibility() {}
|
||
|
|
||
|
public Visibility(Context context, AttributeSet attrs) {
|
||
|
super(context, attrs);
|
||
|
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VisibilityTransition);
|
||
|
int mode = a.getInt(R.styleable.VisibilityTransition_transitionVisibilityMode, 0);
|
||
|
a.recycle();
|
||
|
if (mode != 0) {
|
||
|
setMode(mode);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This tells the Visibility transition to suppress layout during the transition and release
|
||
|
* the suppression after the transition.
|
||
|
* @hide
|
||
|
*/
|
||
|
public void setSuppressLayout(boolean suppress) {
|
||
|
this.mSuppressLayout = suppress;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Changes the transition to support appearing and/or disappearing Views, depending
|
||
|
* on <code>mode</code>.
|
||
|
*
|
||
|
* @param mode The behavior supported by this transition, a combination of
|
||
|
* {@link #MODE_IN} and {@link #MODE_OUT}.
|
||
|
* @attr ref android.R.styleable#VisibilityTransition_transitionVisibilityMode
|
||
|
*/
|
||
|
public void setMode(@VisibilityMode int mode) {
|
||
|
if ((mode & ~(MODE_IN | MODE_OUT)) != 0) {
|
||
|
throw new IllegalArgumentException("Only MODE_IN and MODE_OUT flags are allowed");
|
||
|
}
|
||
|
mMode = mode;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns whether appearing and/or disappearing Views are supported.
|
||
|
*
|
||
|
* Returns whether appearing and/or disappearing Views are supported. A combination of
|
||
|
* {@link #MODE_IN} and {@link #MODE_OUT}.
|
||
|
* @attr ref android.R.styleable#VisibilityTransition_transitionVisibilityMode
|
||
|
*/
|
||
|
@VisibilityMode
|
||
|
public int getMode() {
|
||
|
return mMode;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String[] getTransitionProperties() {
|
||
|
return sTransitionProperties;
|
||
|
}
|
||
|
|
||
|
private void captureValues(TransitionValues transitionValues) {
|
||
|
int visibility = transitionValues.view.getVisibility();
|
||
|
transitionValues.values.put(PROPNAME_VISIBILITY, visibility);
|
||
|
transitionValues.values.put(PROPNAME_PARENT, transitionValues.view.getParent());
|
||
|
int[] loc = new int[2];
|
||
|
transitionValues.view.getLocationOnScreen(loc);
|
||
|
transitionValues.values.put(PROPNAME_SCREEN_LOCATION, loc);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void captureStartValues(TransitionValues transitionValues) {
|
||
|
captureValues(transitionValues);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void captureEndValues(TransitionValues transitionValues) {
|
||
|
captureValues(transitionValues);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns whether the view is 'visible' according to the given values
|
||
|
* object. This is determined by testing the same properties in the values
|
||
|
* object that are used to determine whether the object is appearing or
|
||
|
* disappearing in the {@link
|
||
|
* Transition#createAnimator(ViewGroup, TransitionValues, TransitionValues)}
|
||
|
* method. This method can be called by, for example, subclasses that want
|
||
|
* to know whether the object is visible in the same way that Visibility
|
||
|
* determines it for the actual animation.
|
||
|
*
|
||
|
* @param values The TransitionValues object that holds the information by
|
||
|
* which visibility is determined.
|
||
|
* @return True if the view reference by <code>values</code> is visible,
|
||
|
* false otherwise.
|
||
|
*/
|
||
|
public boolean isVisible(TransitionValues values) {
|
||
|
if (values == null) {
|
||
|
return false;
|
||
|
}
|
||
|
int visibility = (Integer) values.values.get(PROPNAME_VISIBILITY);
|
||
|
View parent = (View) values.values.get(PROPNAME_PARENT);
|
||
|
|
||
|
return visibility == View.VISIBLE && parent != null;
|
||
|
}
|
||
|
|
||
|
private static VisibilityInfo getVisibilityChangeInfo(TransitionValues startValues,
|
||
|
TransitionValues endValues) {
|
||
|
final VisibilityInfo visInfo = new VisibilityInfo();
|
||
|
visInfo.visibilityChange = false;
|
||
|
visInfo.fadeIn = false;
|
||
|
if (startValues != null && startValues.values.containsKey(PROPNAME_VISIBILITY)) {
|
||
|
visInfo.startVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY);
|
||
|
visInfo.startParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT);
|
||
|
} else {
|
||
|
visInfo.startVisibility = -1;
|
||
|
visInfo.startParent = null;
|
||
|
}
|
||
|
if (endValues != null && endValues.values.containsKey(PROPNAME_VISIBILITY)) {
|
||
|
visInfo.endVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY);
|
||
|
visInfo.endParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT);
|
||
|
} else {
|
||
|
visInfo.endVisibility = -1;
|
||
|
visInfo.endParent = null;
|
||
|
}
|
||
|
if (startValues != null && endValues != null) {
|
||
|
if (visInfo.startVisibility == visInfo.endVisibility &&
|
||
|
visInfo.startParent == visInfo.endParent) {
|
||
|
return visInfo;
|
||
|
} else {
|
||
|
if (visInfo.startVisibility != visInfo.endVisibility) {
|
||
|
if (visInfo.startVisibility == View.VISIBLE) {
|
||
|
visInfo.fadeIn = false;
|
||
|
visInfo.visibilityChange = true;
|
||
|
} else if (visInfo.endVisibility == View.VISIBLE) {
|
||
|
visInfo.fadeIn = true;
|
||
|
visInfo.visibilityChange = true;
|
||
|
}
|
||
|
// no visibilityChange if going between INVISIBLE and GONE
|
||
|
} else if (visInfo.startParent != visInfo.endParent) {
|
||
|
if (visInfo.endParent == null) {
|
||
|
visInfo.fadeIn = false;
|
||
|
visInfo.visibilityChange = true;
|
||
|
} else if (visInfo.startParent == null) {
|
||
|
visInfo.fadeIn = true;
|
||
|
visInfo.visibilityChange = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else if (startValues == null && visInfo.endVisibility == View.VISIBLE) {
|
||
|
visInfo.fadeIn = true;
|
||
|
visInfo.visibilityChange = true;
|
||
|
} else if (endValues == null && visInfo.startVisibility == View.VISIBLE) {
|
||
|
visInfo.fadeIn = false;
|
||
|
visInfo.visibilityChange = true;
|
||
|
}
|
||
|
return visInfo;
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
@Override
|
||
|
public Animator createAnimator(@NonNull ViewGroup sceneRoot,
|
||
|
@Nullable TransitionValues startValues,
|
||
|
@Nullable TransitionValues endValues) {
|
||
|
VisibilityInfo visInfo = getVisibilityChangeInfo(startValues, endValues);
|
||
|
if (visInfo.visibilityChange
|
||
|
&& (visInfo.startParent != null || visInfo.endParent != null)) {
|
||
|
if (visInfo.fadeIn) {
|
||
|
return onAppear(sceneRoot, startValues, visInfo.startVisibility,
|
||
|
endValues, visInfo.endVisibility);
|
||
|
} else {
|
||
|
return onDisappear(sceneRoot, startValues, visInfo.startVisibility,
|
||
|
endValues, visInfo.endVisibility
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* The default implementation of this method calls
|
||
|
* {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}.
|
||
|
* Subclasses should override this method or
|
||
|
* {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}.
|
||
|
* if they need to create an Animator when targets appear.
|
||
|
* The method should only be called by the Visibility class; it is
|
||
|
* not intended to be called from external classes.
|
||
|
*
|
||
|
* @param sceneRoot The root of the transition hierarchy
|
||
|
* @param startValues The target values in the start scene
|
||
|
* @param startVisibility The target visibility in the start scene
|
||
|
* @param endValues The target values in the end scene
|
||
|
* @param endVisibility The target visibility in the end scene
|
||
|
* @return An Animator to be started at the appropriate time in the
|
||
|
* overall transition for this scene change. A null value means no animation
|
||
|
* should be run.
|
||
|
*/
|
||
|
public Animator onAppear(ViewGroup sceneRoot,
|
||
|
TransitionValues startValues, int startVisibility,
|
||
|
TransitionValues endValues, int endVisibility) {
|
||
|
if ((mMode & MODE_IN) != MODE_IN || endValues == null) {
|
||
|
return null;
|
||
|
}
|
||
|
if (startValues == null) {
|
||
|
VisibilityInfo parentVisibilityInfo = null;
|
||
|
View endParent = (View) endValues.view.getParent();
|
||
|
TransitionValues startParentValues = getMatchedTransitionValues(endParent,
|
||
|
false);
|
||
|
TransitionValues endParentValues = getTransitionValues(endParent, false);
|
||
|
parentVisibilityInfo =
|
||
|
getVisibilityChangeInfo(startParentValues, endParentValues);
|
||
|
if (parentVisibilityInfo.visibilityChange) {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
return onAppear(sceneRoot, endValues.view, startValues, endValues);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* The default implementation of this method returns a null Animator. Subclasses should
|
||
|
* override this method to make targets appear with the desired transition. The
|
||
|
* method should only be called from
|
||
|
* {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)}.
|
||
|
*
|
||
|
* @param sceneRoot The root of the transition hierarchy
|
||
|
* @param view The View to make appear. This will be in the target scene's View hierarchy and
|
||
|
* will be VISIBLE.
|
||
|
* @param startValues The target values in the start scene
|
||
|
* @param endValues The target values in the end scene
|
||
|
* @return An Animator to be started at the appropriate time in the
|
||
|
* overall transition for this scene change. A null value means no animation
|
||
|
* should be run.
|
||
|
*/
|
||
|
public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
|
||
|
TransitionValues endValues) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Subclasses should override this method or
|
||
|
* {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}
|
||
|
* if they need to create an Animator when targets disappear.
|
||
|
* The method should only be called by the Visibility class; it is
|
||
|
* not intended to be called from external classes.
|
||
|
* <p>
|
||
|
* The default implementation of this method attempts to find a View to use to call
|
||
|
* {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)},
|
||
|
* based on the situation of the View in the View hierarchy. For example,
|
||
|
* if a View was simply removed from its parent, then the View will be added
|
||
|
* into a {@link android.view.ViewGroupOverlay} and passed as the <code>view</code>
|
||
|
* parameter in {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}.
|
||
|
* If a visible View is changed to be {@link View#GONE} or {@link View#INVISIBLE},
|
||
|
* then it can be used as the <code>view</code> and the visibility will be changed
|
||
|
* to {@link View#VISIBLE} for the duration of the animation. However, if a View
|
||
|
* is in a hierarchy which is also altering its visibility, the situation can be
|
||
|
* more complicated. In general, if a view that is no longer in the hierarchy in
|
||
|
* the end scene still has a parent (so its parent hierarchy was removed, but it
|
||
|
* was not removed from its parent), then it will be left alone to avoid side-effects from
|
||
|
* improperly removing it from its parent. The only exception to this is if
|
||
|
* the previous {@link Scene} was {@link Scene#getSceneForLayout(ViewGroup, int,
|
||
|
* android.content.Context) created from a layout resource file}, then it is considered
|
||
|
* safe to un-parent the starting scene view in order to make it disappear.</p>
|
||
|
*
|
||
|
* @param sceneRoot The root of the transition hierarchy
|
||
|
* @param startValues The target values in the start scene
|
||
|
* @param startVisibility The target visibility in the start scene
|
||
|
* @param endValues The target values in the end scene
|
||
|
* @param endVisibility The target visibility in the end scene
|
||
|
* @return An Animator to be started at the appropriate time in the
|
||
|
* overall transition for this scene change. A null value means no animation
|
||
|
* should be run.
|
||
|
*/
|
||
|
public Animator onDisappear(ViewGroup sceneRoot,
|
||
|
TransitionValues startValues, int startVisibility,
|
||
|
TransitionValues endValues, int endVisibility) {
|
||
|
if ((mMode & MODE_OUT) != MODE_OUT) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
if (startValues == null) {
|
||
|
// startValues(and startView) will never be null for disappear transition.
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
final View startView = startValues.view;
|
||
|
final View endView = (endValues != null) ? endValues.view : null;
|
||
|
View overlayView = null;
|
||
|
View viewToKeep = null;
|
||
|
boolean reusingOverlayView = false;
|
||
|
|
||
|
View savedOverlayView = (View) startView.getTag(R.id.transition_overlay_view_tag);
|
||
|
if (savedOverlayView != null) {
|
||
|
// we've already created overlay for the start view.
|
||
|
// it means that we are applying two visibility
|
||
|
// transitions for the same view
|
||
|
overlayView = savedOverlayView;
|
||
|
reusingOverlayView = true;
|
||
|
} else {
|
||
|
boolean needOverlayForStartView = false;
|
||
|
|
||
|
if (endView == null || endView.getParent() == null) {
|
||
|
if (endView != null) {
|
||
|
// endView was removed from its parent - add it to the overlay
|
||
|
overlayView = endView;
|
||
|
} else {
|
||
|
needOverlayForStartView = true;
|
||
|
}
|
||
|
} else {
|
||
|
// visibility change
|
||
|
if (endVisibility == View.INVISIBLE) {
|
||
|
viewToKeep = endView;
|
||
|
} else {
|
||
|
// Becoming GONE
|
||
|
if (startView == endView) {
|
||
|
viewToKeep = endView;
|
||
|
} else {
|
||
|
needOverlayForStartView = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (needOverlayForStartView) {
|
||
|
// endView does not exist. Use startView only under certain
|
||
|
// conditions, because placing a view in an overlay necessitates
|
||
|
// it being removed from its current parent
|
||
|
if (startView.getParent() == null) {
|
||
|
// no parent - safe to use
|
||
|
overlayView = startView;
|
||
|
} else if (startView.getParent() instanceof View) {
|
||
|
View startParent = (View) startView.getParent();
|
||
|
TransitionValues startParentValues = getTransitionValues(startParent, true);
|
||
|
TransitionValues endParentValues = getMatchedTransitionValues(startParent,
|
||
|
true);
|
||
|
VisibilityInfo parentVisibilityInfo =
|
||
|
getVisibilityChangeInfo(startParentValues, endParentValues);
|
||
|
if (!parentVisibilityInfo.visibilityChange) {
|
||
|
overlayView = TransitionUtils.copyViewImage(sceneRoot, startView,
|
||
|
startParent);
|
||
|
} else {
|
||
|
int id = startParent.getId();
|
||
|
if (startParent.getParent() == null && id != View.NO_ID
|
||
|
&& sceneRoot.findViewById(id) != null && mCanRemoveViews) {
|
||
|
// no parent, but its parent is unparented but the parent
|
||
|
// hierarchy has been replaced by a new hierarchy with the same id
|
||
|
// and it is safe to un-parent startView
|
||
|
overlayView = startView;
|
||
|
} else {
|
||
|
// TODO: Handle this case as well
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (overlayView != null) {
|
||
|
// TODO: Need to do this for general case of adding to overlay
|
||
|
final ViewGroupOverlay overlay;
|
||
|
if (!reusingOverlayView) {
|
||
|
overlay = sceneRoot.getOverlay();
|
||
|
int[] screenLoc = (int[]) startValues.values.get(PROPNAME_SCREEN_LOCATION);
|
||
|
int screenX = screenLoc[0];
|
||
|
int screenY = screenLoc[1];
|
||
|
int[] loc = new int[2];
|
||
|
sceneRoot.getLocationOnScreen(loc);
|
||
|
overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft());
|
||
|
overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop());
|
||
|
overlay.add(overlayView);
|
||
|
} else {
|
||
|
overlay = null;
|
||
|
}
|
||
|
Animator animator = onDisappear(sceneRoot, overlayView, startValues, endValues);
|
||
|
if (!reusingOverlayView) {
|
||
|
if (animator == null) {
|
||
|
overlay.remove(overlayView);
|
||
|
} else {
|
||
|
startView.setTagInternal(R.id.transition_overlay_view_tag, overlayView);
|
||
|
final View finalOverlayView = overlayView;
|
||
|
addListener(new TransitionListenerAdapter() {
|
||
|
|
||
|
@Override
|
||
|
public void onTransitionPause(Transition transition) {
|
||
|
overlay.remove(finalOverlayView);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onTransitionResume(Transition transition) {
|
||
|
if (finalOverlayView.getParent() == null) {
|
||
|
overlay.add(finalOverlayView);
|
||
|
} else {
|
||
|
cancel();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onTransitionEnd(Transition transition) {
|
||
|
startView.setTagInternal(R.id.transition_overlay_view_tag, null);
|
||
|
overlay.remove(finalOverlayView);
|
||
|
transition.removeListener(this);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
return animator;
|
||
|
}
|
||
|
|
||
|
if (viewToKeep != null) {
|
||
|
int originalVisibility = viewToKeep.getVisibility();
|
||
|
viewToKeep.setTransitionVisibility(View.VISIBLE);
|
||
|
Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues);
|
||
|
if (animator != null) {
|
||
|
DisappearListener disappearListener = new DisappearListener(viewToKeep,
|
||
|
endVisibility, mSuppressLayout);
|
||
|
animator.addListener(disappearListener);
|
||
|
animator.addPauseListener(disappearListener);
|
||
|
addListener(disappearListener);
|
||
|
} else {
|
||
|
viewToKeep.setTransitionVisibility(originalVisibility);
|
||
|
}
|
||
|
return animator;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean isTransitionRequired(TransitionValues startValues, TransitionValues newValues) {
|
||
|
if (startValues == null && newValues == null) {
|
||
|
return false;
|
||
|
}
|
||
|
if (startValues != null && newValues != null &&
|
||
|
newValues.values.containsKey(PROPNAME_VISIBILITY) !=
|
||
|
startValues.values.containsKey(PROPNAME_VISIBILITY)) {
|
||
|
// The transition wasn't targeted in either the start or end, so it couldn't
|
||
|
// have changed.
|
||
|
return false;
|
||
|
}
|
||
|
VisibilityInfo changeInfo = getVisibilityChangeInfo(startValues, newValues);
|
||
|
return changeInfo.visibilityChange && (changeInfo.startVisibility == View.VISIBLE ||
|
||
|
changeInfo.endVisibility == View.VISIBLE);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* The default implementation of this method returns a null Animator. Subclasses should
|
||
|
* override this method to make targets disappear with the desired transition. The
|
||
|
* method should only be called from
|
||
|
* {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)}.
|
||
|
*
|
||
|
* @param sceneRoot The root of the transition hierarchy
|
||
|
* @param view The View to make disappear. This will be in the target scene's View
|
||
|
* hierarchy or in an {@link android.view.ViewGroupOverlay} and will be
|
||
|
* VISIBLE.
|
||
|
* @param startValues The target values in the start scene
|
||
|
* @param endValues The target values in the end scene
|
||
|
* @return An Animator to be started at the appropriate time in the
|
||
|
* overall transition for this scene change. A null value means no animation
|
||
|
* should be run.
|
||
|
*/
|
||
|
public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
|
||
|
TransitionValues endValues) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
private static class DisappearListener
|
||
|
extends TransitionListenerAdapter implements AnimatorListener, AnimatorPauseListener {
|
||
|
private final View mView;
|
||
|
private final int mFinalVisibility;
|
||
|
private final ViewGroup mParent;
|
||
|
private final boolean mSuppressLayout;
|
||
|
|
||
|
private boolean mLayoutSuppressed;
|
||
|
boolean mCanceled = false;
|
||
|
|
||
|
public DisappearListener(View view, int finalVisibility, boolean suppressLayout) {
|
||
|
this.mView = view;
|
||
|
this.mFinalVisibility = finalVisibility;
|
||
|
this.mParent = (ViewGroup) view.getParent();
|
||
|
this.mSuppressLayout = suppressLayout;
|
||
|
// Prevent a layout from including mView in its calculation.
|
||
|
suppressLayout(true);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onAnimationPause(Animator animation) {
|
||
|
if (!mCanceled) {
|
||
|
mView.setTransitionVisibility(mFinalVisibility);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onAnimationResume(Animator animation) {
|
||
|
if (!mCanceled) {
|
||
|
mView.setTransitionVisibility(View.VISIBLE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onAnimationCancel(Animator animation) {
|
||
|
mCanceled = true;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onAnimationRepeat(Animator animation) {
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onAnimationStart(Animator animation) {
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onAnimationEnd(Animator animation) {
|
||
|
hideViewWhenNotCanceled();
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onTransitionEnd(Transition transition) {
|
||
|
hideViewWhenNotCanceled();
|
||
|
transition.removeListener(this);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onTransitionPause(Transition transition) {
|
||
|
suppressLayout(false);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onTransitionResume(Transition transition) {
|
||
|
suppressLayout(true);
|
||
|
}
|
||
|
|
||
|
private void hideViewWhenNotCanceled() {
|
||
|
if (!mCanceled) {
|
||
|
// Recreate the parent's display list in case it includes mView.
|
||
|
mView.setTransitionVisibility(mFinalVisibility);
|
||
|
if (mParent != null) {
|
||
|
mParent.invalidate();
|
||
|
}
|
||
|
}
|
||
|
// Layout is allowed now that the View is in its final state
|
||
|
suppressLayout(false);
|
||
|
}
|
||
|
|
||
|
private void suppressLayout(boolean suppress) {
|
||
|
if (mSuppressLayout && mLayoutSuppressed != suppress && mParent != null) {
|
||
|
mLayoutSuppressed = suppress;
|
||
|
mParent.suppressLayout(suppress);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|