1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.transition; 18 19 import android.content.Context; 20 import android.util.SparseArray; 21 import android.view.LayoutInflater; 22 import android.view.View; 23 import android.view.ViewGroup; 24 25 /** 26 * A scene represents the collection of values that various properties in the 27 * View hierarchy will have when the scene is applied. A Scene can be 28 * configured to automatically run a Transition when it is applied, which will 29 * animate the various property changes that take place during the 30 * scene change. 31 */ 32 public final class Scene { 33 34 private Context mContext; 35 private int mLayoutId = -1; 36 private ViewGroup mSceneRoot; 37 private View mLayout; // alternative to layoutId 38 Runnable mEnterAction, mExitAction; 39 40 /** 41 * Returns a Scene described by the resource file associated with the given 42 * <code>layoutId</code> parameter. If such a Scene has already been created for 43 * the given <code>sceneRoot</code>, that same Scene will be returned. 44 * This caching of layoutId-based scenes enables sharing of common scenes 45 * between those created in code and those referenced by {@link TransitionManager} 46 * XML resource files. 47 * 48 * @param sceneRoot The root of the hierarchy in which scene changes 49 * and transitions will take place. 50 * @param layoutId The id of a standard layout resource file. 51 * @param context The context used in the process of inflating 52 * the layout resource. 53 * @return The scene for the given root and layout id 54 */ 55 public static Scene getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context) { 56 SparseArray<Scene> scenes = (SparseArray<Scene>) sceneRoot.getTag( 57 com.android.internal.R.id.scene_layoutid_cache); 58 if (scenes == null) { 59 scenes = new SparseArray<Scene>(); 60 sceneRoot.setTagInternal(com.android.internal.R.id.scene_layoutid_cache, scenes); 61 } 62 Scene scene = scenes.get(layoutId); 63 if (scene != null) { 64 return scene; 65 } else { 66 scene = new Scene(sceneRoot, layoutId, context); 67 scenes.put(layoutId, scene); 68 return scene; 69 } 70 } 71 72 /** 73 * Constructs a Scene with no information about how values will change 74 * when this scene is applied. This constructor might be used when 75 * a Scene is created with the intention of being dynamically configured, 76 * through setting {@link #setEnterAction(Runnable)} and possibly 77 * {@link #setExitAction(Runnable)}. 78 * 79 * @param sceneRoot The root of the hierarchy in which scene changes 80 * and transitions will take place. 81 */ 82 public Scene(ViewGroup sceneRoot) { 83 mSceneRoot = sceneRoot; 84 } 85 86 /** 87 * Constructs a Scene which, when entered, will remove any 88 * children from the sceneRoot container and will inflate and add 89 * the hierarchy specified by the layoutId resource file. 90 * 91 * <p>This method is hidden because layoutId-based scenes should be 92 * created by the caching factory method {@link Scene#getCurrentScene(View)}.</p> 93 * 94 * @param sceneRoot The root of the hierarchy in which scene changes 95 * and transitions will take place. 96 * @param layoutId The id of a resource file that defines the view 97 * hierarchy of this scene. 98 * @param context The context used in the process of inflating 99 * the layout resource. 100 */ 101 private Scene(ViewGroup sceneRoot, int layoutId, Context context) { 102 mContext = context; 103 mSceneRoot = sceneRoot; 104 mLayoutId = layoutId; 105 } 106 107 /** 108 * Constructs a Scene which, when entered, will remove any 109 * children from the sceneRoot container and add the layout 110 * object as a new child of that container. 111 * 112 * @param sceneRoot The root of the hierarchy in which scene changes 113 * and transitions will take place. 114 * @param layout The view hierarchy of this scene, added as a child 115 * of sceneRoot when this scene is entered. 116 */ 117 public Scene(ViewGroup sceneRoot, View layout) { 118 mSceneRoot = sceneRoot; 119 mLayout = layout; 120 } 121 122 /** 123 * @deprecated use {@link #Scene(ViewGroup, View)}. 124 */ 125 @Deprecated 126 public Scene(ViewGroup sceneRoot, ViewGroup layout) { 127 mSceneRoot = sceneRoot; 128 mLayout = layout; 129 } 130 131 /** 132 * Gets the root of the scene, which is the root of the view hierarchy 133 * affected by changes due to this scene, and which will be animated 134 * when this scene is entered. 135 * 136 * @return The root of the view hierarchy affected by this scene. 137 */ 138 public ViewGroup getSceneRoot() { 139 return mSceneRoot; 140 } 141 142 /** 143 * Exits this scene, if it is the current scene 144 * on the scene's {@link #getSceneRoot() scene root}. The current scene is 145 * set when {@link #enter() entering} a scene. 146 * Exiting a scene runs the {@link #setExitAction(Runnable) exit action} 147 * if there is one. 148 */ 149 public void exit() { 150 if (getCurrentScene(mSceneRoot) == this) { 151 if (mExitAction != null) { 152 mExitAction.run(); 153 } 154 } 155 } 156 157 /** 158 * Enters this scene, which entails changing all values that 159 * are specified by this scene. These may be values associated 160 * with a layout view group or layout resource file which will 161 * now be added to the scene root, or it may be values changed by 162 * an {@link #setEnterAction(Runnable)} enter action}, or a 163 * combination of the these. No transition will be run when the 164 * scene is entered. To get transition behavior in scene changes, 165 * use one of the methods in {@link TransitionManager} instead. 166 */ 167 public void enter() { 168 169 // Apply layout change, if any 170 if (mLayoutId > 0 || mLayout != null) { 171 // empty out parent container before adding to it 172 getSceneRoot().removeAllViews(); 173 174 if (mLayoutId > 0) { 175 LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot); 176 } else { 177 mSceneRoot.addView(mLayout); 178 } 179 } 180 181 // Notify next scene that it is entering. Subclasses may override to configure scene. 182 if (mEnterAction != null) { 183 mEnterAction.run(); 184 } 185 186 setCurrentScene(mSceneRoot, this); 187 } 188 189 /** 190 * Set the scene that the given view is in. The current scene is set only 191 * on the root view of a scene, not for every view in that hierarchy. This 192 * information is used by Scene to determine whether there is a previous 193 * scene which should be exited before the new scene is entered. 194 * 195 * @param view The view on which the current scene is being set 196 */ 197 static void setCurrentScene(View view, Scene scene) { 198 view.setTagInternal(com.android.internal.R.id.current_scene, scene); 199 } 200 201 /** 202 * Gets the current {@link Scene} set on the given view. A scene is set on a view 203 * only if that view is the scene root. 204 * 205 * @return The current Scene set on this view. A value of null indicates that 206 * no Scene is currently set. 207 */ 208 static Scene getCurrentScene(View view) { 209 return (Scene) view.getTag(com.android.internal.R.id.current_scene); 210 } 211 212 /** 213 * Scenes that are not defined with layout resources or 214 * hierarchies, or which need to perform additional steps 215 * after those hierarchies are changed to, should set an enter 216 * action, and possibly an exit action as well. An enter action 217 * will cause Scene to call back into application code to do 218 * anything else the application needs after transitions have 219 * captured pre-change values and after any other scene changes 220 * have been applied, such as the layout (if any) being added to 221 * the view hierarchy. After this method is called, Transitions will 222 * be played. 223 * 224 * @param action The runnable whose {@link Runnable#run() run()} method will 225 * be called when this scene is entered 226 * @see #setExitAction(Runnable) 227 * @see Scene#Scene(ViewGroup, int, Context) 228 * @see Scene#Scene(ViewGroup, ViewGroup) 229 */ 230 public void setEnterAction(Runnable action) { 231 mEnterAction = action; 232 } 233 234 /** 235 * Scenes that are not defined with layout resources or 236 * hierarchies, or which need to perform additional steps 237 * after those hierarchies are changed to, should set an enter 238 * action, and possibly an exit action as well. An exit action 239 * will cause Scene to call back into application code to do 240 * anything the application needs to do after applicable transitions have 241 * captured pre-change values, but before any other scene changes 242 * have been applied, such as the new layout (if any) being added to 243 * the view hierarchy. After this method is called, the next scene 244 * will be entered, including a call to {@link #setEnterAction(Runnable)} 245 * if an enter action is set. 246 * 247 * @see #setEnterAction(Runnable) 248 * @see Scene#Scene(ViewGroup, int, Context) 249 * @see Scene#Scene(ViewGroup, ViewGroup) 250 */ 251 public void setExitAction(Runnable action) { 252 mExitAction = action; 253 } 254 255 256 /** 257 * Returns whether this Scene was created by a layout resource file, determined 258 * by the layoutId passed into 259 * {@link #getSceneForLayout(android.view.ViewGroup, int, android.content.Context)}. 260 * This is called by TransitionManager to determine whether it is safe for views from 261 * this scene to be removed from their parents when the scene is exited, which is 262 * used by {@link Fade} to fade these views out (the views must be removed from 263 * their parent in order to add them to the overlay for fading purposes). If a 264 * Scene is not based on a resource file, then the impact of removing views 265 * arbitrarily is unknown and should be avoided. 266 */ 267 boolean isCreatedFromLayoutResource() { 268 return (mLayoutId > 0); 269 } 270 }