java.lang.Object
↳androidx.transition.TransitionManager
Gradle dependencies
compile group: 'androidx.transition', name: 'transition', version: '1.5.1'
- groupId: androidx.transition
- artifactId: transition
- version: 1.5.1
Artifact androidx.transition:transition:1.5.1 it located at Google repository (https://maven.google.com/)
Androidx artifact mapping:
androidx.transition:transition com.android.support:transition
Androidx class mapping:
androidx.transition.TransitionManager android.support.transition.TransitionManager
Overview
This class manages the set of transitions that fire when there is a
change of Scene. To use the manager, add scenes along with
transition objects with calls to TransitionManager.setTransition(Scene, Transition)
or TransitionManager.setTransition(Scene, Scene, Transition). Setting specific
transitions for scene changes is not required; by default, a Scene change
will use AutoTransition to do something reasonable for most
situations. Specifying other transitions for particular scene changes is
only necessary if the application wants different transition behavior
in these situations.
TransitionManagers can be declared in XML resource files inside the
res/transition
directory. TransitionManager resources consist of
the transitionManager
tag name, containing one or more
transition
tags, each of which describe the relationship of
that transition to the from/to scene information in that tag.
For example, here is a resource file that declares several scene
transitions:
<transitionManager xmlns:android="http://schemas.android.com/apk/res/android">
<transition android:fromScene="@layout/transition_scene1"
android:toScene="@layout/transition_scene2"
android:transition="@transition/changebounds"/>
<transition android:fromScene="@layout/transition_scene2"
android:toScene="@layout/transition_scene1"
android:transition="@transition/changebounds"/>
<transition android:toScene="@layout/transition_scene3"
android:transition="@transition/changebounds_fadein_together"/>
<transition android:fromScene="@layout/transition_scene3"
android:toScene="@layout/transition_scene1"
android:transition="@transition/changebounds_fadeout_sequential"/>
<transition android:fromScene="@layout/transition_scene3"
android:toScene="@layout/transition_scene2"
android:transition="@transition/changebounds_fadeout_sequential"/>
</transitionManager>
For each of the fromScene
and toScene
attributes,
there is a reference to a standard XML layout file. This is equivalent to
creating a scene from a layout in code by calling
Scene.getSceneForLayout(ViewGroup, int, Context). For the
transition
attribute, there is a reference to a resource
file in the res/transition
directory which describes that
transition.
Summary
Methods |
---|
public static void | beginDelayedTransition(ViewGroup sceneRoot)
Convenience method to animate, using the default transition,
to a new scene defined by all changes within the given scene root between
calling this method and the next rendering frame. |
public static void | beginDelayedTransition(ViewGroup sceneRoot, Transition transition)
Convenience method to animate to a new scene defined by all changes within
the given scene root between calling this method and the next rendering frame. |
public static TransitionSeekController | controlDelayedTransition(ViewGroup sceneRoot, Transition transition)
Create a TransitionSeekController to allow seeking an animation to a new
scene defined by all changes within the given scene root between calling this method and
the next rendered frame. |
public static TransitionSeekController | createSeekController(Scene scene, Transition transition)
Convenience method to seek to the given scene using the given transition. |
public static void | endTransitions(ViewGroup sceneRoot)
Ends all pending and ongoing transitions on the specified scene root. |
public static void | go(Scene scene)
Convenience method to simply change to the given scene using
the default transition for TransitionManager. |
public static void | go(Scene scene, Transition transition)
Convenience method to simply change to the given scene using
the given transition. |
public void | setTransition(Scene fromScene, Scene toScene, Transition transition)
Sets a specific transition to occur when the given pair of scenes is
exited/entered. |
public void | setTransition(Scene scene, Transition transition)
Sets a specific transition to occur when the given scene is entered. |
public void | transitionTo(Scene scene)
Change to the given scene, using the
appropriate transition for this particular scene change
(as specified to the TransitionManager, or the default
if no such transition exists). |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Constructors
public
TransitionManager()
Methods
Sets a specific transition to occur when the given scene is entered.
Parameters:
scene: The scene which, when applied, will cause the given
transition to run.
transition: The transition that will play when the given scene is
entered. A value of null will result in the default behavior of
using the default transition instead.
Sets a specific transition to occur when the given pair of scenes is
exited/entered.
Parameters:
fromScene: The scene being exited when the given transition will
be run
toScene: The scene being entered when the given transition will
be run
transition: The transition that will play when the given scene is
entered. A value of null will result in the default behavior of
using the default transition instead.
public void
transitionTo(
Scene scene)
Change to the given scene, using the
appropriate transition for this particular scene change
(as specified to the TransitionManager, or the default
if no such transition exists).
Parameters:
scene: The Scene to change to
public static void
go(
Scene scene)
Convenience method to simply change to the given scene using
the default transition for TransitionManager.
Parameters:
scene: The Scene to change to
Convenience method to seek to the given scene using the given transition. If seeking
is not supported because the device is or earlier,
the scene transition is immediate and null is returned.
Parameters:
scene: The Scene to change to
transition: The transition to use for this scene change.
Returns:
a TransitionSeekController that can be used control the animation to the
destination scene. null is returned when seeking is not supported on the scene,
either because it is running on or earlier,
another Transition is being captured for sceneRoot, or sceneRoot hasn't
had a layout yet.
Convenience method to simply change to the given scene using
the given transition.
Passing in null
for the transition parameter will
result in the scene changing without any transition running, and is
equivalent to calling Scene.exit() on the scene root's
current scene, followed by Scene.enter() on the scene
specified by the scene
parameter.
Parameters:
scene: The Scene to change to
transition: The transition to use for this scene change. A
value of null causes the scene change to happen with no transition.
public static void
beginDelayedTransition(ViewGroup sceneRoot)
Convenience method to animate, using the default transition,
to a new scene defined by all changes within the given scene root between
calling this method and the next rendering frame.
Equivalent to calling TransitionManager.beginDelayedTransition(ViewGroup, Transition)
with a value of null
for the transition
parameter.
Parameters:
sceneRoot: The root of the View hierarchy to run the transition on.
public static void
beginDelayedTransition(ViewGroup sceneRoot,
Transition transition)
Convenience method to animate to a new scene defined by all changes within
the given scene root between calling this method and the next rendering frame.
Calling this method causes TransitionManager to capture current values in the
scene root and then post a request to run a transition on the next frame.
At that time, the new values in the scene root will be captured and changes
will be animated. There is no need to create a Scene; it is implied by
changes which take place between calling this method and the next frame when
the transition begins.
Calling this method several times before the next frame (for example, if
unrelated code also wants to make dynamic changes and run a transition on
the same scene root), only the first call will trigger capturing values
and exiting the current scene. Subsequent calls to the method with the
same scene root during the same frame will be ignored.
Passing in null
for the transition parameter will
cause the TransitionManager to use its default transition.
Parameters:
sceneRoot: The root of the View hierarchy to run the transition on.
transition: The transition to use for this change. A
value of null causes the TransitionManager to use the default transition.
Create a TransitionSeekController to allow seeking an animation to a new
scene defined by all changes within the given scene root between calling this method and
the next rendered frame. Calling this method causes TransitionManager to capture current
values in the scene root and then post a request to run a transition on the next frame.
At that time, the new values in the scene root will be captured and changes
will be animated. There is no need to create a Scene; it is implied by
changes which take place between calling this method and the next frame when
the transition begins.
Calling this method several times before the next frame (for example, if
unrelated code also wants to make dynamic changes and run a transition on
the same scene root), only the first call will trigger capturing values
and exiting the current scene. Subsequent calls to the method with the
same scene root during the same frame will be ignored.
Parameters:
sceneRoot: The root of the View hierarchy to run the transition on.
transition: The transition to use for this change.
Returns:
a TransitionSeekController that can be used control the animation to the
destination scene. null is returned when seeking is not supported on the scene,
either because it is running on or earlier,
another Transition is being captured for sceneRoot, or sceneRoot hasn't
had a layout yet.
public static void
endTransitions(ViewGroup sceneRoot)
Ends all pending and ongoing transitions on the specified scene root.
Parameters:
sceneRoot: The root of the View hierarchy to end transitions on.
Source
/*
* Copyright (C) 2016 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 androidx.transition;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.collection.ArrayMap;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
* This class manages the set of transitions that fire when there is a
* change of {@link Scene}. To use the manager, add scenes along with
* transition objects with calls to {@link #setTransition(Scene, Transition)}
* or {@link #setTransition(Scene, Scene, Transition)}. Setting specific
* transitions for scene changes is not required; by default, a Scene change
* will use {@link AutoTransition} to do something reasonable for most
* situations. Specifying other transitions for particular scene changes is
* only necessary if the application wants different transition behavior
* in these situations.
*
* <p>TransitionManagers can be declared in XML resource files inside the
* <code>res/transition</code> directory. TransitionManager resources consist of
* the <code>transitionManager</code>tag name, containing one or more
* <code>transition</code> tags, each of which describe the relationship of
* that transition to the from/to scene information in that tag.
* For example, here is a resource file that declares several scene
* transitions:</p>
*
* <pre>
* <transitionManager xmlns:android="http://schemas.android.com/apk/res/android">
* <transition android:fromScene="@layout/transition_scene1"
* android:toScene="@layout/transition_scene2"
* android:transition="@transition/changebounds"/>
* <transition android:fromScene="@layout/transition_scene2"
* android:toScene="@layout/transition_scene1"
* android:transition="@transition/changebounds"/>
* <transition android:toScene="@layout/transition_scene3"
* android:transition="@transition/changebounds_fadein_together"/>
* <transition android:fromScene="@layout/transition_scene3"
* android:toScene="@layout/transition_scene1"
* android:transition="@transition/changebounds_fadeout_sequential"/>
* <transition android:fromScene="@layout/transition_scene3"
* android:toScene="@layout/transition_scene2"
* android:transition="@transition/changebounds_fadeout_sequential"/>
* </transitionManager>
* </pre>
*
* <p>For each of the <code>fromScene</code> and <code>toScene</code> attributes,
* there is a reference to a standard XML layout file. This is equivalent to
* creating a scene from a layout in code by calling
* {@link Scene#getSceneForLayout(ViewGroup, int, Context)}. For the
* <code>transition</code> attribute, there is a reference to a resource
* file in the <code>res/transition</code> directory which describes that
* transition.</p>
*/
public class TransitionManager {
private static final String LOG_TAG = "TransitionManager";
private static Transition sDefaultTransition = new AutoTransition();
private ArrayMap<Scene, Transition> mSceneTransitions = new ArrayMap<>();
private ArrayMap<Scene, ArrayMap<Scene, Transition>> mScenePairTransitions = new ArrayMap<>();
private static ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>>
sRunningTransitions = new ThreadLocal<>();
@SuppressWarnings("WeakerAccess") /* synthetic access */
static ArrayList<ViewGroup> sPendingTransitions = new ArrayList<>();
/**
* Sets a specific transition to occur when the given scene is entered.
*
* @param scene The scene which, when applied, will cause the given
* transition to run.
* @param transition The transition that will play when the given scene is
* entered. A value of null will result in the default behavior of
* using the default transition instead.
*/
public void setTransition(@NonNull Scene scene, @Nullable Transition transition) {
mSceneTransitions.put(scene, transition);
}
/**
* Sets a specific transition to occur when the given pair of scenes is
* exited/entered.
*
* @param fromScene The scene being exited when the given transition will
* be run
* @param toScene The scene being entered when the given transition will
* be run
* @param transition The transition that will play when the given scene is
* entered. A value of null will result in the default behavior of
* using the default transition instead.
*/
public void setTransition(@NonNull Scene fromScene, @NonNull Scene toScene,
@Nullable Transition transition) {
ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(toScene);
if (sceneTransitionMap == null) {
sceneTransitionMap = new ArrayMap<>();
mScenePairTransitions.put(toScene, sceneTransitionMap);
}
sceneTransitionMap.put(fromScene, transition);
}
/**
* Returns the Transition for the given scene being entered. The result
* depends not only on the given scene, but also the scene which the
* {@link Scene#getSceneRoot() sceneRoot} of the Scene is currently in.
*
* @param scene The scene being entered
* @return The Transition to be used for the given scene change. If no
* Transition was specified for this scene change, the default transition
* will be used instead.
*/
private Transition getTransition(Scene scene) {
Transition transition;
ViewGroup sceneRoot = scene.getSceneRoot();
// TODO: cached in Scene instead? long-term, cache in View itself
Scene currScene = Scene.getCurrentScene(sceneRoot);
if (currScene != null) {
ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions
.get(scene);
if (sceneTransitionMap != null) {
transition = sceneTransitionMap.get(currScene);
if (transition != null) {
return transition;
}
}
}
transition = mSceneTransitions.get(scene);
return (transition != null) ? transition : sDefaultTransition;
}
/**
* This is where all of the work of a transition/scene-change is
* orchestrated. This method captures the start values for the given
* transition, exits the current Scene, enters the new scene, captures
* the end values for the transition, and finally plays the
* resulting values-populated transition.
*
* @param scene The scene being entered
* @param transition The transition to play for this scene change
*/
private static void changeScene(Scene scene, Transition transition) {
final ViewGroup sceneRoot = scene.getSceneRoot();
if (!sPendingTransitions.contains(sceneRoot)) {
Scene oldScene = Scene.getCurrentScene(sceneRoot);
if (transition == null) {
// Notify old scene that it is being exited
if (oldScene != null) {
oldScene.exit();
}
scene.enter();
} else {
sPendingTransitions.add(sceneRoot);
Transition transitionClone = transition.clone();
if (oldScene != null && oldScene.isCreatedFromLayoutResource()) {
transitionClone.setCanRemoveViews(true);
}
sceneChangeSetup(sceneRoot, transitionClone);
scene.enter();
sceneChangeRunTransition(sceneRoot, transitionClone);
}
}
}
@VisibleForTesting
static ArrayMap<ViewGroup, ArrayList<Transition>> getRunningTransitions() {
WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>> runningTransitions =
sRunningTransitions.get();
if (runningTransitions != null) {
ArrayMap<ViewGroup, ArrayList<Transition>> transitions = runningTransitions.get();
if (transitions != null) {
return transitions;
}
}
ArrayMap<ViewGroup, ArrayList<Transition>> transitions = new ArrayMap<>();
runningTransitions = new WeakReference<>(transitions);
sRunningTransitions.set(runningTransitions);
return transitions;
}
private static void sceneChangeRunTransition(final ViewGroup sceneRoot,
final Transition transition) {
if (transition != null && sceneRoot != null) {
MultiListener listener = new MultiListener(transition, sceneRoot);
sceneRoot.addOnAttachStateChangeListener(listener);
sceneRoot.getViewTreeObserver().addOnPreDrawListener(listener);
}
}
/**
* This private utility class is used to listen for both OnPreDraw and
* OnAttachStateChange events. OnPreDraw events are the main ones we care
* about since that's what triggers the transition to take place.
* OnAttachStateChange events are also important in case the view is removed
* from the hierarchy before the OnPreDraw event takes place; it's used to
* clean up things since the OnPreDraw listener didn't get called in time.
*/
private static class MultiListener implements ViewTreeObserver.OnPreDrawListener,
View.OnAttachStateChangeListener {
Transition mTransition;
ViewGroup mSceneRoot;
MultiListener(Transition transition, ViewGroup sceneRoot) {
mTransition = transition;
mSceneRoot = sceneRoot;
}
private void removeListeners() {
mSceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
mSceneRoot.removeOnAttachStateChangeListener(this);
}
@Override
public void onViewAttachedToWindow(View v) {
}
@Override
public void onViewDetachedFromWindow(View v) {
removeListeners();
sPendingTransitions.remove(mSceneRoot);
ArrayList<Transition> runningTransitions = getRunningTransitions().get(mSceneRoot);
if (runningTransitions != null && runningTransitions.size() > 0) {
for (Transition runningTransition : runningTransitions) {
runningTransition.resume(mSceneRoot);
}
}
mTransition.clearValues(true);
}
@Override
public boolean onPreDraw() {
removeListeners();
// Don't start the transition if it's no longer pending.
if (!sPendingTransitions.remove(mSceneRoot)) {
return true;
}
// Add to running list, handle end to remove it
final ArrayMap<ViewGroup, ArrayList<Transition>> runningTransitions =
getRunningTransitions();
ArrayList<Transition> currentTransitions = runningTransitions.get(mSceneRoot);
ArrayList<Transition> previousRunningTransitions = null;
if (currentTransitions == null) {
currentTransitions = new ArrayList<>();
runningTransitions.put(mSceneRoot, currentTransitions);
} else if (currentTransitions.size() > 0) {
previousRunningTransitions = new ArrayList<>(currentTransitions);
}
currentTransitions.add(mTransition);
mTransition.addListener(new TransitionListenerAdapter() {
@Override
public void onTransitionEnd(@NonNull Transition transition) {
ArrayList<Transition> currentTransitions = runningTransitions.get(mSceneRoot);
currentTransitions.remove(transition);
transition.removeListener(this);
}
});
mTransition.captureValues(mSceneRoot, false);
if (previousRunningTransitions != null) {
for (Transition runningTransition : previousRunningTransitions) {
runningTransition.resume(mSceneRoot);
}
}
mTransition.playTransition(mSceneRoot);
return true;
}
}
private static void sceneChangeSetup(ViewGroup sceneRoot, Transition transition) {
// Capture current values
ArrayList<Transition> runningTransitions = getRunningTransitions().get(sceneRoot);
if (runningTransitions != null && runningTransitions.size() > 0) {
for (Transition runningTransition : runningTransitions) {
runningTransition.pause(sceneRoot);
}
}
if (transition != null) {
transition.captureValues(sceneRoot, true);
}
// Notify previous scene that it is being exited
Scene previousScene = Scene.getCurrentScene(sceneRoot);
if (previousScene != null) {
previousScene.exit();
}
}
/**
* Change to the given scene, using the
* appropriate transition for this particular scene change
* (as specified to the TransitionManager, or the default
* if no such transition exists).
*
* @param scene The Scene to change to
*/
public void transitionTo(@NonNull Scene scene) {
// Auto transition if there is no transition declared for the Scene, but there is
// a root or parent view
changeScene(scene, getTransition(scene));
}
/**
* Convenience method to simply change to the given scene using
* the default transition for TransitionManager.
*
* @param scene The Scene to change to
*/
public static void go(@NonNull Scene scene) {
changeScene(scene, sDefaultTransition);
}
/**
* Convenience method to seek to the given scene using the given transition. If seeking
* is not supported because the device is {@link Build.VERSION_CODES.TIRAMISU} or earlier,
* the scene transition is immediate and {@code null} is returned.
*
* @param scene The Scene to change to
* @param transition The transition to use for this scene change.
* @return a {@link TransitionSeekController} that can be used control the animation to the
* destination scene. {@code null} is returned when seeking is not supported on the scene,
* either because it is running on {@link android.os.Build.VERSION_CODES.TIRAMISU} or earlier,
* another Transition is being captured for {@code sceneRoot}, or {@code sceneRoot} hasn't
* had a layout yet.
* @throws IllegalArgumentException if {@code transition} returns {@code false} from
* {@link Transition#isSeekingSupported()}.
*/
@Nullable
public static TransitionSeekController createSeekController(
@NonNull Scene scene,
@NonNull Transition transition
) {
final ViewGroup sceneRoot = scene.getSceneRoot();
if (!transition.isSeekingSupported()) {
throw new IllegalArgumentException("The Transition must support seeking.");
}
if (sPendingTransitions.contains(sceneRoot)) {
return null; // Already in the process of transitioning
}
Scene oldScene = Scene.getCurrentScene(sceneRoot);
if (!sceneRoot.isLaidOut()
|| Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE
) {
// Can't control it, so just change the scene immediately
if (oldScene != null) {
oldScene.exit();
}
scene.enter();
return null;
}
sPendingTransitions.add(sceneRoot);
final Transition transitionClone = transition.clone();
final TransitionSet set = new TransitionSet();
set.addTransition(transitionClone);
if (oldScene != null && oldScene.isCreatedFromLayoutResource()) {
set.setCanRemoveViews(true);
}
sceneChangeSetup(sceneRoot, set);
scene.enter();
sceneChangeRunTransition(sceneRoot, set);
return set.createSeekController();
}
/**
* Convenience method to simply change to the given scene using
* the given transition.
*
* <p>Passing in <code>null</code> for the transition parameter will
* result in the scene changing without any transition running, and is
* equivalent to calling {@link Scene#exit()} on the scene root's
* current scene, followed by {@link Scene#enter()} on the scene
* specified by the <code>scene</code> parameter.</p>
*
* @param scene The Scene to change to
* @param transition The transition to use for this scene change. A
* value of null causes the scene change to happen with no transition.
*/
public static void go(@NonNull Scene scene, @Nullable Transition transition) {
changeScene(scene, transition);
}
/**
* Convenience method to animate, using the default transition,
* to a new scene defined by all changes within the given scene root between
* calling this method and the next rendering frame.
* Equivalent to calling {@link #beginDelayedTransition(ViewGroup, Transition)}
* with a value of <code>null</code> for the <code>transition</code> parameter.
*
* @param sceneRoot The root of the View hierarchy to run the transition on.
*/
public static void beginDelayedTransition(@NonNull final ViewGroup sceneRoot) {
beginDelayedTransition(sceneRoot, null);
}
/**
* Convenience method to animate to a new scene defined by all changes within
* the given scene root between calling this method and the next rendering frame.
* Calling this method causes TransitionManager to capture current values in the
* scene root and then post a request to run a transition on the next frame.
* At that time, the new values in the scene root will be captured and changes
* will be animated. There is no need to create a Scene; it is implied by
* changes which take place between calling this method and the next frame when
* the transition begins.
*
* <p>Calling this method several times before the next frame (for example, if
* unrelated code also wants to make dynamic changes and run a transition on
* the same scene root), only the first call will trigger capturing values
* and exiting the current scene. Subsequent calls to the method with the
* same scene root during the same frame will be ignored.</p>
*
* <p>Passing in <code>null</code> for the transition parameter will
* cause the TransitionManager to use its default transition.</p>
*
* @param sceneRoot The root of the View hierarchy to run the transition on.
* @param transition The transition to use for this change. A
* value of null causes the TransitionManager to use the default transition.
*/
public static void beginDelayedTransition(@NonNull final ViewGroup sceneRoot,
@Nullable Transition transition) {
if (!sPendingTransitions.contains(sceneRoot) && sceneRoot.isLaidOut()) {
if (Transition.DBG) {
Log.d(LOG_TAG, "beginDelayedTransition: root, transition = "
+ sceneRoot + ", " + transition);
}
sPendingTransitions.add(sceneRoot);
if (transition == null) {
transition = sDefaultTransition;
}
final Transition transitionClone = transition.clone();
sceneChangeSetup(sceneRoot, transitionClone);
Scene.setCurrentScene(sceneRoot, null);
sceneChangeRunTransition(sceneRoot, transitionClone);
}
}
/**
* Create a {@link TransitionSeekController} to allow seeking an animation to a new
* scene defined by all changes within the given scene root between calling this method and
* the next rendered frame. Calling this method causes TransitionManager to capture current
* values in the scene root and then post a request to run a transition on the next frame.
* At that time, the new values in the scene root will be captured and changes
* will be animated. There is no need to create a Scene; it is implied by
* changes which take place between calling this method and the next frame when
* the transition begins.
*
* <p>Calling this method several times before the next frame (for example, if
* unrelated code also wants to make dynamic changes and run a transition on
* the same scene root), only the first call will trigger capturing values
* and exiting the current scene. Subsequent calls to the method with the
* same scene root during the same frame will be ignored.</p>
*
* @param sceneRoot The root of the View hierarchy to run the transition on.
* @param transition The transition to use for this change.
* @return a {@link TransitionSeekController} that can be used control the animation to the
* destination scene. {@code null} is returned when seeking is not supported on the scene,
* either because it is running on {@link android.os.Build.VERSION_CODES#TIRAMISU} or earlier,
* another Transition is being captured for {@code sceneRoot}, or {@code sceneRoot} hasn't
* had a layout yet.
* @throws IllegalArgumentException if {@code transition} returns {@code false} from
* {@link Transition#isSeekingSupported()}.
*/
@Nullable
public static TransitionSeekController controlDelayedTransition(
@NonNull final ViewGroup sceneRoot,
@NonNull Transition transition
) {
if (sPendingTransitions.contains(sceneRoot) || !sceneRoot.isLaidOut()
|| Build.VERSION.SDK_INT < 34) {
return null;
}
if (!transition.isSeekingSupported()) {
throw new IllegalArgumentException("The Transition must support seeking.");
}
if (Transition.DBG) {
Log.d(LOG_TAG, "controlDelayedTransition: root, transition = "
+ sceneRoot + ", " + transition);
}
sPendingTransitions.add(sceneRoot);
final Transition transitionClone = transition.clone();
final TransitionSet set = new TransitionSet();
set.addTransition(transitionClone);
sceneChangeSetup(sceneRoot, set);
Scene.setCurrentScene(sceneRoot, null);
sceneChangeRunTransition(sceneRoot, set);
sceneRoot.invalidate();
return set.createSeekController();
}
/**
* Ends all pending and ongoing transitions on the specified scene root.
*
* @param sceneRoot The root of the View hierarchy to end transitions on.
*/
public static void endTransitions(final @Nullable ViewGroup sceneRoot) {
sPendingTransitions.remove(sceneRoot);
final ArrayList<Transition> runningTransitions = getRunningTransitions().get(sceneRoot);
if (runningTransitions != null && !runningTransitions.isEmpty()) {
// Make a copy in case this is called by an onTransitionEnd listener
ArrayList<Transition> copy = new ArrayList<>(runningTransitions);
for (int i = copy.size() - 1; i >= 0; i--) {
final Transition transition = copy.get(i);
transition.forceToEnd(sceneRoot);
}
}
}
}