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