summaryrefslogtreecommitdiffstats
path: root/core/java/android/transition/TransitionManager.java
diff options
context:
space:
mode:
authorChet Haase <chet@google.com>2013-08-26 14:20:16 -0700
committerChet Haase <chet@google.com>2013-09-04 14:46:38 -0700
commitd82c8ac4db7091d2e976af4c89a1734465d20cd2 (patch)
treea3ccb4d7a4325582dfe028030e4ca2d38ba4c2be /core/java/android/transition/TransitionManager.java
parent2a59f3f24076d96cbb10e1c00c3264dec43f19b1 (diff)
downloadframeworks_base-d82c8ac4db7091d2e976af4c89a1734465d20cd2.zip
frameworks_base-d82c8ac4db7091d2e976af4c89a1734465d20cd2.tar.gz
frameworks_base-d82c8ac4db7091d2e976af4c89a1734465d20cd2.tar.bz2
Transition API changes from API council recommendations
Issue #10460684 KLP API Review: android.view.transition and android.animation Issue #10570740 Transitions: inflate transition targets from xml Change-Id: I7a3f6d3aece2fcafc5efd555d033f79e86635c98
Diffstat (limited to 'core/java/android/transition/TransitionManager.java')
-rw-r--r--core/java/android/transition/TransitionManager.java360
1 files changed, 360 insertions, 0 deletions
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
new file mode 100644
index 0000000..9904413
--- /dev/null
+++ b/core/java/android/transition/TransitionManager.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.transition;
+
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+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>
+ *
+ * {@sample development/samples/ApiDemos/res/transition/transitions_mgr.xml TransitionManager}
+ *
+ * <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>
+ *
+ * Information on XML resource descriptions for transitions can be found for
+ * {@link android.R.styleable#Transition}, {@link android.R.styleable#TransitionSet},
+ * {@link android.R.styleable#TransitionTarget}, {@link android.R.styleable#Fade},
+ * and {@link android.R.styleable#TransitionManager}.
+ */
+public class TransitionManager {
+ // TODO: how to handle enter/exit?
+
+ private static String LOG_TAG = "TransitionManager";
+
+ private static Transition sDefaultTransition = new AutoTransition();
+
+ ArrayMap<Scene, Transition> mSceneTransitions = new ArrayMap<Scene, Transition>();
+ ArrayMap<Scene, ArrayMap<Scene, Transition>> mScenePairTransitions =
+ new ArrayMap<Scene, ArrayMap<Scene, Transition>>();
+ private static ThreadLocal<ArrayMap<ViewGroup, ArrayList<Transition>>> sRunningTransitions =
+ new ThreadLocal<ArrayMap<ViewGroup, ArrayList<Transition>>>();
+ private static ArrayList<ViewGroup> sPendingTransitions = new ArrayList<ViewGroup>();
+
+
+ /**
+ * Sets the transition to be used for any scene change for which no
+ * other transition is explicitly set. The initial value is
+ * an {@link AutoTransition} instance.
+ *
+ * @param transition The default transition to be used for scene changes.
+ */
+ public void setDefaultTransition(Transition transition) {
+ sDefaultTransition = transition;
+ }
+
+ /**
+ * Gets the current default transition. The initial value is an {@link
+ * AutoTransition} instance.
+ *
+ * @return The current default transition.
+ * @see #setDefaultTransition(Transition)
+ */
+ public static Transition getDefaultTransition() {
+ return sDefaultTransition;
+ }
+
+ /**
+ * 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 {@link #getDefaultTransition() default transition} instead.
+ */
+ public void setTransition(Scene scene, 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 {@link #getDefaultTransition() default transition} instead.
+ */
+ public void setTransition(Scene fromScene, Scene toScene, Transition transition) {
+ ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(toScene);
+ if (sceneTransitionMap == null) {
+ sceneTransitionMap = new ArrayMap<Scene, Transition>();
+ 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 {@link #getDefaultTransition()
+ * default transition} will be used instead.
+ */
+ private Transition getTransition(Scene scene) {
+ Transition transition = null;
+ ViewGroup sceneRoot = scene.getSceneRoot();
+ if (sceneRoot != null) {
+ // 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();
+
+ Transition transitionClone = transition.clone();
+ transitionClone.setSceneRoot(sceneRoot);
+
+ sceneChangeSetup(sceneRoot, transitionClone);
+
+ scene.enter();
+
+ sceneChangeRunTransition(sceneRoot, transitionClone);
+ }
+
+ private static ArrayMap<ViewGroup, ArrayList<Transition>> getRunningTransitions() {
+ ArrayMap<ViewGroup, ArrayList<Transition>> runningTransitions =
+ sRunningTransitions.get();
+ if (runningTransitions == null) {
+ runningTransitions = new ArrayMap<ViewGroup, ArrayList<Transition>>();
+ sRunningTransitions.set(runningTransitions);
+ }
+ return runningTransitions;
+ }
+
+ private static void sceneChangeRunTransition(final ViewGroup sceneRoot,
+ final Transition transition) {
+ if (transition != null) {
+ final ViewTreeObserver observer = sceneRoot.getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ public boolean onPreDraw() {
+ sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ sPendingTransitions.remove(sceneRoot);
+ // Add to running list, handle end to remove it
+ final ArrayMap<ViewGroup, ArrayList<Transition>> runningTransitions =
+ getRunningTransitions();
+ ArrayList<Transition> currentTransitions = runningTransitions.get(sceneRoot);
+ ArrayList<Transition> previousRunningTransitions = null;
+ if (currentTransitions == null) {
+ currentTransitions = new ArrayList<Transition>();
+ runningTransitions.put(sceneRoot, currentTransitions);
+ } else if (currentTransitions.size() > 0) {
+ previousRunningTransitions = new ArrayList<Transition>(currentTransitions);
+ }
+ currentTransitions.add(transition);
+ transition.addListener(new Transition.TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ ArrayList<Transition> currentTransitions =
+ runningTransitions.get(sceneRoot);
+ currentTransitions.remove(transition);
+ }
+ });
+ transition.captureValues(sceneRoot, false);
+ if (previousRunningTransitions != null) {
+ for (Transition runningTransition : previousRunningTransitions) {
+ runningTransition.resume();
+ }
+ }
+ transition.playTransition(sceneRoot);
+
+ // Returning false from onPreDraw() skips the current frame. This is
+ // necessary to avoid artifacts caused by resetting target views
+ // to their proper end states for capturing. Waiting until the next
+ // frame to draw allows these views to have their mid-transition
+ // values set on them again and avoid artifacts.
+ return false;
+ }
+ });
+ }
+ }
+
+ 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();
+ }
+ }
+
+ 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(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(Scene scene) {
+ changeScene(scene, sDefaultTransition);
+ }
+
+ /**
+ * 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(Scene scene, 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(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(final ViewGroup sceneRoot, 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 finalTransition = transition.clone();
+ sceneChangeSetup(sceneRoot, transition);
+ Scene.setCurrentScene(sceneRoot, null);
+ sceneChangeRunTransition(sceneRoot, finalTransition);
+ }
+ }
+}