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