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