diff options
author | Chet Haase <chet@google.com> | 2013-08-26 14:20:16 -0700 |
---|---|---|
committer | Chet Haase <chet@google.com> | 2013-09-04 14:46:38 -0700 |
commit | d82c8ac4db7091d2e976af4c89a1734465d20cd2 (patch) | |
tree | a3ccb4d7a4325582dfe028030e4ca2d38ba4c2be /core/java/android/transition/TransitionManager.java | |
parent | 2a59f3f24076d96cbb10e1c00c3264dec43f19b1 (diff) | |
download | frameworks_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.java | 360 |
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); + } + } +} |