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