AnimatorInflater.java revision 2794eb3b02e2404d453d3ad22a8a85a138130a07
1/* 2 * Copyright (C) 2010 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 */ 16package android.animation; 17 18import android.content.Context; 19import android.content.res.Resources; 20import android.content.res.TypedArray; 21import android.content.res.XmlResourceParser; 22import android.content.res.Resources.NotFoundException; 23import android.util.AttributeSet; 24import android.util.Xml; 25import android.view.animation.AnimationUtils; 26import org.xmlpull.v1.XmlPullParser; 27import org.xmlpull.v1.XmlPullParserException; 28 29import java.io.IOException; 30import java.util.ArrayList; 31 32/** 33 * This class is used to instantiate menu XML files into Animator objects. 34 * <p> 35 * For performance reasons, menu inflation relies heavily on pre-processing of 36 * XML files that is done at build time. Therefore, it is not currently possible 37 * to use MenuInflater with an XmlPullParser over a plain XML file at runtime; 38 * it only works with an XmlPullParser returned from a compiled resource (R. 39 * <em>something</em> file.) 40 */ 41public class AnimatorInflater { 42 43 /** 44 * These flags are used when parsing AnimatorSet objects 45 */ 46 private static final int TOGETHER = 0; 47 private static final int SEQUENTIALLY = 1; 48 49 /** 50 * Enum values used in XML attributes to indicate the value for mValueType 51 */ 52 private static final int VALUE_TYPE_FLOAT = 0; 53 private static final int VALUE_TYPE_INT = 1; 54 private static final int VALUE_TYPE_DOUBLE = 2; 55 private static final int VALUE_TYPE_LONG = 3; 56 private static final int VALUE_TYPE_COLOR = 4; 57 private static final int VALUE_TYPE_CUSTOM = 5; 58 59 /** 60 * Loads an {@link Animator} object from a resource 61 * 62 * @param context Application context used to access resources 63 * @param id The resource id of the animation to load 64 * @return The animator object reference by the specified id 65 * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded 66 */ 67 public static Animator loadAnimator(Context context, int id) 68 throws NotFoundException { 69 70 XmlResourceParser parser = null; 71 try { 72 parser = context.getResources().getAnimation(id); 73 return createAnimatorFromXml(context, parser); 74 } catch (XmlPullParserException ex) { 75 Resources.NotFoundException rnf = 76 new Resources.NotFoundException("Can't load animation resource ID #0x" + 77 Integer.toHexString(id)); 78 rnf.initCause(ex); 79 throw rnf; 80 } catch (IOException ex) { 81 Resources.NotFoundException rnf = 82 new Resources.NotFoundException("Can't load animation resource ID #0x" + 83 Integer.toHexString(id)); 84 rnf.initCause(ex); 85 throw rnf; 86 } finally { 87 if (parser != null) parser.close(); 88 } 89 } 90 91 private static Animator createAnimatorFromXml(Context c, XmlPullParser parser) 92 throws XmlPullParserException, IOException { 93 94 return createAnimatorFromXml(c, parser, Xml.asAttributeSet(parser), null, 0); 95 } 96 97 private static Animator createAnimatorFromXml(Context c, XmlPullParser parser, 98 AttributeSet attrs, AnimatorSet parent, int sequenceOrdering) 99 throws XmlPullParserException, IOException { 100 101 Animator anim = null; 102 ArrayList<Animator> childAnims = null; 103 104 // Make sure we are on a start tag. 105 int type; 106 int depth = parser.getDepth(); 107 108 while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 109 && type != XmlPullParser.END_DOCUMENT) { 110 111 if (type != XmlPullParser.START_TAG) { 112 continue; 113 } 114 115 String name = parser.getName(); 116 117 if (name.equals("objectAnimator")) { 118 anim = loadObjectAnimator(c, attrs); 119 } else if (name.equals("animator")) { 120 anim = loadAnimator(c, attrs, null); 121 } else if (name.equals("set")) { 122 anim = new AnimatorSet(); 123 TypedArray a = c.obtainStyledAttributes(attrs, 124 com.android.internal.R.styleable.AnimatorSet); 125 int ordering = a.getInt(com.android.internal.R.styleable.AnimatorSet_ordering, 126 TOGETHER); 127 createAnimatorFromXml(c, parser, attrs, (AnimatorSet) anim, ordering); 128 a.recycle(); 129 } else { 130 throw new RuntimeException("Unknown animator name: " + parser.getName()); 131 } 132 133 if (parent != null) { 134 if (childAnims == null) { 135 childAnims = new ArrayList<Animator>(); 136 } 137 childAnims.add(anim); 138 } 139 } 140 if (parent != null && childAnims != null) { 141 Animator[] animsArray = new Animator[childAnims.size()]; 142 int index = 0; 143 for (Animator a : childAnims) { 144 animsArray[index++] = a; 145 } 146 if (sequenceOrdering == TOGETHER) { 147 parent.playTogether(animsArray); 148 } else { 149 parent.playSequentially(animsArray); 150 } 151 } 152 153 return anim; 154 155 } 156 157 private static ObjectAnimator loadObjectAnimator(Context context, AttributeSet attrs) 158 throws NotFoundException { 159 160 ObjectAnimator anim = new ObjectAnimator(); 161 162 loadAnimator(context, attrs, anim); 163 164 TypedArray a = 165 context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.PropertyAnimator); 166 167 String propertyName = a.getString(com.android.internal.R.styleable.PropertyAnimator_propertyName); 168 169 anim.setPropertyName(propertyName); 170 171 a.recycle(); 172 173 return anim; 174 } 175 176 /** 177 * Creates a new animation whose parameters come from the specified context and 178 * attributes set. 179 * 180 * @param context the application environment 181 * @param attrs the set of attributes holding the animation parameters 182 */ 183 private static ValueAnimator loadAnimator(Context context, AttributeSet attrs, ValueAnimator anim) 184 throws NotFoundException { 185 186 TypedArray a = 187 context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animator); 188 189 long duration = a.getInt(com.android.internal.R.styleable.Animator_duration, 0); 190 191 long startDelay = a.getInt(com.android.internal.R.styleable.Animator_startOffset, 0); 192 193 int valueType = a.getInt(com.android.internal.R.styleable.Animator_valueType, 194 VALUE_TYPE_FLOAT); 195 196 if (anim == null) { 197 anim = new ValueAnimator(); 198 } 199 TypeEvaluator evaluator = null; 200 boolean hasFrom = a.hasValue(com.android.internal.R.styleable.Animator_valueFrom); 201 boolean hasTo = a.hasValue(com.android.internal.R.styleable.Animator_valueTo); 202 203 switch (valueType) { 204 205 case VALUE_TYPE_FLOAT: { 206 float valueFrom; 207 float valueTo; 208 if (hasFrom) { 209 valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f); 210 if (hasTo) { 211 valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); 212 anim.setFloatValues(valueFrom, valueTo); 213 } else { 214 anim.setFloatValues(valueFrom); 215 } 216 } else { 217 valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); 218 anim.setFloatValues(valueTo); 219 } 220 } 221 break; 222 223 case VALUE_TYPE_COLOR: 224 evaluator = new RGBEvaluator(); 225 anim.setEvaluator(evaluator); 226 // fall through to pick up values 227 case VALUE_TYPE_INT: { 228 int valueFrom; 229 int valueTo; 230 if (hasFrom) { 231 valueFrom = a.getInteger(com.android.internal.R.styleable.Animator_valueFrom, 0); 232 if (hasTo) { 233 valueTo = a.getInteger(com.android.internal.R.styleable.Animator_valueTo, 0); 234 anim.setIntValues(valueFrom, valueTo); 235 } else { 236 anim.setIntValues(valueFrom); 237 } 238 } else { 239 valueTo = a.getInteger(com.android.internal.R.styleable.Animator_valueTo, 0); 240 anim.setIntValues(valueTo); 241 } 242 } 243 break; 244 245 case VALUE_TYPE_LONG: { 246 int valueFrom; 247 int valueTo; 248 if (hasFrom) { 249 valueFrom = a.getInteger(com.android.internal.R.styleable.Animator_valueFrom, 0); 250 if (hasTo) { 251 valueTo = a.getInteger(com.android.internal.R.styleable.Animator_valueTo, 0); 252 anim.setLongValues(valueFrom, valueTo); 253 } else { 254 anim.setLongValues(valueFrom); 255 } 256 } else { 257 valueTo = a.getInteger(com.android.internal.R.styleable.Animator_valueTo, 0); 258 anim.setLongValues(valueTo); 259 } 260 } 261 break; 262 263 case VALUE_TYPE_DOUBLE: { 264 double valueFrom; 265 double valueTo; 266 if (hasFrom) { 267 valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f); 268 if (hasTo) { 269 valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); 270 anim.setDoubleValues(valueFrom, valueTo); 271 } else { 272 anim.setDoubleValues(valueFrom); 273 } 274 } else { 275 valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); 276 anim.setDoubleValues(valueTo); 277 } 278 } 279 break; 280 281 case VALUE_TYPE_CUSTOM: { 282 // TODO: How to get an 'Object' value? 283 float valueFrom; 284 float valueTo; 285 if (hasFrom) { 286 valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f); 287 if (hasTo) { 288 valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); 289 anim.setFloatValues(valueFrom, valueTo); 290 } else { 291 anim.setFloatValues(valueFrom); 292 } 293 } else { 294 valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); 295 anim.setFloatValues(valueTo); 296 } 297 } 298 break; 299 } 300 301 302 anim.setDuration(duration); 303 anim.setStartDelay(startDelay); 304 305 if (a.hasValue(com.android.internal.R.styleable.Animator_repeatCount)) { 306 anim.setRepeatCount( 307 a.getInt(com.android.internal.R.styleable.Animator_repeatCount, 0)); 308 } 309 if (a.hasValue(com.android.internal.R.styleable.Animator_repeatMode)) { 310 anim.setRepeatMode( 311 a.getInt(com.android.internal.R.styleable.Animator_repeatMode, 312 ValueAnimator.RESTART)); 313 } 314 if (evaluator != null) { 315 anim.setEvaluator(evaluator); 316 } 317 318 final int resID = 319 a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0); 320 if (resID > 0) { 321 anim.setInterpolator(AnimationUtils.loadInterpolator(context, resID)); 322 } 323 a.recycle(); 324 325 return anim; 326 } 327} 328