AnimationUtils.java revision f54a8d7c479485174941c38f151ea7083c658da3
1/* 2 * Copyright (C) 2007 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.view.animation; 18 19import android.animation.Animatable; 20import android.animation.Animator; 21import android.animation.PropertyAnimator; 22import android.animation.Sequencer; 23import android.content.res.TypedArray; 24import android.util.TypedValue; 25import org.xmlpull.v1.XmlPullParser; 26import org.xmlpull.v1.XmlPullParserException; 27 28import android.content.Context; 29import android.content.res.XmlResourceParser; 30import android.content.res.Resources.NotFoundException; 31import android.util.AttributeSet; 32import android.util.Xml; 33import android.os.SystemClock; 34 35import java.io.IOException; 36import java.util.ArrayList; 37 38/** 39 * Defines common utilities for working with animations. 40 * 41 */ 42public class AnimationUtils { 43 44 /** 45 * These flags are used when parsing Sequencer objects 46 */ 47 private static final int TOGETHER = 0; 48 private static final int SEQUENTIALLY = 1; 49 50 51 /** 52 * Returns the current animation time in milliseconds. This time should be used when invoking 53 * {@link Animation#setStartTime(long)}. Refer to {@link android.os.SystemClock} for more 54 * information about the different available clocks. The clock used by this method is 55 * <em>not</em> the "wall" clock (it is not {@link System#currentTimeMillis}). 56 * 57 * @return the current animation time in milliseconds 58 * 59 * @see android.os.SystemClock 60 */ 61 public static long currentAnimationTimeMillis() { 62 return SystemClock.uptimeMillis(); 63 } 64 65 /** 66 * Loads an {@link Animation} object from a resource 67 * 68 * @param context Application context used to access resources 69 * @param id The resource id of the animation to load 70 * @return The animation object reference by the specified id 71 * @throws NotFoundException when the animation cannot be loaded 72 */ 73 public static Animation loadAnimation(Context context, int id) 74 throws NotFoundException { 75 76 XmlResourceParser parser = null; 77 try { 78 parser = context.getResources().getAnimation(id); 79 return createAnimationFromXml(context, parser); 80 } catch (XmlPullParserException ex) { 81 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + 82 Integer.toHexString(id)); 83 rnf.initCause(ex); 84 throw rnf; 85 } catch (IOException ex) { 86 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + 87 Integer.toHexString(id)); 88 rnf.initCause(ex); 89 throw rnf; 90 } finally { 91 if (parser != null) parser.close(); 92 } 93 } 94 95 /** 96 * Loads an {@link Animation} object from a resource 97 * 98 * @param context Application context used to access resources 99 * @param id The resource id of the animation to load 100 * @return The animation object reference by the specified id 101 * @throws NotFoundException when the animation cannot be loaded 102 * @hide 103 */ 104 public static Animatable loadAnimator(Context context, int id) 105 throws NotFoundException { 106 107 XmlResourceParser parser = null; 108 try { 109 parser = context.getResources().getAnimation(id); 110 return createAnimatableFromXml(context, parser); 111 } catch (XmlPullParserException ex) { 112 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + 113 Integer.toHexString(id)); 114 rnf.initCause(ex); 115 throw rnf; 116 } catch (IOException ex) { 117 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + 118 Integer.toHexString(id)); 119 rnf.initCause(ex); 120 throw rnf; 121 } finally { 122 if (parser != null) parser.close(); 123 } 124 } 125 126 private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser) 127 throws XmlPullParserException, IOException { 128 129 return createAnimatableFromXml(c, parser, Xml.asAttributeSet(parser), null, 0); 130 } 131 132 private static Animation createAnimationFromXml(Context c, XmlPullParser parser) 133 throws XmlPullParserException, IOException { 134 135 return createAnimationFromXml(c, parser, null, Xml.asAttributeSet(parser)); 136 } 137 138 private static Animation createAnimationFromXml(Context c, XmlPullParser parser, 139 AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException { 140 141 Animation anim = null; 142 143 // Make sure we are on a start tag. 144 int type; 145 int depth = parser.getDepth(); 146 147 while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 148 && type != XmlPullParser.END_DOCUMENT) { 149 150 if (type != XmlPullParser.START_TAG) { 151 continue; 152 } 153 154 String name = parser.getName(); 155 156 if (name.equals("set")) { 157 anim = new AnimationSet(c, attrs); 158 createAnimationFromXml(c, parser, (AnimationSet)anim, attrs); 159 } else if (name.equals("alpha")) { 160 anim = new AlphaAnimation(c, attrs); 161 } else if (name.equals("scale")) { 162 anim = new ScaleAnimation(c, attrs); 163 } else if (name.equals("rotate")) { 164 anim = new RotateAnimation(c, attrs); 165 } else if (name.equals("translate")) { 166 anim = new TranslateAnimation(c, attrs); 167 } else { 168 throw new RuntimeException("Unknown animation name: " + parser.getName()); 169 } 170 171 if (parent != null) { 172 parent.addAnimation(anim); 173 } 174 } 175 176 return anim; 177 178 } 179 180 private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser, 181 AttributeSet attrs, Sequencer parent, int sequenceOrdering) 182 throws XmlPullParserException, IOException { 183 184 Animatable anim = null; 185 ArrayList<Animatable> childAnims = null; 186 187 // Make sure we are on a start tag. 188 int type; 189 int depth = parser.getDepth(); 190 191 while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 192 && type != XmlPullParser.END_DOCUMENT) { 193 194 if (type != XmlPullParser.START_TAG) { 195 continue; 196 } 197 198 String name = parser.getName(); 199 200 if (name.equals("property")) { 201 anim = new PropertyAnimator(c, attrs); 202 } else if (name.equals("animator")) { 203 anim = new Animator(c, attrs); 204 } else if (name.equals("sequencer")) { 205 anim = new Sequencer(); 206 TypedArray a = c.obtainStyledAttributes(attrs, 207 com.android.internal.R.styleable.Sequencer); 208 int ordering = a.getInt(com.android.internal.R.styleable.Sequencer_ordering, 209 TOGETHER); 210 createAnimatableFromXml(c, parser, attrs, (Sequencer) anim, ordering); 211 a.recycle(); 212 } else { 213 throw new RuntimeException("Unknown animator name: " + parser.getName()); 214 } 215 216 if (parent != null) { 217 if (childAnims == null) { 218 childAnims = new ArrayList<Animatable>(); 219 } 220 childAnims.add(anim); 221 } 222 } 223 if (parent != null && childAnims != null) { 224 Animatable[] animsArray = new Animatable[childAnims.size()]; 225 int index = 0; 226 for (Animatable a : childAnims) { 227 animsArray[index++] = a; 228 } 229 if (sequenceOrdering == TOGETHER) { 230 parent.playTogether(animsArray); 231 } else { 232 parent.playSequentially(animsArray); 233 } 234 } 235 236 return anim; 237 238 } 239 240 public static LayoutAnimationController loadLayoutAnimation(Context context, int id) 241 throws NotFoundException { 242 243 XmlResourceParser parser = null; 244 try { 245 parser = context.getResources().getAnimation(id); 246 return createLayoutAnimationFromXml(context, parser); 247 } catch (XmlPullParserException ex) { 248 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + 249 Integer.toHexString(id)); 250 rnf.initCause(ex); 251 throw rnf; 252 } catch (IOException ex) { 253 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + 254 Integer.toHexString(id)); 255 rnf.initCause(ex); 256 throw rnf; 257 } finally { 258 if (parser != null) parser.close(); 259 } 260 } 261 262 private static LayoutAnimationController createLayoutAnimationFromXml(Context c, 263 XmlPullParser parser) throws XmlPullParserException, IOException { 264 265 return createLayoutAnimationFromXml(c, parser, Xml.asAttributeSet(parser)); 266 } 267 268 private static LayoutAnimationController createLayoutAnimationFromXml(Context c, 269 XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException { 270 271 LayoutAnimationController controller = null; 272 273 int type; 274 int depth = parser.getDepth(); 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 285 if ("layoutAnimation".equals(name)) { 286 controller = new LayoutAnimationController(c, attrs); 287 } else if ("gridLayoutAnimation".equals(name)) { 288 controller = new GridLayoutAnimationController(c, attrs); 289 } else { 290 throw new RuntimeException("Unknown layout animation name: " + name); 291 } 292 } 293 294 return controller; 295 } 296 297 /** 298 * Make an animation for objects becoming visible. Uses a slide and fade 299 * effect. 300 * 301 * @param c Context for loading resources 302 * @param fromLeft is the object to be animated coming from the left 303 * @return The new animation 304 */ 305 public static Animation makeInAnimation(Context c, boolean fromLeft) { 306 Animation a; 307 if (fromLeft) { 308 a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_left); 309 } else { 310 a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_right); 311 } 312 313 a.setInterpolator(new DecelerateInterpolator()); 314 a.setStartTime(currentAnimationTimeMillis()); 315 return a; 316 } 317 318 /** 319 * Make an animation for objects becoming invisible. Uses a slide and fade 320 * effect. 321 * 322 * @param c Context for loading resources 323 * @param toRight is the object to be animated exiting to the right 324 * @return The new animation 325 */ 326 public static Animation makeOutAnimation(Context c, boolean toRight) { 327 Animation a; 328 if (toRight) { 329 a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_out_right); 330 } else { 331 a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_out_left); 332 } 333 334 a.setInterpolator(new AccelerateInterpolator()); 335 a.setStartTime(currentAnimationTimeMillis()); 336 return a; 337 } 338 339 340 /** 341 * Make an animation for objects becoming visible. Uses a slide up and fade 342 * effect. 343 * 344 * @param c Context for loading resources 345 * @return The new animation 346 */ 347 public static Animation makeInChildBottomAnimation(Context c) { 348 Animation a; 349 a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_child_bottom); 350 a.setInterpolator(new AccelerateInterpolator()); 351 a.setStartTime(currentAnimationTimeMillis()); 352 return a; 353 } 354 355 /** 356 * Loads an {@link Interpolator} object from a resource 357 * 358 * @param context Application context used to access resources 359 * @param id The resource id of the animation to load 360 * @return The animation object reference by the specified id 361 * @throws NotFoundException 362 */ 363 public static Interpolator loadInterpolator(Context context, int id) throws NotFoundException { 364 XmlResourceParser parser = null; 365 try { 366 parser = context.getResources().getAnimation(id); 367 return createInterpolatorFromXml(context, parser); 368 } catch (XmlPullParserException ex) { 369 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + 370 Integer.toHexString(id)); 371 rnf.initCause(ex); 372 throw rnf; 373 } catch (IOException ex) { 374 NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + 375 Integer.toHexString(id)); 376 rnf.initCause(ex); 377 throw rnf; 378 } finally { 379 if (parser != null) parser.close(); 380 } 381 382 } 383 384 private static Interpolator createInterpolatorFromXml(Context c, XmlPullParser parser) 385 throws XmlPullParserException, IOException { 386 387 Interpolator interpolator = null; 388 389 // Make sure we are on a start tag. 390 int type; 391 int depth = parser.getDepth(); 392 393 while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 394 && type != XmlPullParser.END_DOCUMENT) { 395 396 if (type != XmlPullParser.START_TAG) { 397 continue; 398 } 399 400 AttributeSet attrs = Xml.asAttributeSet(parser); 401 402 String name = parser.getName(); 403 404 405 if (name.equals("linearInterpolator")) { 406 interpolator = new LinearInterpolator(c, attrs); 407 } else if (name.equals("accelerateInterpolator")) { 408 interpolator = new AccelerateInterpolator(c, attrs); 409 } else if (name.equals("decelerateInterpolator")) { 410 interpolator = new DecelerateInterpolator(c, attrs); 411 } else if (name.equals("accelerateDecelerateInterpolator")) { 412 interpolator = new AccelerateDecelerateInterpolator(c, attrs); 413 } else if (name.equals("cycleInterpolator")) { 414 interpolator = new CycleInterpolator(c, attrs); 415 } else if (name.equals("anticipateInterpolator")) { 416 interpolator = new AnticipateInterpolator(c, attrs); 417 } else if (name.equals("overshootInterpolator")) { 418 interpolator = new OvershootInterpolator(c, attrs); 419 } else if (name.equals("anticipateOvershootInterpolator")) { 420 interpolator = new AnticipateOvershootInterpolator(c, attrs); 421 } else if (name.equals("bounceInterpolator")) { 422 interpolator = new BounceInterpolator(c, attrs); 423 } else { 424 throw new RuntimeException("Unknown interpolator name: " + parser.getName()); 425 } 426 427 } 428 429 return interpolator; 430 431 } 432} 433