TransitionInflater.java revision cfbe9be5b3b701d95fb24fa0f7c8d9be43eec776
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 17package android.transition; 18 19import android.content.Context; 20import android.content.res.Resources; 21import android.content.res.TypedArray; 22import android.content.res.XmlResourceParser; 23import android.text.TextUtils; 24import android.util.AttributeSet; 25import android.util.Xml; 26import android.view.InflateException; 27import android.view.ViewGroup; 28import android.view.animation.AnimationUtils; 29import org.xmlpull.v1.XmlPullParser; 30import org.xmlpull.v1.XmlPullParserException; 31 32import java.io.IOException; 33import java.util.ArrayList; 34 35/** 36 * This class inflates scenes and transitions from resource files. 37 * 38 * Information on XML resource descriptions for transitions can be found for 39 * {@link android.R.styleable#Transition}, {@link android.R.styleable#TransitionSet}, 40 * {@link android.R.styleable#TransitionTarget}, {@link android.R.styleable#Fade}, 41 * and {@link android.R.styleable#TransitionManager}. 42 */ 43public class TransitionInflater { 44 45 private Context mContext; 46 47 private TransitionInflater(Context context) { 48 mContext = context; 49 } 50 51 /** 52 * Obtains the TransitionInflater from the given context. 53 */ 54 public static TransitionInflater from(Context context) { 55 return new TransitionInflater(context); 56 } 57 58 /** 59 * Loads a {@link Transition} object from a resource 60 * 61 * @param resource The resource id of the transition to load 62 * @return The loaded Transition object 63 * @throws android.content.res.Resources.NotFoundException when the 64 * transition cannot be loaded 65 */ 66 public Transition inflateTransition(int resource) { 67 XmlResourceParser parser = mContext.getResources().getXml(resource); 68 try { 69 return createTransitionFromXml(parser, Xml.asAttributeSet(parser), null); 70 } catch (XmlPullParserException e) { 71 InflateException ex = new InflateException(e.getMessage()); 72 ex.initCause(e); 73 throw ex; 74 } catch (IOException e) { 75 InflateException ex = new InflateException( 76 parser.getPositionDescription() 77 + ": " + e.getMessage()); 78 ex.initCause(e); 79 throw ex; 80 } finally { 81 parser.close(); 82 } 83 } 84 85 /** 86 * Loads a {@link TransitionManager} object from a resource 87 * 88 * 89 * 90 * @param resource The resource id of the transition manager to load 91 * @return The loaded TransitionManager object 92 * @throws android.content.res.Resources.NotFoundException when the 93 * transition manager cannot be loaded 94 */ 95 public TransitionManager inflateTransitionManager(int resource, ViewGroup sceneRoot) { 96 XmlResourceParser parser = mContext.getResources().getXml(resource); 97 try { 98 return createTransitionManagerFromXml(parser, Xml.asAttributeSet(parser), sceneRoot); 99 } catch (XmlPullParserException e) { 100 InflateException ex = new InflateException(e.getMessage()); 101 ex.initCause(e); 102 throw ex; 103 } catch (IOException e) { 104 InflateException ex = new InflateException( 105 parser.getPositionDescription() 106 + ": " + e.getMessage()); 107 ex.initCause(e); 108 throw ex; 109 } finally { 110 parser.close(); 111 } 112 } 113 114 // 115 // Transition loading 116 // 117 118 private Transition createTransitionFromXml(XmlPullParser parser, 119 AttributeSet attrs, TransitionSet transitionSet) 120 throws XmlPullParserException, IOException { 121 122 Transition transition = null; 123 124 // Make sure we are on a start tag. 125 int type; 126 int depth = parser.getDepth(); 127 128 while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 129 && type != XmlPullParser.END_DOCUMENT) { 130 131 boolean newTransition = false; 132 133 if (type != XmlPullParser.START_TAG) { 134 continue; 135 } 136 137 String name = parser.getName(); 138 if ("fade".equals(name)) { 139 TypedArray a = mContext.obtainStyledAttributes(attrs, 140 com.android.internal.R.styleable.Fade); 141 int fadingMode = a.getInt(com.android.internal.R.styleable.Fade_fadingMode, 142 Fade.IN | Fade.OUT); 143 transition = new Fade(fadingMode); 144 newTransition = true; 145 } else if ("changeBounds".equals(name)) { 146 transition = new ChangeBounds(); 147 newTransition = true; 148 } else if ("slide".equals(name)) { 149 transition = new Slide(); 150 newTransition = true; 151 } else if ("autoTransition".equals(name)) { 152 transition = new AutoTransition(); 153 newTransition = true; 154 } else if ("recolor".equals(name)) { 155 transition = new Recolor(); 156 newTransition = true; 157 } else if ("transitionSet".equals(name)) { 158 transition = new TransitionSet(); 159 TypedArray a = mContext.obtainStyledAttributes(attrs, 160 com.android.internal.R.styleable.TransitionSet); 161 int ordering = a.getInt( 162 com.android.internal.R.styleable.TransitionSet_transitionOrdering, 163 TransitionSet.ORDERING_TOGETHER); 164 ((TransitionSet) transition).setOrdering(ordering); 165 createTransitionFromXml(parser, attrs, ((TransitionSet) transition)); 166 a.recycle(); 167 newTransition = true; 168 } else if ("targets".equals(name)) { 169 if (parser.getDepth() - 1 > depth && transition != null) { 170 // We're inside the child tag - add targets to the child 171 getTargetIds(parser, attrs, transition); 172 } else if (parser.getDepth() - 1 == depth && transitionSet != null) { 173 // add targets to the set 174 getTargetIds(parser, attrs, transitionSet); 175 } 176 } 177 if (transition != null || "targets".equals(name)) { 178 if (newTransition) { 179 loadTransition(transition, attrs); 180 if (transitionSet != null) { 181 transitionSet.addTransition(transition); 182 } 183 } 184 } else { 185 throw new RuntimeException("Unknown scene name: " + parser.getName()); 186 } 187 } 188 189 return transition; 190 } 191 192 private void getTargetIds(XmlPullParser parser, 193 AttributeSet attrs, Transition transition) throws XmlPullParserException, IOException { 194 195 // Make sure we are on a start tag. 196 int type; 197 int depth = parser.getDepth(); 198 199 ArrayList<Integer> targetIds = new ArrayList<Integer>(); 200 while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 201 && type != XmlPullParser.END_DOCUMENT) { 202 203 if (type != XmlPullParser.START_TAG) { 204 continue; 205 } 206 207 String name = parser.getName(); 208 if (name.equals("target")) { 209 TypedArray a = mContext.obtainStyledAttributes(attrs, 210 com.android.internal.R.styleable.TransitionTarget); 211 int id = a.getResourceId( 212 com.android.internal.R.styleable.TransitionTarget_targetId, -1); 213 if (id >= 0) { 214 targetIds.add(id); 215 } 216 } else { 217 throw new RuntimeException("Unknown scene name: " + parser.getName()); 218 } 219 } 220 int numTargets = targetIds.size(); 221 if (numTargets > 0) { 222 for (int i = 0; i < numTargets; ++i) { 223 transition.addTarget(targetIds.get(i)); 224 } 225 } 226 } 227 228 private Transition loadTransition(Transition transition, AttributeSet attrs) 229 throws Resources.NotFoundException { 230 231 TypedArray a = 232 mContext.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Transition); 233 long duration = a.getInt(com.android.internal.R.styleable.Transition_duration, -1); 234 if (duration >= 0) { 235 transition.setDuration(duration); 236 } 237 long startDelay = a.getInt(com.android.internal.R.styleable.Transition_startDelay, -1); 238 if (startDelay > 0) { 239 transition.setStartDelay(startDelay); 240 } 241 final int resID = 242 a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0); 243 if (resID > 0) { 244 transition.setInterpolator(AnimationUtils.loadInterpolator(mContext, resID)); 245 } 246 a.recycle(); 247 return transition; 248 } 249 250 // 251 // TransitionManager loading 252 // 253 254 private TransitionManager createTransitionManagerFromXml(XmlPullParser parser, 255 AttributeSet attrs, ViewGroup sceneRoot) throws XmlPullParserException, IOException { 256 257 // Make sure we are on a start tag. 258 int type; 259 int depth = parser.getDepth(); 260 TransitionManager transitionManager = null; 261 262 while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 263 && type != XmlPullParser.END_DOCUMENT) { 264 265 if (type != XmlPullParser.START_TAG) { 266 continue; 267 } 268 269 String name = parser.getName(); 270 if (name.equals("transitionManager")) { 271 transitionManager = new TransitionManager(); 272 } else if (name.equals("transition") && (transitionManager != null)) { 273 loadTransition(attrs, sceneRoot, transitionManager); 274 } else { 275 throw new RuntimeException("Unknown scene name: " + parser.getName()); 276 } 277 } 278 return transitionManager; 279 } 280 281 private void loadTransition(AttributeSet attrs, ViewGroup sceneRoot, 282 TransitionManager transitionManager) throws Resources.NotFoundException { 283 284 TypedArray a = mContext.obtainStyledAttributes(attrs, 285 com.android.internal.R.styleable.TransitionManager); 286 int transitionId = a.getResourceId( 287 com.android.internal.R.styleable.TransitionManager_transition, -1); 288 Scene fromScene = null, toScene = null; 289 int fromId = a.getResourceId( 290 com.android.internal.R.styleable.TransitionManager_fromScene, -1); 291 if (fromId >= 0) fromScene = Scene.getSceneForLayout(sceneRoot, fromId, mContext); 292 int toId = a.getResourceId( 293 com.android.internal.R.styleable.TransitionManager_toScene, -1); 294 if (toId >= 0) toScene = Scene.getSceneForLayout(sceneRoot, toId, mContext); 295 String fromName = a.getString( 296 com.android.internal.R.styleable.TransitionManager_fromSceneName); 297 String toName = a.getString( 298 com.android.internal.R.styleable.TransitionManager_toSceneName); 299 if (transitionId >= 0) { 300 Transition transition = inflateTransition(transitionId); 301 if (transition != null) { 302 if (fromScene != null) { 303 boolean hasDest = false; 304 if (toScene != null) { 305 transitionManager.setTransition(fromScene, toScene, transition); 306 hasDest = true; 307 } 308 309 if (!TextUtils.isEmpty(toName)) { 310 transitionManager.setTransition(fromScene, toName, transition); 311 hasDest = true; 312 } 313 314 if (!hasDest) { 315 throw new RuntimeException("No matching toScene or toSceneName for given " + 316 "fromScene for transition ID " + transitionId); 317 } 318 } else if (toId >= 0) { 319 transitionManager.setTransition(toScene, transition); 320 } 321 if (fromName != null) { 322 if (toScene != null) { 323 transitionManager.setTransition(fromName, toScene, transition); 324 } else { 325 throw new RuntimeException("No matching toScene for given fromSceneName " + 326 "for transition ID " + transitionId); 327 } 328 } 329 } 330 } 331 a.recycle(); 332 } 333} 334