Make Fragment Transitions match Acitivty Transitions API

Bug 17188255

Change-Id: I506a097be4010d7156caf465c95295c58612c16e
This commit is contained in:
George Mount
2014-08-22 17:04:02 -07:00
parent 8fd8124911
commit c03da0e7a9
10 changed files with 1295 additions and 301 deletions

View File

@@ -612,12 +612,20 @@ package android {
field public static final int format12Hour = 16843722; // 0x10103ca
field public static final int format24Hour = 16843723; // 0x10103cb
field public static final int fragment = 16843491; // 0x10102e3
field public static final int fragmentAllowEnterTransitionOverlap = 16843977; // 0x10104c9
field public static final int fragmentAllowReturnTransitionOverlap = 16843978; // 0x10104ca
field public static final int fragmentCloseEnterAnimation = 16843495; // 0x10102e7
field public static final int fragmentCloseExitAnimation = 16843496; // 0x10102e8
field public static final int fragmentEnterTransition = 16843972; // 0x10104c4
field public static final int fragmentExitTransition = 16843971; // 0x10104c3
field public static final int fragmentFadeEnterAnimation = 16843497; // 0x10102e9
field public static final int fragmentFadeExitAnimation = 16843498; // 0x10102ea
field public static final int fragmentOpenEnterAnimation = 16843493; // 0x10102e5
field public static final int fragmentOpenExitAnimation = 16843494; // 0x10102e6
field public static final int fragmentReenterTransition = 16843976; // 0x10104c8
field public static final int fragmentReturnTransition = 16843974; // 0x10104c6
field public static final int fragmentSharedElementEnterTransition = 16843973; // 0x10104c5
field public static final int fragmentSharedElementReturnTransition = 16843975; // 0x10104c7
field public static final int freezesText = 16843116; // 0x101016c
field public static final int fromAlpha = 16843210; // 0x10101ca
field public static final int fromDegrees = 16843187; // 0x10101b3
@@ -1418,7 +1426,7 @@ package android {
field public static final int windowActionBarOverlay = 16843492; // 0x10102e4
field public static final int windowActionModeOverlay = 16843485; // 0x10102dd
field public static final int windowAllowEnterTransitionOverlap = 16843836; // 0x101043c
field public static final int windowAllowExitTransitionOverlap = 16843835; // 0x101043b
field public static final int windowAllowReturnTransitionOverlap = 16843835; // 0x101043b
field public static final int windowAnimationStyle = 16842926; // 0x10100ae
field public static final int windowBackground = 16842836; // 0x1010054
field public static final int windowClipToOutline = 16843948; // 0x10104ac
@@ -4245,14 +4253,22 @@ package android.app {
method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public final boolean equals(java.lang.Object);
method public final android.app.Activity getActivity();
method public boolean getAllowEnterTransitionOverlap();
method public boolean getAllowReturnTransitionOverlap();
method public final android.os.Bundle getArguments();
method public final android.app.FragmentManager getChildFragmentManager();
method public android.transition.Transition getEnterTransition();
method public android.transition.Transition getExitTransition();
method public final android.app.FragmentManager getFragmentManager();
method public final int getId();
method public android.app.LoaderManager getLoaderManager();
method public final android.app.Fragment getParentFragment();
method public android.transition.Transition getReenterTransition();
method public final android.content.res.Resources getResources();
method public final boolean getRetainInstance();
method public android.transition.Transition getReturnTransition();
method public android.transition.Transition getSharedElementEnterTransition();
method public android.transition.Transition getSharedElementReturnTransition();
method public final java.lang.String getString(int);
method public final java.lang.String getString(int, java.lang.Object...);
method public final java.lang.String getTag();
@@ -4301,11 +4317,21 @@ package android.app {
method public void onViewCreated(android.view.View, android.os.Bundle);
method public void onViewStateRestored(android.os.Bundle);
method public void registerForContextMenu(android.view.View);
method public void setAllowEnterTransitionOverlap(boolean);
method public void setAllowReturnTransitionOverlap(boolean);
method public void setArguments(android.os.Bundle);
method public void setEnterSharedElementTransitionListener(android.app.SharedElementListener);
method public void setEnterTransition(android.transition.Transition);
method public void setExitSharedElementTransitionListener(android.app.SharedElementListener);
method public void setExitTransition(android.transition.Transition);
method public void setHasOptionsMenu(boolean);
method public void setInitialSavedState(android.app.Fragment.SavedState);
method public void setMenuVisibility(boolean);
method public void setReenterTransition(android.transition.Transition);
method public void setRetainInstance(boolean);
method public void setReturnTransition(android.transition.Transition);
method public void setSharedElementEnterTransition(android.transition.Transition);
method public void setSharedElementReturnTransition(android.transition.Transition);
method public void setTargetFragment(android.app.Fragment, int);
method public void setUserVisibleHint(boolean);
method public void startActivity(android.content.Intent);
@@ -4405,7 +4431,6 @@ package android.app {
method public abstract android.app.FragmentTransaction setBreadCrumbTitle(java.lang.CharSequence);
method public abstract android.app.FragmentTransaction setCustomAnimations(int, int);
method public abstract android.app.FragmentTransaction setCustomAnimations(int, int, int, int);
method public abstract android.app.FragmentTransaction setCustomTransition(int, int);
method public abstract android.app.FragmentTransaction setTransition(int);
method public abstract android.app.FragmentTransaction setTransitionStyle(int);
method public abstract android.app.FragmentTransaction show(android.app.Fragment);
@@ -34864,7 +34889,7 @@ package android.view {
method public abstract void closePanel(int);
method public android.view.View findViewById(int);
method public boolean getAllowEnterTransitionOverlap();
method public boolean getAllowExitTransitionOverlap();
method public boolean getAllowReturnTransitionOverlap();
method public final android.view.WindowManager.LayoutParams getAttributes();
method public final android.view.Window.Callback getCallback();
method public final android.view.Window getContainer();
@@ -34913,7 +34938,7 @@ package android.view {
method public abstract void restoreHierarchyState(android.os.Bundle);
method public abstract android.os.Bundle saveHierarchyState();
method public void setAllowEnterTransitionOverlap(boolean);
method public void setAllowExitTransitionOverlap(boolean);
method public void setAllowReturnTransitionOverlap(boolean);
method public void setAttributes(android.view.WindowManager.LayoutParams);
method public abstract void setBackgroundDrawable(android.graphics.drawable.Drawable);
method public void setBackgroundDrawableResource(int);

View File

@@ -23,15 +23,17 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.transition.Transition;
import android.transition.TransitionInflater;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.transition.TransitionUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.LogWriter;
import android.util.Pair;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -42,8 +44,6 @@ final class BackStackState implements Parcelable {
final int[] mOps;
final int mTransition;
final int mTransitionStyle;
final int mCustomTransition;
final int mSceneRoot;
final String mName;
final int mIndex;
final int mBreadCrumbTitleRes;
@@ -96,8 +96,6 @@ final class BackStackState implements Parcelable {
mBreadCrumbTitleText = bse.mBreadCrumbTitleText;
mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes;
mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
mCustomTransition = bse.mCustomTransition;
mSceneRoot = bse.mSceneRoot;
mSharedElementSourceNames = bse.mSharedElementSourceNames;
mSharedElementTargetNames = bse.mSharedElementTargetNames;
}
@@ -112,8 +110,6 @@ final class BackStackState implements Parcelable {
mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mBreadCrumbShortTitleRes = in.readInt();
mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mCustomTransition = in.readInt();
mSceneRoot = in.readInt();
mSharedElementSourceNames = in.createStringArrayList();
mSharedElementTargetNames = in.createStringArrayList();
}
@@ -164,8 +160,6 @@ final class BackStackState implements Parcelable {
bse.mBreadCrumbTitleText = mBreadCrumbTitleText;
bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes;
bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
bse.mCustomTransition = mCustomTransition;
bse.mSceneRoot = mSceneRoot;
bse.mSharedElementSourceNames = mSharedElementSourceNames;
bse.mSharedElementTargetNames = mSharedElementTargetNames;
bse.bumpBackStackNesting(1);
@@ -186,8 +180,6 @@ final class BackStackState implements Parcelable {
TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0);
dest.writeInt(mBreadCrumbShortTitleRes);
TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
dest.writeInt(mCustomTransition);
dest.writeInt(mSceneRoot);
dest.writeStringList(mSharedElementSourceNames);
dest.writeStringList(mSharedElementTargetNames);
}
@@ -254,8 +246,6 @@ final class BackStackRecord extends FragmentTransaction implements
int mBreadCrumbShortTitleRes;
CharSequence mBreadCrumbShortTitleText;
int mCustomTransition;
int mSceneRoot;
ArrayList<String> mSharedElementSourceNames;
ArrayList<String> mSharedElementTargetNames;
@@ -572,13 +562,6 @@ final class BackStackRecord extends FragmentTransaction implements
return this;
}
@Override
public FragmentTransaction setCustomTransition(int sceneRootId, int transitionId) {
mSceneRoot = sceneRootId;
mCustomTransition = transitionId;
return this;
}
@Override
public FragmentTransaction addSharedElement(View sharedElement, String name) {
String transitionName = sharedElement.getTransitionName();
@@ -760,8 +743,15 @@ final class BackStackRecord extends FragmentTransaction implements
bumpBackStackNesting(1);
TransitionState state = beginTransition(mSharedElementSourceNames,
mSharedElementTargetNames);
SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
calculateFragments(firstOutFragments, lastInFragments);
TransitionState state = null;
if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) {
state = beginTransition(firstOutFragments, lastInFragments, false);
}
Op op = mHead;
while (op != null) {
@@ -854,142 +844,606 @@ final class BackStackRecord extends FragmentTransaction implements
}
if (state != null) {
updateTransitionEndState(state, mSharedElementTargetNames);
updateTransitionEndState(state, firstOutFragments, lastInFragments, false);
}
}
private TransitionState beginTransition(ArrayList<String> sourceNames,
ArrayList<String> targetNames) {
if (mCustomTransition <= 0 || mSceneRoot <= 0) {
return null;
private static void setFirstOut(SparseArray<Fragment> fragments, Fragment fragment) {
if (fragment != null) {
int containerId = fragment.mContainerId;
if (containerId != 0 && !fragment.isHidden() && fragment.isAdded() &&
fragment.getView() != null && fragments.get(containerId) == null) {
fragments.put(containerId, fragment);
}
}
View rootView = mManager.mContainer.findViewById(mSceneRoot);
if (!(rootView instanceof ViewGroup)) {
throw new IllegalArgumentException("SceneRoot is not a ViewGroup");
}
TransitionState state = new TransitionState();
// get Transition scene root and create Transitions
state.sceneRoot = (ViewGroup) rootView;
state.sceneRoot.captureTransitioningViews(state.transitioningViews);
}
private void setLastIn(SparseArray<Fragment> fragments, Fragment fragment) {
if (fragment != null) {
int containerId = fragment.mContainerId;
if (containerId != 0) {
fragments.put(containerId, fragment);
}
}
}
/**
* Finds the first removed fragment and last added fragments when going forward.
* If none of the fragments have transitions, then both lists will be empty.
*
* @param firstOutFragments The list of first fragments to be removed, keyed on the
* container ID. This list will be modified by the method.
* @param lastInFragments The list of last fragments to be added, keyed on the
* container ID. This list will be modified by the method.
*/
private void calculateFragments(SparseArray<Fragment> firstOutFragments,
SparseArray<Fragment> lastInFragments) {
Op op = mHead;
while (op != null) {
switch (op.cmd) {
case OP_ADD:
setLastIn(lastInFragments, op.fragment);
break;
case OP_REPLACE: {
Fragment f = op.fragment;
if (mManager.mAdded != null) {
for (int i = 0; i < mManager.mAdded.size(); i++) {
Fragment old = mManager.mAdded.get(i);
if (f == null || old.mContainerId == f.mContainerId) {
if (old == f) {
f = null;
} else {
setFirstOut(firstOutFragments, old);
}
}
}
}
setLastIn(lastInFragments, f);
break;
}
case OP_REMOVE:
setFirstOut(firstOutFragments, op.fragment);
break;
case OP_HIDE:
setFirstOut(firstOutFragments, op.fragment);
break;
case OP_SHOW:
setLastIn(lastInFragments, op.fragment);
break;
case OP_DETACH:
setFirstOut(firstOutFragments, op.fragment);
break;
case OP_ATTACH:
setLastIn(lastInFragments, op.fragment);
break;
}
op = op.next;
}
if (!haveTransitions(firstOutFragments, lastInFragments, false)) {
firstOutFragments.clear();
lastInFragments.clear();
}
}
/**
* @return true if custom transitions exist on any fragment in firstOutFragments or
* lastInFragments or false otherwise.
*/
private static boolean haveTransitions(SparseArray<Fragment> firstOutFragments,
SparseArray<Fragment> lastInFragments, boolean isBack) {
for (int i = firstOutFragments.size() - 1; i >= 0; i--) {
Fragment f = firstOutFragments.valueAt(i);
if (isBack) {
if (f.getReturnTransition() != null ||
f.getSharedElementReturnTransition() != null) {
return true;
}
} else if (f.getExitTransition() != null) {
return true;
}
}
for (int i = lastInFragments.size() - 1; i >= 0; i--) {
Fragment f = lastInFragments.valueAt(i);
if (isBack) {
if (f.getReenterTransition() != null) {
return true;
}
} else if (f.getEnterTransition() != null ||
f.getSharedElementEnterTransition() != null) {
return true;
}
}
return false;
}
/**
* Finds the first removed fragment and last added fragments when popping the back stack.
* If none of the fragments have transitions, then both lists will be empty.
*
* @param firstOutFragments The list of first fragments to be removed, keyed on the
* container ID. This list will be modified by the method.
* @param lastInFragments The list of last fragments to be added, keyed on the
* container ID. This list will be modified by the method.
*/
public void calculateBackFragments(SparseArray<Fragment> firstOutFragments,
SparseArray<Fragment> lastInFragments) {
Op op = mHead;
while (op != null) {
switch (op.cmd) {
case OP_ADD:
setFirstOut(firstOutFragments, op.fragment);
break;
case OP_REPLACE:
if (op.removed != null) {
for (int i = op.removed.size() - 1; i >= 0; i--) {
setLastIn(lastInFragments, op.removed.get(i));
}
}
setFirstOut(firstOutFragments, op.fragment);
break;
case OP_REMOVE:
setLastIn(lastInFragments, op.fragment);
break;
case OP_HIDE:
setLastIn(lastInFragments, op.fragment);
break;
case OP_SHOW:
setFirstOut(firstOutFragments, op.fragment);
break;
case OP_DETACH:
setLastIn(lastInFragments, op.fragment);
break;
case OP_ATTACH:
setFirstOut(firstOutFragments, op.fragment);
break;
}
op = op.next;
}
if (!haveTransitions(firstOutFragments, lastInFragments, true)) {
firstOutFragments.clear();
lastInFragments.clear();
}
}
/**
* When custom fragment transitions are used, this sets up the state for each transition
* and begins the transition. A different transition is started for each fragment container
* and consists of up to 3 different transitions: the exit transition, a shared element
* transition and an enter transition.
*
* <p>The exit transition operates against the leaf nodes of the first fragment
* with a view that was removed. If no such fragment was removed, then no exit
* transition is executed. The exit transition comes from the outgoing fragment.</p>
*
* <p>The enter transition operates against the last fragment that was added. If
* that fragment does not have a view or no fragment was added, then no enter
* transition is executed. The enter transition comes from the incoming fragment.</p>
*
* <p>The shared element transition operates against all views and comes either
* from the outgoing fragment or the incoming fragment, depending on whether this
* is going forward or popping the back stack. When going forward, the incoming
* fragment's enter shared element transition is used, but when going back, the
* outgoing fragment's return shared element transition is used. Shared element
* transitions only operate if there is both an incoming and outgoing fragment.</p>
*
* @param firstOutFragments The list of first fragments to be removed, keyed on the
* container ID.
* @param lastInFragments The list of last fragments to be added, keyed on the
* container ID.
* @param isBack true if this is popping the back stack or false if this is a
* forward operation.
* @return The TransitionState used to complete the operation of the transition
* in {@link #updateTransitionEndState(android.app.BackStackRecord.TransitionState,
* android.util.SparseArray, android.util.SparseArray, boolean)}.
*/
private TransitionState beginTransition(SparseArray<Fragment> firstOutFragments,
SparseArray<Fragment> lastInFragments, boolean isBack) {
TransitionState state = new TransitionState();
state.exitTransition = TransitionInflater.from(mManager.mActivity)
.inflateTransition(mCustomTransition);
state.sharedElementTransition = TransitionInflater.from(mManager.mActivity)
.inflateTransition(mCustomTransition);
state.enterTransition = TransitionInflater.from(mManager.mActivity)
.inflateTransition(mCustomTransition);
// Adding a non-existent target view makes sure that the transitions don't target
// any views by default. They'll only target the views we tell add. If we don't
// add any, then no views will be targeted.
View nonExistentView = new View(mManager.mActivity);
state.enterTransition.addTarget(nonExistentView);
state.exitTransition.addTarget(nonExistentView);
state.sharedElementTransition.addTarget(nonExistentView);
state.nonExistentView = new View(mManager.mActivity);
setSharedElementEpicenter(state.enterTransition, state);
ArrayMap<String, View> tempViews1 = new ArrayMap<String, View>();
ArrayMap<String, View> tempViews2 = new ArrayMap<String, View>();
ArrayList<String> tempNames = new ArrayList<String>();
ArrayList<View> tempViewList = new ArrayList<View>();
state.excludingTransition = new TransitionSet()
.addTransition(state.exitTransition)
.addTransition(state.enterTransition);
if (sourceNames != null) {
// Map shared elements.
state.sceneRoot.findNamedViews(state.namedViews);
state.namedViews.retainAll(sourceNames);
View epicenterView = state.namedViews.get(sourceNames.get(0));
if (epicenterView != null) {
// The epicenter is only the first shared element.
setEpicenter(state.exitTransition, epicenterView);
setEpicenter(state.sharedElementTransition, epicenterView);
}
state.transitioningViews.removeAll(state.namedViews.values());
state.excludingTransition.addTransition(state.sharedElementTransition);
addTransitioningViews(state.sharedElementTransition, state.namedViews.values());
// Go over all leaving fragments.
for (int i = 0; i < firstOutFragments.size(); i++) {
int containerId = firstOutFragments.keyAt(i);
configureTransitions(containerId, state, isBack, firstOutFragments,
lastInFragments, tempViews1, tempViews2, tempNames, tempViewList);
}
// Adds the (maybe) exiting views, not including the shared element.
// If some stay, that's ok.
addTransitioningViews(state.exitTransition, state.transitioningViews);
// Now go over all entering fragments that didn't have a leaving fragment.
for (int i = 0; i < lastInFragments.size(); i++) {
int containerId = lastInFragments.keyAt(i);
if (firstOutFragments.get(containerId) == null) {
configureTransitions(containerId, state, isBack, firstOutFragments,
lastInFragments, tempViews1, tempViews2, tempNames, tempViewList);
}
}
// Prepare for shared element name mapping. This could be chained in the case
// of popping several back stack states.
state.excludingTransition.setNameOverrides(new ArrayMap<String, String>());
setNameOverrides(state, sourceNames, targetNames);
// Don't include any subtree in the views that are hidden when capturing the
// view hierarchy transitions. They should be as if not there.
excludeHiddenFragments(state, true);
TransitionManager.beginDelayedTransition(state.sceneRoot, state.excludingTransition);
if (state.overallTransitions.size() == 0) {
state = null;
}
return state;
}
private void updateTransitionEndState(TransitionState state, ArrayList<String> names) {
// Find all views that are entering.
ArrayList<View> enteringViews = new ArrayList<View>();
state.sceneRoot.captureTransitioningViews(enteringViews);
enteringViews.removeAll(state.transitioningViews);
private static Transition getEnterTransition(Fragment inFragment, boolean isBack) {
if (inFragment == null) {
return null;
}
return isBack ? inFragment.getReenterTransition() : inFragment.getEnterTransition();
}
if (names != null) {
// find all shared elements.
state.namedViews.clear();
state.sceneRoot.findNamedViews(state.namedViews);
state.namedViews.retainAll(names);
if (!state.namedViews.isEmpty()) {
enteringViews.removeAll(state.namedViews.values());
addTransitioningViews(state.sharedElementTransition, state.namedViews.values());
// now we know the epicenter of the entering transition.
state.mEnteringEpicenterView = state.namedViews.get(names.get(0));
private static Transition getExitTransition(Fragment outFragment, boolean isBack) {
if (outFragment == null) {
return null;
}
return isBack ? outFragment.getReturnTransition() : outFragment.getExitTransition();
}
private static Transition getSharedElementTransition(Fragment inFragment, Fragment outFragment,
boolean isBack) {
if (inFragment == null || outFragment == null) {
return null;
}
return isBack ? outFragment.getSharedElementReturnTransition() :
inFragment.getSharedElementEnterTransition();
}
private static Transition captureExitingViews(Transition exitTransition, Fragment outFragment,
ArrayList<View> viewList) {
if (exitTransition != null) {
View root = outFragment.getView();
viewList.clear();
root.captureTransitioningViews(viewList);
if (viewList.isEmpty()) {
exitTransition = null;
} else {
addTransitioningViews(exitTransition, viewList);
}
}
return exitTransition;
}
private ArrayMap<String, View> remapSharedElements(TransitionState state, Fragment outFragment,
ArrayMap<String, View> namedViews, ArrayMap<String, View> tempViews2, boolean isBack) {
if (mSharedElementSourceNames != null) {
outFragment.getView().findNamedViews(namedViews);
if (isBack) {
namedViews.retainAll(mSharedElementTargetNames);
} else {
namedViews = remapNames(mSharedElementSourceNames, mSharedElementTargetNames,
namedViews, tempViews2);
}
}
// Add all entering views to the enter transition.
addTransitioningViews(state.enterTransition, enteringViews);
// Don't allow capturing state for the newly-hidden fragments.
excludeHiddenFragments(state, false);
// Allow capturing state for the newly-shown fragments
includeVisibleFragments(state.excludingTransition);
}
private void addTransitioningViews(Transition transition, Collection<View> views) {
if (views.isEmpty()) {
// Add a view so that we can modify the valid views at the end of the
// fragment transaction.
transition.addTarget(new View(mManager.mActivity));
if (isBack) {
outFragment.mEnterTransitionListener.remapSharedElements(
mSharedElementTargetNames, namedViews);
setBackNameOverrides(state, namedViews, false);
} else {
for (View view : views) {
transition.addTarget(view);
outFragment.mExitTransitionListener.remapSharedElements(
mSharedElementTargetNames, namedViews);
setNameOverrides(state, namedViews, false);
}
return namedViews;
}
/**
* Prepares the enter transition by adding a non-existent view to the transition's target list
* and setting it epicenter callback. By adding a non-existent view to the target list,
* we can prevent any view from being targeted at the beginning of the transition.
* We will add to the views before the end state of the transition is captured so that the
* views will appear. At the start of the transition, we clear the list of targets so that
* we can restore the state of the transition and use it again.
*/
private void prepareEnterTransition(TransitionState state, final Transition enterTransition,
final View container, final Fragment inFragment) {
if (enterTransition != null) {
final ArrayList<View> enteringViews = new ArrayList<View>();
final View nonExistentView = state.nonExistentView;
enterTransition.addTarget(state.nonExistentView);
enterTransition.addListener(new Transition.TransitionListenerAdapter() {
@Override
public void onTransitionStart(Transition transition) {
transition.removeListener(this);
transition.removeTarget(nonExistentView);
int numViews = enteringViews.size();
for (int i = 0; i < numViews; i++) {
transition.removeTarget(enteringViews.get(i));
}
}
});
container.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
container.getViewTreeObserver().removeOnPreDrawListener(this);
View view = inFragment.getView();
if (view != null) {
view.captureTransitioningViews(enteringViews);
addTransitioningViews(enterTransition, enteringViews);
}
return true;
}
});
setSharedElementEpicenter(enterTransition, state);
}
}
private static Transition mergeTransitions(Transition enterTransition,
Transition exitTransition, Transition sharedElementTransition, Fragment inFragment,
boolean isBack) {
boolean overlap = true;
if (enterTransition != null && exitTransition != null) {
overlap = isBack ? inFragment.getAllowReturnTransitionOverlap() :
inFragment.getAllowEnterTransitionOverlap();
}
Transition transition;
if (overlap) {
transition = TransitionUtils.mergeTransitions(enterTransition, exitTransition,
sharedElementTransition);
} else {
TransitionSet staggered = new TransitionSet()
.addTransition(exitTransition)
.addTransition(enterTransition)
.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
transition = TransitionUtils.mergeTransitions(staggered, sharedElementTransition);
}
return transition;
}
/**
* Configures custom transitions for a specific fragment container.
*
* @param containerId The container ID of the fragments to configure the transition for.
* @param state The Transition State to be shared with {@link #updateTransitionEndState(
* android.app.BackStackRecord.TransitionState, android.util.SparseArray,
* android.util.SparseArray, boolean)} later.
* @param firstOutFragments The list of first fragments to be removed, keyed on the
* container ID.
* @param lastInFragments The list of last fragments to be added, keyed on the
* container ID.
* @param isBack true if this is popping the back stack or false if this is a
* forward operation.
* @param tempViews1 A temporary mapping of names to Views, used to avoid allocation
* inside a loop.
* @param tempViews2 A temporary mapping of names to Views, used to avoid allocation
* inside a loop.
* @param tempNames A temporary list of Strings, used to avoid allocation inside a loop.
* @param tempViewList A temporary list of Views, used to avoid allocation inside a loop.
*/
private void configureTransitions(int containerId, TransitionState state, boolean isBack,
SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments,
ArrayMap<String, View> tempViews1, ArrayMap<String, View> tempViews2,
ArrayList<String> tempNames, ArrayList<View> tempViewList) {
ViewGroup sceneRoot = (ViewGroup) mManager.mContainer.findViewById(containerId);
if (sceneRoot != null) {
Fragment inFragment = lastInFragments.get(containerId);
Fragment outFragment = firstOutFragments.get(containerId);
Transition enterTransition = getEnterTransition(inFragment, isBack);
Transition sharedElementTransition = getSharedElementTransition(inFragment, outFragment,
isBack);
Transition exitTransition = getExitTransition(outFragment, isBack);
exitTransition = captureExitingViews(exitTransition, outFragment, tempViewList);
ArrayMap<String, View> namedViews = tempViews1;
namedViews.clear();
if (sharedElementTransition != null) {
namedViews = remapSharedElements(state,
outFragment, namedViews, tempViews2, isBack);
}
// Notify the start of the transition.
SharedElementListener listener = isBack ?
outFragment.mEnterTransitionListener :
inFragment.mEnterTransitionListener;
tempNames.clear();
tempNames.addAll(namedViews.keySet());
tempViewList.clear();
tempViewList.addAll(namedViews.values());
listener.setSharedElementStart(tempNames, tempViewList, null);
// Set the epicenter of the exit transition
if (mSharedElementTargetNames != null && exitTransition != null) {
View epicenterView = namedViews.get(mSharedElementTargetNames.get(0));
if (epicenterView != null) {
setEpicenter(exitTransition, epicenterView);
}
}
prepareEnterTransition(state, enterTransition, sceneRoot, inFragment);
Transition transition = mergeTransitions(enterTransition, exitTransition,
sharedElementTransition, inFragment, isBack);
if (transition != null) {
state.overallTransitions.put(containerId, transition);
transition.setNameOverrides(state.nameOverrides);
// We want to exclude hidden views later, so we need a non-null list in the
// transition now.
transition.excludeTarget(state.nonExistentView, true);
// Now exclude all currently hidden fragments.
excludeHiddenFragments(state, containerId, transition);
cleanupHiddenFragments(transition, state);
TransitionManager.beginDelayedTransition(sceneRoot, transition);
}
}
}
private void excludeHiddenFragments(TransitionState state, boolean forceExclude) {
if (mManager.mAdded != null) {
for (int i = 0; i < mManager.mAdded.size(); i++) {
Fragment fragment = mManager.mAdded.get(i);
if (fragment.mView != null && fragment.mHidden
&& (forceExclude || !state.hiddenViews.contains(fragment.mView))) {
state.excludingTransition.excludeTarget(fragment.mView, true);
state.hiddenViews.add(fragment.mView);
/**
* Remaps a name-to-View map, substituting different names for keys.
*
* @param inMap A list of keys found in the map, in the order in toGoInMap
* @param toGoInMap A list of keys to use for the new map, in the order of inMap
* @param namedViews The current mapping
* @param tempMap A temporary mapping that will be filled with the new values.
* @return tempMap after it has been mapped with the new names as keys.
*/
private static ArrayMap<String, View> remapNames(ArrayList<String> inMap,
ArrayList<String> toGoInMap, ArrayMap<String, View> namedViews,
ArrayMap<String, View> tempMap) {
tempMap.clear();
if (!namedViews.isEmpty()) {
int numKeys = inMap.size();
for (int i = 0; i < numKeys; i++) {
View view = namedViews.get(inMap.get(i));
if (view != null) {
tempMap.put(toGoInMap.get(i), view);
}
}
}
if (forceExclude && state.hiddenViews.isEmpty()) {
state.excludingTransition.excludeTarget(new View(mManager.mActivity), true);
return tempMap;
}
/**
* After making all fragment changes, this updates the custom transitions to take into
* account the entering views and any remapping.
*
* @param state The transition State as returned from {@link #beginTransition(
* android.util.SparseArray, android.util.SparseArray, boolean)}.
* @param outFragments The list of first fragments to be removed, keyed on the
* container ID.
* @param inFragments The list of last fragments to be added, keyed on the
* container ID.
* @param isBack true if this is popping the back stack or false if this is a
* forward operation.
*/
private void updateTransitionEndState(TransitionState state, SparseArray<Fragment> outFragments,
SparseArray<Fragment> inFragments, boolean isBack) {
ArrayMap<String, View> tempViews1 = new ArrayMap<String, View>();
ArrayMap<String, View> tempViews2 = new ArrayMap<String, View>();
ArrayList<String> tempNames = new ArrayList<String>();
ArrayList<View> tempViews = new ArrayList<View>();
int numInFragments = inFragments.size();
for (int i = 0; i < numInFragments; i++) {
Fragment inFragment = inFragments.valueAt(i);
tempViews1.clear();
ArrayMap<String, View> namedViews = mapEnteringSharedElements(inFragment, tempViews1,
tempViews2, isBack);
// remap shared elements and set the name mapping used in the shared element transition.
if (isBack) {
inFragment.mExitTransitionListener.remapSharedElements(
mSharedElementTargetNames, namedViews);
setBackNameOverrides(state, namedViews, true);
} else {
inFragment.mEnterTransitionListener.remapSharedElements(
mSharedElementTargetNames, namedViews);
setNameOverrides(state, namedViews, true);
}
if (mSharedElementTargetNames != null && !namedViews.isEmpty()) {
// now we know the epicenter of the entering transition.
View epicenter = namedViews.get(mSharedElementTargetNames.get(0));
if (epicenter != null) {
state.enteringEpicenterView = epicenter;
}
}
int containerId = inFragments.keyAt(i);
SharedElementListener sharedElementListener = isBack ?
outFragments.get(containerId).mEnterTransitionListener :
inFragment.mEnterTransitionListener;
tempNames.clear();
tempNames.addAll(namedViews.keySet());
tempViews.clear();
tempViews.addAll(namedViews.values());
sharedElementListener.setSharedElementEnd(tempNames, tempViews, null);
}
// Don't include any newly-hidden fragments in the transition.
excludeHiddenFragments(state);
}
private ArrayMap<String, View> mapEnteringSharedElements(Fragment inFragment,
ArrayMap<String, View> namedViews, ArrayMap<String, View> tempViews2, boolean isBack) {
View root = inFragment.getView();
if (root != null) {
if (mSharedElementSourceNames != null) {
root.findNamedViews(namedViews);
if (isBack) {
namedViews = remapNames(mSharedElementSourceNames,
mSharedElementTargetNames, namedViews, tempViews2);
} else {
namedViews.retainAll(mSharedElementTargetNames);
}
}
}
return namedViews;
}
private static void cleanupHiddenFragments(Transition transition, TransitionState state) {
final ArrayList<View> hiddenViews = state.hiddenFragmentViews;
transition.addListener(new Transition.TransitionListenerAdapter() {
@Override
public void onTransitionStart(Transition transition) {
transition.removeListener(this);
int numViews = hiddenViews.size();
for (int i = 0; i < numViews; i++) {
transition.excludeTarget(hiddenViews.get(i), false);
}
}
});
}
private void excludeHiddenFragments(TransitionState state, int containerId,
Transition transition) {
if (mManager.mAdded != null) {
for (int i = 0; i < mManager.mAdded.size(); i++) {
Fragment fragment = mManager.mAdded.get(i);
if (fragment.mView != null && fragment.mContainer != null &&
fragment.mContainerId == containerId) {
if (fragment.mHidden) {
if (!state.hiddenFragmentViews.contains(fragment.mView)) {
transition.excludeTarget(fragment.mView, true);
state.hiddenFragmentViews.add(fragment.mView);
}
} else {
transition.excludeTarget(fragment.mView, false);
state.hiddenFragmentViews.remove(fragment.mView);
}
}
}
}
}
private void includeVisibleFragments(Transition transition) {
if (mManager.mAdded != null) {
for (int i = 0; i < mManager.mAdded.size(); i++) {
Fragment fragment = mManager.mAdded.get(i);
if (fragment.mView != null && !fragment.mHidden) {
transition.excludeTarget(fragment.mView, false);
private void excludeHiddenFragments(TransitionState state) {
int numTransitions = state.overallTransitions.size();
for (int i = 0; i < numTransitions; i++) {
Transition transition = state.overallTransitions.valueAt(i);
int containerId = state.overallTransitions.keyAt(i);
excludeHiddenFragments(state, containerId, transition);
}
}
private static void addTransitioningViews(Transition transition, final Collection<View> views) {
for (View view : views) {
transition.addTarget(view);
}
transition.addListener(new Transition.TransitionListenerAdapter() {
@Override
public void onTransitionStart(Transition transition) {
transition.removeListener(this);
for (View view : views) {
transition.removeTarget(view);
}
}
}
});
}
private static void setEpicenter(Transition transition, View view) {
@@ -1010,16 +1464,17 @@ final class BackStackRecord extends FragmentTransaction implements
@Override
public Rect onGetEpicenter(Transition transition) {
if (mEpicenter == null && state.mEnteringEpicenterView != null) {
if (mEpicenter == null && state.enteringEpicenterView != null) {
mEpicenter = new Rect();
state.mEnteringEpicenterView.getBoundsOnScreen(mEpicenter);
state.enteringEpicenterView.getBoundsOnScreen(mEpicenter);
}
return mEpicenter;
}
});
}
public TransitionState popFromBackStack(boolean doStateMove, TransitionState state) {
public TransitionState popFromBackStack(boolean doStateMove, TransitionState state,
SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "popFromBackStack: " + this);
LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
@@ -1029,8 +1484,10 @@ final class BackStackRecord extends FragmentTransaction implements
}
if (state == null) {
state = beginTransition(mSharedElementTargetNames, mSharedElementSourceNames);
} else {
if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) {
state = beginTransition(firstOutFragments, lastInFragments, true);
}
} else if (!doStateMove) {
setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames);
}
@@ -1110,7 +1567,7 @@ final class BackStackRecord extends FragmentTransaction implements
mManager.moveToState(mManager.mCurState,
FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);
if (state != null) {
updateTransitionEndState(state, mSharedElementSourceNames);
updateTransitionEndState(state, firstOutFragments, lastInFragments, true);
state = null;
}
}
@@ -1122,15 +1579,17 @@ final class BackStackRecord extends FragmentTransaction implements
return state;
}
private static void setNameOverride(Transition transition, String source, String target) {
ArrayMap<String, String> overrides = transition.getNameOverrides();
for (int index = 0; index < overrides.size(); index++) {
if (source.equals(overrides.valueAt(index))) {
overrides.setValueAt(index, target);
return;
private static void setNameOverride(ArrayMap<String, String> overrides,
String source, String target) {
if (source != null && target != null && !source.equals(target)) {
for (int index = 0; index < overrides.size(); index++) {
if (source.equals(overrides.valueAt(index))) {
overrides.setValueAt(index, target);
return;
}
}
overrides.put(source, target);
}
overrides.put(source, target);
}
private static void setNameOverrides(TransitionState state, ArrayList<String> sourceNames,
@@ -1139,7 +1598,36 @@ final class BackStackRecord extends FragmentTransaction implements
for (int i = 0; i < sourceNames.size(); i++) {
String source = sourceNames.get(i);
String target = targetNames.get(i);
setNameOverride(state.excludingTransition, source, target);
setNameOverride(state.nameOverrides, source, target);
}
}
}
private void setBackNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
boolean isEnd) {
int count = mSharedElementTargetNames.size();
for (int i = 0; i < count; i++) {
String source = mSharedElementSourceNames.get(i);
String originalTarget = mSharedElementTargetNames.get(i);
String target = namedViews.get(originalTarget).getTransitionName();
if (isEnd) {
setNameOverride(state.nameOverrides, source, target);
} else {
setNameOverride(state.nameOverrides, target, source);
}
}
}
private void setNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
boolean isEnd) {
int count = namedViews.size();
for (int i = 0; i < count; i++) {
String source = namedViews.keyAt(i);
String target = namedViews.valueAt(i).getTransitionName();
if (isEnd) {
setNameOverride(state.nameOverrides, source, target);
} else {
setNameOverride(state.nameOverrides, target, source);
}
}
}
@@ -1161,14 +1649,11 @@ final class BackStackRecord extends FragmentTransaction implements
}
public class TransitionState {
public ArrayList<View> hiddenViews = new ArrayList<View>();
public ArrayList<View> transitioningViews = new ArrayList<View>();
public ArrayMap<String, View> namedViews = new ArrayMap<String, View>();
public Transition exitTransition;
public Transition sharedElementTransition;
public Transition enterTransition;
public TransitionSet excludingTransition;
public ViewGroup sceneRoot;
public View mEnteringEpicenterView;
public SparseArray<Transition> overallTransitions = new SparseArray<Transition>();
public ArrayMap<String, String> nameOverrides = new ArrayMap<String, String>();
public ArrayList<View> hiddenFragmentViews = new ArrayList<View>();
public View enteringEpicenterView;
public View nonExistentView;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1497,7 +1497,10 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
return false;
}
final BackStackRecord bss = mBackStack.remove(last);
bss.popFromBackStack(true, null);
SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
bss.calculateBackFragments(firstOutFragments, lastInFragments);
bss.popFromBackStack(true, null, firstOutFragments, lastInFragments);
reportBackStackChanged();
} else {
int index = -1;
@@ -1541,10 +1544,16 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
states.add(mBackStack.remove(i));
}
final int LAST = states.size()-1;
SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
for (int i=0; i<=LAST; i++) {
states.get(i).calculateBackFragments(firstOutFragments, lastInFragments);
}
BackStackRecord.TransitionState state = null;
for (int i=0; i<=LAST; i++) {
if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
state = states.get(i).popFromBackStack(i == LAST, state);
state = states.get(i).popFromBackStack(i == LAST, state,
firstOutFragments, lastInFragments);
}
reportBackStackChanged();
}

View File

@@ -172,19 +172,16 @@ public abstract class FragmentTransaction {
public abstract FragmentTransaction setTransition(int transit);
/**
* Set a {@link android.transition.Transition} resource id to use with this transaction.
* <var>transitionId</var> will be played for fragments when going forward and when popping
* the back stack.
* @param sceneRootId The ID of the element acting as the scene root for the transition.
* This should be a ViewGroup containing all Fragments in the transaction.
* @param transitionId The resource ID for the Transition used during the Fragment transaction.
* TODO: remove from API
* @hide
*/
public abstract FragmentTransaction setCustomTransition(int sceneRootId, int transitionId);
public FragmentTransaction setCustomTransition(int sceneRootId, int transitionId) {
return this;
}
/**
* Used with {@link #setCustomTransition(int, int)} to map a View from a removed or hidden
* Fragment to a View from a shown or added Fragment.
* <var>sharedElement</var> must have a unique transitionName in the View hierarchy.
* Used with to map a View from a removed or hidden Fragment to a View from a shown
* or added Fragment.
* @param sharedElement A View in a disappearing Fragment to match with a View in an
* appearing Fragment.
* @param name The transitionName for a View in an appearing Fragment to match to the shared

View File

@@ -40,6 +40,33 @@ public class TransitionUtils {
}
}
public static Transition mergeTransitions(Transition... transitions) {
int count = 0;
int nonNullIndex = -1;
for (int i = 0; i < transitions.length; i++) {
if (transitions[i] != null) {
count++;
nonNullIndex = i;
}
}
if (count == 0) {
return null;
}
if (count == 1) {
return transitions[nonNullIndex];
}
TransitionSet transitionSet = new TransitionSet();
for (int i = 0; i < transitions.length; i++) {
if (transitions[i] != null) {
transitionSet.addTransition(transitions[i]);
}
}
return transitionSet;
}
public static class MatrixEvaluator implements TypeEvaluator<Matrix> {
float[] mTempStartValues = new float[9];

View File

@@ -1430,7 +1430,9 @@ public abstract class Window {
* {@link android.transition.Visibility} as entering is governed by changing visibility from
* {@link View#INVISIBLE} to {@link View#VISIBLE}. If <code>transition</code> is null,
* entering Views will remain unaffected.
*
* @param transition The Transition to use to move Views into the initial Scene.
* @attr ref android.R.styleable#Window_windowEnterTransition
*/
public void setEnterTransition(Transition transition) {}
@@ -1444,8 +1446,10 @@ public abstract class Window {
* {@link View#VISIBLE} to {@link View#INVISIBLE}. If <code>transition</code> is null,
* entering Views will remain unaffected. If nothing is set, the default will be to
* use the same value as set in {@link #setEnterTransition(android.transition.Transition)}.
*
* @param transition The Transition to use to move Views out of the Scene when the Window
* is preparing to close.
* @attr ref android.R.styleable#Window_windowReturnTransition
*/
public void setReturnTransition(Transition transition) {}
@@ -1456,8 +1460,10 @@ public abstract class Window {
* {@link android.transition.Visibility} as exiting is governed by changing visibility
* from {@link View#VISIBLE} to {@link View#INVISIBLE}. If transition is null, the views will
* remain unaffected. Requires {@link #FEATURE_CONTENT_TRANSITIONS}.
*
* @param transition The Transition to use to move Views out of the scene when calling a
* new Activity.
* @attr ref android.R.styleable#Window_windowExitTransition
*/
public void setExitTransition(Transition transition) {}
@@ -1470,8 +1476,10 @@ public abstract class Window {
* the views will remain unaffected. If nothing is set, the default will be to use the same
* transition as {@link #setExitTransition(android.transition.Transition)}.
* Requires {@link #FEATURE_CONTENT_TRANSITIONS}.
*
* @param transition The Transition to use to move Views into the scene when reentering from a
* previously-started Activity.
* @attr ref android.R.styleable#Window_windowReenterTransition
*/
public void setReenterTransition(Transition transition) {}
@@ -1484,6 +1492,7 @@ public abstract class Window {
* entering Views will remain unaffected. Requires {@link #FEATURE_CONTENT_TRANSITIONS}.
*
* @return the Transition to use to move Views into the initial Scene.
* @attr ref android.R.styleable#Window_windowEnterTransition
*/
public Transition getEnterTransition() { return null; }
@@ -1495,8 +1504,10 @@ public abstract class Window {
* {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend
* {@link android.transition.Visibility} as entering is governed by changing visibility from
* {@link View#VISIBLE} to {@link View#INVISIBLE}.
*
* @return The Transition to use to move Views out of the Scene when the Window
* is preparing to close.
* @attr ref android.R.styleable#Window_windowReturnTransition
*/
public Transition getReturnTransition() { return null; }
@@ -1507,8 +1518,10 @@ public abstract class Window {
* {@link android.transition.Visibility} as exiting is governed by changing visibility
* from {@link View#VISIBLE} to {@link View#INVISIBLE}. If transition is null, the views will
* remain unaffected. Requires {@link #FEATURE_CONTENT_TRANSITIONS}.
*
* @return the Transition to use to move Views out of the scene when calling a
* new Activity.
* @attr ref android.R.styleable#Window_windowExitTransition
*/
public Transition getExitTransition() { return null; }
@@ -1519,8 +1532,10 @@ public abstract class Window {
* will extend {@link android.transition.Visibility} as exiting is governed by changing
* visibility from {@link View#VISIBLE} to {@link View#INVISIBLE}.
* Requires {@link #FEATURE_CONTENT_TRANSITIONS}.
*
* @return The Transition to use to move Views into the scene when reentering from a
* previously-started Activity.
* @attr ref android.R.styleable#Window_windowReenterTransition
*/
public Transition getReenterTransition() { return null; }
@@ -1530,8 +1545,10 @@ public abstract class Window {
* {@link android.transition.ChangeBounds}. A null
* value will cause transferred shared elements to blink to the final position.
* Requires {@link #FEATURE_CONTENT_TRANSITIONS}.
*
* @param transition The Transition to use for shared elements transferred into the content
* Scene.
* @attr ref android.R.styleable#Window_windowSharedElementEnterTransition
*/
public void setSharedElementEnterTransition(Transition transition) {}
@@ -1543,22 +1560,28 @@ public abstract class Window {
* If no value is set, the default will be to use the same value as
* {@link #setSharedElementEnterTransition(android.transition.Transition)}.
* Requires {@link #FEATURE_CONTENT_TRANSITIONS}.
*
* @param transition The Transition to use for shared elements transferred out of the content
* Scene.
* @attr ref android.R.styleable#Window_windowSharedElementReturnTransition
*/
public void setSharedElementReturnTransition(Transition transition) {}
/**
* Returns the Transition that will be used for shared elements transferred into the content
* Scene. Requires {@link #FEATURE_CONTENT_TRANSITIONS}.
*
* @return Transition to use for sharend elements transferred into the content Scene.
* @attr ref android.R.styleable#Window_windowSharedElementEnterTransition
*/
public Transition getSharedElementEnterTransition() { return null; }
/**
* Returns the Transition that will be used for shared elements transferred back to a
* calling Activity. Requires {@link #FEATURE_CONTENT_TRANSITIONS}.
*
* @return Transition to use for sharend elements transferred into the content Scene.
* @attr ref android.R.styleable#Window_windowSharedElementReturnTransition
*/
public Transition getSharedElementReturnTransition() { return null; }
@@ -1568,8 +1591,10 @@ public abstract class Window {
* must animate during the exit transition, this Transition should be used. Upon completion,
* the shared elements may be transferred to the started Activity.
* Requires {@link #FEATURE_CONTENT_TRANSITIONS}.
*
* @param transition The Transition to use for shared elements in the launching Window
* prior to transferring to the launched Activity's Window.
* @attr ref android.R.styleable#Window_windowSharedElementExitTransition
*/
public void setSharedElementExitTransition(Transition transition) {}
@@ -1579,8 +1604,10 @@ public abstract class Window {
* is set, this will default to
* {@link #setSharedElementExitTransition(android.transition.Transition)}.
* Requires {@link #FEATURE_CONTENT_TRANSITIONS}.
*
* @param transition The Transition to use for shared elements in the launching Window
* after the shared element has returned to the Window.
* @attr ref android.R.styleable#Window_windowSharedElementReenterTransition
*/
public void setSharedElementReenterTransition(Transition transition) {}
@@ -1591,6 +1618,7 @@ public abstract class Window {
*
* @return the Transition to use for shared elements in the launching Window prior
* to transferring to the launched Activity's Window.
* @attr ref android.R.styleable#Window_windowSharedElementExitTransition
*/
public Transition getSharedElementExitTransition() { return null; }
@@ -1601,6 +1629,7 @@ public abstract class Window {
*
* @return the Transition that will be used for shared elements reentering from a started
* Activity after it has returned the shared element to it start location.
* @attr ref android.R.styleable#Window_windowSharedElementReenterTransition
*/
public Transition getSharedElementReenterTransition() { return null; }
@@ -1610,8 +1639,10 @@ public abstract class Window {
* transition of the calling Activity. When true, the transition will start as soon as possible.
* When false, the transition will wait until the remote exiting transition completes before
* starting.
*
* @param allow true to start the enter transition when possible or false to
* wait until the exiting transition completes.
* @attr ref android.R.styleable#Window_windowAllowEnterTransitionOverlap
*/
public void setAllowEnterTransitionOverlap(boolean allow) {}
@@ -1621,8 +1652,10 @@ public abstract class Window {
* transition of the calling Activity. When true, the transition will start as soon as possible.
* When false, the transition will wait until the remote exiting transition completes before
* starting.
*
* @return true when the enter transition should start as soon as possible or false to
* when it should wait until the exiting transition completes.
* @attr ref android.R.styleable#Window_windowAllowEnterTransitionOverlap
*/
public boolean getAllowEnterTransitionOverlap() { return true; }
@@ -1632,10 +1665,20 @@ public abstract class Window {
* transition of the called Activity when reentering after if finishes. When true,
* the transition will start as soon as possible. When false, the transition will wait
* until the called Activity's exiting transition completes before starting.
*
* @param allow true to start the transition when possible or false to wait until the
* called Activity's exiting transition completes.
* @attr ref android.R.styleable#Window_windowAllowReturnTransitionOverlap
*/
public void setAllowExitTransitionOverlap(boolean allow) {}
public void setAllowReturnTransitionOverlap(boolean allow) {}
/**
* TODO: remove this.
* @hide
*/
public void setAllowExitTransitionOverlap(boolean allow) {
setAllowReturnTransitionOverlap(allow);
}
/**
* Returns how the transition set in
@@ -1643,10 +1686,18 @@ public abstract class Window {
* transition of the called Activity when reentering after if finishes. When true,
* the transition will start as soon as possible. When false, the transition will wait
* until the called Activity's exiting transition completes before starting.
*
* @return true when the transition should start when possible or false when it should wait
* until the called Activity's exiting transition completes.
* @attr ref android.R.styleable#Window_windowAllowReturnTransitionOverlap
*/
public boolean getAllowExitTransitionOverlap() { return true; }
public boolean getAllowReturnTransitionOverlap() { return true; }
/**
* TODO: remove this.
* @hide
*/
public boolean getAllowExitTransitionOverlap() { return getAllowReturnTransitionOverlap(); }
/**
* Returns the duration, in milliseconds, of the window background fade
@@ -1654,8 +1705,10 @@ public abstract class Window {
* <p>When executing the enter transition, the background starts transparent
* and fades in. This requires {@link #FEATURE_CONTENT_TRANSITIONS}. The default is
* 300 milliseconds.</p>
*
* @return The duration of the window background fade to opaque during enter transition.
* @see #getEnterTransition()
* @attr ref android.R.styleable#Window_windowTransitionBackgroundFadeDuration
*/
public long getTransitionBackgroundFadeDuration() { return 0; }
@@ -1665,9 +1718,11 @@ public abstract class Window {
* <p>When executing the enter transition, the background starts transparent
* and fades in. This requires {@link #FEATURE_CONTENT_TRANSITIONS}. The default is
* 300 milliseconds.</p>
*
* @param fadeDurationMillis The duration of the window background fade to or from opaque
* during enter transition.
* @see #setEnterTransition(android.transition.Transition)
* @attr ref android.R.styleable#Window_windowTransitionBackgroundFadeDuration
*/
public void setTransitionBackgroundFadeDuration(long fadeDurationMillis) { }
@@ -1679,6 +1734,7 @@ public abstract class Window {
* @return <code>true</code> when shared elements should use an Overlay during
* shared element transitions or <code>false</code> when they should animate as
* part of the normal View hierarchy.
* @attr ref android.R.styleable#Window_windowSharedElementsUseOverlay
*/
public boolean getSharedElementsUseOverlay() { return true; }
@@ -1689,6 +1745,7 @@ public abstract class Window {
* @param sharedElementsUseOverlay <code>true</code> indicates that shared elements should
* be transitioned with an Overlay or <code>false</code>
* to transition within the normal View hierarchy.
* @attr ref android.R.styleable#Window_windowSharedElementsUseOverlay
*/
public void setSharedElementsUseOverlay(boolean sharedElementsUseOverlay) { }

View File

@@ -514,8 +514,8 @@
<!-- Flag indicating whether this Window's transition should overlap with
the exiting transition of the called Activity when the called Activity
finishes. Corresponds to
{@link android.view.Window#setAllowExitTransitionOverlap(boolean)}. -->
<attr name="windowAllowExitTransitionOverlap" format="boolean"/>
{@link android.view.Window#setAllowReturnTransitionOverlap(boolean)}. -->
<attr name="windowAllowReturnTransitionOverlap" format="boolean"/>
<!-- Indicates whether or not shared elements should use an overlay
during transitions. The default value is true. -->
@@ -1853,8 +1853,8 @@
<!-- Flag indicating whether this Window's transition should overlap with
the exiting transition of the called Activity when the called Activity
finishes. Corresponds to
{@link android.view.Window#setAllowExitTransitionOverlap(boolean)}. -->
<attr name="windowAllowExitTransitionOverlap"/>
{@link android.view.Window#setAllowReturnTransitionOverlap(boolean)}. -->
<attr name="windowAllowReturnTransitionOverlap"/>
<!-- Indicates whether or not shared elements should use an overlay
during transitions. The default value is true. -->
@@ -6627,6 +6627,53 @@
IDs (through the android:id attribute) instead of tags because
they are faster and allow for compile-time type checking. -->
<attr name="tag" />
<!-- The Transition that will be used to move Views out of the scene when the
fragment is removed, hidden, or detached when not popping the back stack.
Corresponds to {@link android.app.Fragment#setExitTransition(
android.transition.Transition)} -->
<attr name="fragmentExitTransition" format="reference"/>
<!-- The Transition that will be used to move Views into the initial scene.
Corresponds to {@link android.app.Fragment#setEnterTransition(
android.transition.Transition)} -->
<attr name="fragmentEnterTransition" format="reference"/>
<!-- The Transition that will be used for shared elements transferred into the content
Scene.
Corresponds to {@link android.app.Fragment#setSharedElementEnterTransition(
android.transition.Transition)} -->
<attr name="fragmentSharedElementEnterTransition" format="reference"/>
<!-- The Transition that will be used to move Views out of the scene when the Fragment is
preparing to be removed, hidden, or detached because of popping the back stack.
Corresponds to {@link android.app.Fragment#setReturnTransition(
android.transition.Transition)} -->
<attr name="fragmentReturnTransition" format="reference"/>
<!-- The Transition that will be used for shared elements transferred back during a
pop of the back stack. This Transition acts in the leaving Fragment.
Corresponds to {@link android.app.Fragment#setSharedElementReturnTransition(
android.transition.Transition)} -->
<attr name="fragmentSharedElementReturnTransition" format="reference"/>
<!-- The Transition that will be used to move Views in to the scene when returning due
to popping a back stack.
Corresponds to {@link android.app.Fragment#setReenterTransition(
android.transition.Transition)} -->
<attr name="fragmentReenterTransition" format="reference"/>
<!-- Sets whether the enter and exit transitions should overlap when transitioning
forward.
Corresponds to {@link android.app.Fragment#setAllowEnterTransitionOverlap(
boolean)} -->
<attr name="fragmentAllowEnterTransitionOverlap" format="reference"/>
<!-- Sets whether the enter and exit transitions should overlap when transitioning
because of popping the back stack.
Corresponds to {@link android.app.Fragment#setAllowReturnTransitionOverlap(
boolean)} -->
<attr name="fragmentAllowReturnTransitionOverlap" format="reference"/>
</declare-styleable>
<!-- Use <code>device-admin</code> as the root tag of the XML resource that

View File

@@ -2146,7 +2146,7 @@
<public type="attr" name="windowExitTransition" />
<public type="attr" name="windowSharedElementEnterTransition" />
<public type="attr" name="windowSharedElementExitTransition" />
<public type="attr" name="windowAllowExitTransitionOverlap" />
<public type="attr" name="windowAllowReturnTransitionOverlap" />
<public type="attr" name="windowAllowEnterTransitionOverlap" />
<public type="attr" name="sessionService" />
<public type="attr" name="stackViewStyle" />
@@ -2282,6 +2282,14 @@
<public type="attr" name="spotShadowAlpha" />
<public type="attr" name="navigationIcon" />
<public type="attr" name="navigationContentDescription" />
<public type="attr" name="fragmentExitTransition" />
<public type="attr" name="fragmentEnterTransition" />
<public type="attr" name="fragmentSharedElementEnterTransition" />
<public type="attr" name="fragmentReturnTransition" />
<public type="attr" name="fragmentSharedElementReturnTransition" />
<public type="attr" name="fragmentReenterTransition" />
<public type="attr" name="fragmentAllowEnterTransitionOverlap" />
<public type="attr" name="fragmentAllowReturnTransitionOverlap" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />

View File

@@ -264,7 +264,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private Transition mSharedElementReturnTransition = USE_DEFAULT_TRANSITION;
private Transition mSharedElementExitTransition = null;
private Transition mSharedElementReenterTransition = USE_DEFAULT_TRANSITION;
private Boolean mAllowExitTransitionOverlap;
private Boolean mAllowReturnTransitionOverlap;
private Boolean mAllowEnterTransitionOverlap;
private long mBackgroundFadeDurationMillis = -1;
private Boolean mSharedElementsUseOverlay;
@@ -3549,9 +3549,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
mAllowEnterTransitionOverlap = getWindowStyle().getBoolean(
R.styleable.Window_windowAllowEnterTransitionOverlap, true);
}
if (mAllowExitTransitionOverlap == null) {
mAllowExitTransitionOverlap = getWindowStyle().getBoolean(
R.styleable.Window_windowAllowExitTransitionOverlap, true);
if (mAllowReturnTransitionOverlap == null) {
mAllowReturnTransitionOverlap = getWindowStyle().getBoolean(
R.styleable.Window_windowAllowReturnTransitionOverlap, true);
}
if (mBackgroundFadeDurationMillis < 0) {
mBackgroundFadeDurationMillis = getWindowStyle().getInteger(
@@ -4017,13 +4017,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
@Override
public void setAllowExitTransitionOverlap(boolean allowExitTransitionOverlap) {
mAllowExitTransitionOverlap = allowExitTransitionOverlap;
public void setAllowReturnTransitionOverlap(boolean allowExitTransitionOverlap) {
mAllowReturnTransitionOverlap = allowExitTransitionOverlap;
}
@Override
public boolean getAllowExitTransitionOverlap() {
return (mAllowExitTransitionOverlap == null) ? true : mAllowExitTransitionOverlap;
public boolean getAllowReturnTransitionOverlap() {
return (mAllowReturnTransitionOverlap == null) ? true : mAllowReturnTransitionOverlap;
}
@Override