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