AnimatorInflater.java revision 7c608f25d494c8a0a671e7373efbb47ca635367e
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_COLOR = 4; 55 private static final int VALUE_TYPE_CUSTOM = 5; 56 57 /** 58 * Loads an {@link Animator} object from a resource 59 * 60 * @param context Application context used to access resources 61 * @param id The resource id of the animation to load 62 * @return The animator object reference by the specified id 63 * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded 64 */ 65 public static Animator loadAnimator(Context context, int id) 66 throws NotFoundException { 67 68 XmlResourceParser parser = null; 69 try { 70 parser = context.getResources().getAnimation(id); 71 return createAnimatorFromXml(context, parser); 72 } catch (XmlPullParserException ex) { 73 Resources.NotFoundException rnf = 74 new Resources.NotFoundException("Can't load animation resource ID #0x" + 75 Integer.toHexString(id)); 76 rnf.initCause(ex); 77 throw rnf; 78 } catch (IOException ex) { 79 Resources.NotFoundException rnf = 80 new Resources.NotFoundException("Can't load animation resource ID #0x" + 81 Integer.toHexString(id)); 82 rnf.initCause(ex); 83 throw rnf; 84 } finally { 85 if (parser != null) parser.close(); 86 } 87 } 88 89 private static Animator createAnimatorFromXml(Context c, XmlPullParser parser) 90 throws XmlPullParserException, IOException { 91 92 return createAnimatorFromXml(c, parser, Xml.asAttributeSet(parser), null, 0); 93 } 94 95 private static Animator createAnimatorFromXml(Context c, XmlPullParser parser, 96 AttributeSet attrs, AnimatorSet parent, int sequenceOrdering) 97 throws XmlPullParserException, IOException { 98 99 Animator anim = null; 100 ArrayList<Animator> childAnims = null; 101 102 // Make sure we are on a start tag. 103 int type; 104 int depth = parser.getDepth(); 105 106 while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 107 && type != XmlPullParser.END_DOCUMENT) { 108 109 if (type != XmlPullParser.START_TAG) { 110 continue; 111 } 112 113 String name = parser.getName(); 114 115 if (name.equals("objectAnimator")) { 116 anim = loadObjectAnimator(c, attrs); 117 } else if (name.equals("animator")) { 118 anim = loadAnimator(c, attrs, null); 119 } else if (name.equals("set")) { 120 anim = new AnimatorSet(); 121 TypedArray a = c.obtainStyledAttributes(attrs, 122 com.android.internal.R.styleable.AnimatorSet); 123 int ordering = a.getInt(com.android.internal.R.styleable.AnimatorSet_ordering, 124 TOGETHER); 125 createAnimatorFromXml(c, parser, attrs, (AnimatorSet) anim, ordering); 126 a.recycle(); 127 } else { 128 throw new RuntimeException("Unknown animator name: " + parser.getName()); 129 } 130 131 if (parent != null) { 132 if (childAnims == null) { 133 childAnims = new ArrayList<Animator>(); 134 } 135 childAnims.add(anim); 136 } 137 } 138 if (parent != null && childAnims != null) { 139 Animator[] animsArray = new Animator[childAnims.size()]; 140 int index = 0; 141 for (Animator a : childAnims) { 142 animsArray[index++] = a; 143 } 144 if (sequenceOrdering == TOGETHER) { 145 parent.playTogether(animsArray); 146 } else { 147 parent.playSequentially(animsArray); 148 } 149 } 150 151 return anim; 152 153 } 154 155 private static ObjectAnimator loadObjectAnimator(Context context, AttributeSet attrs) 156 throws NotFoundException { 157 158 ObjectAnimator anim = new ObjectAnimator(); 159 160 loadAnimator(context, attrs, anim); 161 162 TypedArray a = 163 context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.PropertyAnimator); 164 165 String propertyName = a.getString(com.android.internal.R.styleable.PropertyAnimator_propertyName); 166 167 anim.setPropertyName(propertyName); 168 169 a.recycle(); 170 171 return anim; 172 } 173 174 /** 175 * Creates a new animation whose parameters come from the specified context and 176 * attributes set. 177 * 178 * @param context the application environment 179 * @param attrs the set of attributes holding the animation parameters 180 */ 181 private static ValueAnimator loadAnimator(Context context, AttributeSet attrs, ValueAnimator anim) 182 throws NotFoundException { 183 184 TypedArray a = 185 context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animator); 186 187 long duration = a.getInt(com.android.internal.R.styleable.Animator_duration, 0); 188 189 long startDelay = a.getInt(com.android.internal.R.styleable.Animator_startOffset, 0); 190 191 int valueType = a.getInt(com.android.internal.R.styleable.Animator_valueType, 192 VALUE_TYPE_FLOAT); 193 194 if (anim == null) { 195 anim = new ValueAnimator(); 196 } 197 TypeEvaluator evaluator = null; 198 boolean hasFrom = a.hasValue(com.android.internal.R.styleable.Animator_valueFrom); 199 boolean hasTo = a.hasValue(com.android.internal.R.styleable.Animator_valueTo); 200 201 switch (valueType) { 202 203 case VALUE_TYPE_FLOAT: { 204 float valueFrom; 205 float valueTo; 206 if (hasFrom) { 207 valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f); 208 if (hasTo) { 209 valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); 210 anim.setFloatValues(valueFrom, valueTo); 211 } else { 212 anim.setFloatValues(valueFrom); 213 } 214 } else { 215 valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); 216 anim.setFloatValues(valueTo); 217 } 218 } 219 break; 220 221 case VALUE_TYPE_COLOR: 222 evaluator = new RGBEvaluator(); 223 anim.setEvaluator(evaluator); 224 // fall through to pick up values 225 case VALUE_TYPE_INT: { 226 int valueFrom; 227 int valueTo; 228 if (hasFrom) { 229 valueFrom = a.getInteger(com.android.internal.R.styleable.Animator_valueFrom, 0); 230 if (hasTo) { 231 valueTo = a.getInteger(com.android.internal.R.styleable.Animator_valueTo, 0); 232 anim.setIntValues(valueFrom, valueTo); 233 } else { 234 anim.setIntValues(valueFrom); 235 } 236 } else { 237 valueTo = a.getInteger(com.android.internal.R.styleable.Animator_valueTo, 0); 238 anim.setIntValues(valueTo); 239 } 240 } 241 break; 242 243 case VALUE_TYPE_CUSTOM: { 244 // TODO: How to get an 'Object' value? 245 float valueFrom; 246 float valueTo; 247 if (hasFrom) { 248 valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f); 249 if (hasTo) { 250 valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); 251 anim.setFloatValues(valueFrom, valueTo); 252 } else { 253 anim.setFloatValues(valueFrom); 254 } 255 } else { 256 valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); 257 anim.setFloatValues(valueTo); 258 } 259 } 260 break; 261 } 262 263 264 anim.setDuration(duration); 265 anim.setStartDelay(startDelay); 266 267 if (a.hasValue(com.android.internal.R.styleable.Animator_repeatCount)) { 268 anim.setRepeatCount( 269 a.getInt(com.android.internal.R.styleable.Animator_repeatCount, 0)); 270 } 271 if (a.hasValue(com.android.internal.R.styleable.Animator_repeatMode)) { 272 anim.setRepeatMode( 273 a.getInt(com.android.internal.R.styleable.Animator_repeatMode, 274 ValueAnimator.RESTART)); 275 } 276 if (evaluator != null) { 277 anim.setEvaluator(evaluator); 278 } 279 280 final int resID = 281 a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0); 282 if (resID > 0) { 283 anim.setInterpolator(AnimationUtils.loadInterpolator(context, resID)); 284 } 285 a.recycle(); 286 287 return anim; 288 } 289} 290