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