TransitionInflater.java revision d6107a3170df61d9e776fcd5666acfc9135c6f16
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 ("autoTransition".equals(name)) { 157 transition = new AutoTransition(); 158 newTransition = true; 159 } else if ("recolor".equals(name)) { 160 transition = new Recolor(); 161 newTransition = true; 162 } else if ("transitionSet".equals(name)) { 163 transition = new TransitionSet(); 164 TypedArray a = mContext.obtainStyledAttributes(attrs, 165 com.android.internal.R.styleable.TransitionSet); 166 int ordering = a.getInt( 167 com.android.internal.R.styleable.TransitionSet_transitionOrdering, 168 TransitionSet.ORDERING_TOGETHER); 169 ((TransitionSet) transition).setOrdering(ordering); 170 createTransitionFromXml(parser, attrs, ((TransitionSet) transition)); 171 a.recycle(); 172 newTransition = true; 173 } else if ("targets".equals(name)) { 174 if (parser.getDepth() - 1 > depth && transition != null) { 175 // We're inside the child tag - add targets to the child 176 getTargetIds(parser, attrs, transition); 177 } else if (parser.getDepth() - 1 == depth && transitionSet != null) { 178 // add targets to the set 179 getTargetIds(parser, attrs, transitionSet); 180 } 181 } 182 if (transition != null || "targets".equals(name)) { 183 if (newTransition) { 184 loadTransition(transition, attrs); 185 if (transitionSet != null) { 186 transitionSet.addTransition(transition); 187 } 188 } 189 } else { 190 throw new RuntimeException("Unknown scene name: " + parser.getName()); 191 } 192 } 193 194 return transition; 195 } 196 197 private Slide createSlideTransition(AttributeSet attrs) { 198 TypedArray a = mContext.obtainStyledAttributes(attrs, 199 com.android.internal.R.styleable.Slide); 200 int edge = a.getInt(com.android.internal.R.styleable.Slide_slideEdge, Slide.BOTTOM); 201 Slide slide = new Slide(edge); 202 a.recycle(); 203 return slide; 204 } 205 206 private void getTargetIds(XmlPullParser parser, 207 AttributeSet attrs, Transition transition) throws XmlPullParserException, IOException { 208 209 // Make sure we are on a start tag. 210 int type; 211 int depth = parser.getDepth(); 212 213 ArrayList<Integer> targetIds = new ArrayList<Integer>(); 214 while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 215 && type != XmlPullParser.END_DOCUMENT) { 216 217 if (type != XmlPullParser.START_TAG) { 218 continue; 219 } 220 221 String name = parser.getName(); 222 if (name.equals("target")) { 223 TypedArray a = mContext.obtainStyledAttributes(attrs, 224 com.android.internal.R.styleable.TransitionTarget); 225 int id = a.getResourceId( 226 com.android.internal.R.styleable.TransitionTarget_targetId, -1); 227 if (id >= 0) { 228 targetIds.add(id); 229 } 230 } else { 231 throw new RuntimeException("Unknown scene name: " + parser.getName()); 232 } 233 } 234 int numTargets = targetIds.size(); 235 if (numTargets > 0) { 236 for (int i = 0; i < numTargets; ++i) { 237 transition.addTarget(targetIds.get(i)); 238 } 239 } 240 } 241 242 private Transition loadTransition(Transition transition, AttributeSet attrs) 243 throws Resources.NotFoundException { 244 245 TypedArray a = 246 mContext.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Transition); 247 long duration = a.getInt(com.android.internal.R.styleable.Transition_duration, -1); 248 if (duration >= 0) { 249 transition.setDuration(duration); 250 } 251 long startDelay = a.getInt(com.android.internal.R.styleable.Transition_startDelay, -1); 252 if (startDelay > 0) { 253 transition.setStartDelay(startDelay); 254 } 255 final int resID = 256 a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0); 257 if (resID > 0) { 258 transition.setInterpolator(AnimationUtils.loadInterpolator(mContext, resID)); 259 } 260 a.recycle(); 261 return transition; 262 } 263 264 // 265 // TransitionManager loading 266 // 267 268 private TransitionManager createTransitionManagerFromXml(XmlPullParser parser, 269 AttributeSet attrs, ViewGroup sceneRoot) throws XmlPullParserException, IOException { 270 271 // Make sure we are on a start tag. 272 int type; 273 int depth = parser.getDepth(); 274 TransitionManager transitionManager = null; 275 276 while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 277 && type != XmlPullParser.END_DOCUMENT) { 278 279 if (type != XmlPullParser.START_TAG) { 280 continue; 281 } 282 283 String name = parser.getName(); 284 if (name.equals("transitionManager")) { 285 transitionManager = new TransitionManager(); 286 } else if (name.equals("transition") && (transitionManager != null)) { 287 loadTransition(attrs, sceneRoot, transitionManager); 288 } else { 289 throw new RuntimeException("Unknown scene name: " + parser.getName()); 290 } 291 } 292 return transitionManager; 293 } 294 295 private void loadTransition(AttributeSet attrs, ViewGroup sceneRoot, 296 TransitionManager transitionManager) throws Resources.NotFoundException { 297 298 TypedArray a = mContext.obtainStyledAttributes(attrs, 299 com.android.internal.R.styleable.TransitionManager); 300 int transitionId = a.getResourceId( 301 com.android.internal.R.styleable.TransitionManager_transition, -1); 302 int fromId = a.getResourceId( 303 com.android.internal.R.styleable.TransitionManager_fromScene, -1); 304 Scene fromScene = (fromId < 0) ? null: Scene.getSceneForLayout(sceneRoot, fromId, mContext); 305 int toId = a.getResourceId( 306 com.android.internal.R.styleable.TransitionManager_toScene, -1); 307 Scene toScene = (toId < 0) ? null : Scene.getSceneForLayout(sceneRoot, toId, mContext); 308 309 if (transitionId >= 0) { 310 Transition transition = inflateTransition(transitionId); 311 if (transition != null) { 312 if (fromScene == null) { 313 if (toScene == null) { 314 throw new RuntimeException("No matching fromScene or toScene " + 315 "for transition ID " + transitionId); 316 } else { 317 transitionManager.setTransition(toScene, transition); 318 } 319 } else if (toScene == null) { 320 transitionManager.setExitTransition(fromScene, transition); 321 } else { 322 transitionManager.setTransition(fromScene, toScene, transition); 323 } 324 } 325 } 326 a.recycle(); 327 } 328} 329