| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.view.animation; |
| |
| import android.animation.Animatable; |
| import android.animation.Animator; |
| import android.animation.PropertyAnimator; |
| import android.animation.Sequencer; |
| import android.content.res.TypedArray; |
| import android.util.TypedValue; |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| |
| import android.content.Context; |
| import android.content.res.XmlResourceParser; |
| import android.content.res.Resources.NotFoundException; |
| import android.util.AttributeSet; |
| import android.util.Xml; |
| import android.os.SystemClock; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| |
| /** |
| * Defines common utilities for working with animations. |
| * |
| */ |
| public class AnimationUtils { |
| |
| /** |
| * These flags are used when parsing Sequencer objects |
| */ |
| private static final int TOGETHER = 0; |
| private static final int SEQUENTIALLY = 1; |
| |
| |
| /** |
| * Returns the current animation time in milliseconds. This time should be used when invoking |
| * {@link Animation#setStartTime(long)}. Refer to {@link android.os.SystemClock} for more |
| * information about the different available clocks. The clock used by this method is |
| * <em>not</em> the "wall" clock (it is not {@link System#currentTimeMillis}). |
| * |
| * @return the current animation time in milliseconds |
| * |
| * @see android.os.SystemClock |
| */ |
| public static long currentAnimationTimeMillis() { |
| return SystemClock.uptimeMillis(); |
| } |
| |
| /** |
| * Loads an {@link Animation} object from a resource |
| * |
| * @param context Application context used to access resources |
| * @param id The resource id of the animation to load |
| * @return The animation object reference by the specified id |
| * @throws NotFoundException when the animation cannot be loaded |
| */ |
| public static Animation loadAnimation(Context context, int id) |
| throws NotFoundException { |
| |
| XmlResourceParser parser = null; |
| try { |
| parser = context.getResources().getAnimation(id); |
| return createAnimationFromXml(context, parser); |
| } catch (XmlPullParserException ex) { |
| NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + |
| Integer.toHexString(id)); |
| rnf.initCause(ex); |
| throw rnf; |
| } catch (IOException ex) { |
| NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + |
| Integer.toHexString(id)); |
| rnf.initCause(ex); |
| throw rnf; |
| } finally { |
| if (parser != null) parser.close(); |
| } |
| } |
| |
| /** |
| * Loads an {@link Animation} object from a resource |
| * |
| * @param context Application context used to access resources |
| * @param id The resource id of the animation to load |
| * @return The animation object reference by the specified id |
| * @throws NotFoundException when the animation cannot be loaded |
| */ |
| public static Animatable loadAnimator(Context context, int id) |
| throws NotFoundException { |
| |
| XmlResourceParser parser = null; |
| try { |
| parser = context.getResources().getAnimation(id); |
| return createAnimatableFromXml(context, parser); |
| } catch (XmlPullParserException ex) { |
| NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + |
| Integer.toHexString(id)); |
| rnf.initCause(ex); |
| throw rnf; |
| } catch (IOException ex) { |
| NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + |
| Integer.toHexString(id)); |
| rnf.initCause(ex); |
| throw rnf; |
| } finally { |
| if (parser != null) parser.close(); |
| } |
| } |
| |
| private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser) |
| throws XmlPullParserException, IOException { |
| |
| return createAnimatableFromXml(c, parser, Xml.asAttributeSet(parser), null, 0); |
| } |
| |
| private static Animation createAnimationFromXml(Context c, XmlPullParser parser) |
| throws XmlPullParserException, IOException { |
| |
| return createAnimationFromXml(c, parser, null, Xml.asAttributeSet(parser)); |
| } |
| |
| private static Animation createAnimationFromXml(Context c, XmlPullParser parser, |
| AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException { |
| |
| Animation anim = null; |
| |
| // Make sure we are on a start tag. |
| int type; |
| int depth = parser.getDepth(); |
| |
| while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) |
| && type != XmlPullParser.END_DOCUMENT) { |
| |
| if (type != XmlPullParser.START_TAG) { |
| continue; |
| } |
| |
| String name = parser.getName(); |
| |
| if (name.equals("set")) { |
| anim = new AnimationSet(c, attrs); |
| createAnimationFromXml(c, parser, (AnimationSet)anim, attrs); |
| } else if (name.equals("alpha")) { |
| anim = new AlphaAnimation(c, attrs); |
| } else if (name.equals("scale")) { |
| anim = new ScaleAnimation(c, attrs); |
| } else if (name.equals("rotate")) { |
| anim = new RotateAnimation(c, attrs); |
| } else if (name.equals("translate")) { |
| anim = new TranslateAnimation(c, attrs); |
| } else { |
| throw new RuntimeException("Unknown animation name: " + parser.getName()); |
| } |
| |
| if (parent != null) { |
| parent.addAnimation(anim); |
| } |
| } |
| |
| return anim; |
| |
| } |
| |
| private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser, |
| AttributeSet attrs, Sequencer parent, int sequenceOrdering) |
| throws XmlPullParserException, IOException { |
| |
| Animatable anim = null; |
| ArrayList<Animatable> childAnims = null; |
| |
| // Make sure we are on a start tag. |
| int type; |
| int depth = parser.getDepth(); |
| |
| while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) |
| && type != XmlPullParser.END_DOCUMENT) { |
| |
| if (type != XmlPullParser.START_TAG) { |
| continue; |
| } |
| |
| String name = parser.getName(); |
| |
| if (name.equals("property")) { |
| anim = new PropertyAnimator(c, attrs); |
| } else if (name.equals("animator")) { |
| anim = new Animator(c, attrs); |
| } else if (name.equals("sequencer")) { |
| anim = new Sequencer(); |
| TypedArray a = c.obtainStyledAttributes(attrs, |
| com.android.internal.R.styleable.Sequencer); |
| int ordering = a.getInt(com.android.internal.R.styleable.Sequencer_ordering, |
| TOGETHER); |
| createAnimatableFromXml(c, parser, attrs, (Sequencer) anim, ordering); |
| a.recycle(); |
| } else { |
| throw new RuntimeException("Unknown animator name: " + parser.getName()); |
| } |
| |
| if (parent != null) { |
| if (childAnims == null) { |
| childAnims = new ArrayList<Animatable>(); |
| } |
| childAnims.add(anim); |
| } |
| } |
| if (parent != null && childAnims != null) { |
| Animatable[] animsArray = new Animatable[childAnims.size()]; |
| int index = 0; |
| for (Animatable a : childAnims) { |
| animsArray[index++] = a; |
| } |
| if (sequenceOrdering == TOGETHER) { |
| parent.playTogether(animsArray); |
| } else { |
| parent.playSequentially(animsArray); |
| } |
| } |
| |
| return anim; |
| |
| } |
| |
| public static LayoutAnimationController loadLayoutAnimation(Context context, int id) |
| throws NotFoundException { |
| |
| XmlResourceParser parser = null; |
| try { |
| parser = context.getResources().getAnimation(id); |
| return createLayoutAnimationFromXml(context, parser); |
| } catch (XmlPullParserException ex) { |
| NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + |
| Integer.toHexString(id)); |
| rnf.initCause(ex); |
| throw rnf; |
| } catch (IOException ex) { |
| NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + |
| Integer.toHexString(id)); |
| rnf.initCause(ex); |
| throw rnf; |
| } finally { |
| if (parser != null) parser.close(); |
| } |
| } |
| |
| private static LayoutAnimationController createLayoutAnimationFromXml(Context c, |
| XmlPullParser parser) throws XmlPullParserException, IOException { |
| |
| return createLayoutAnimationFromXml(c, parser, Xml.asAttributeSet(parser)); |
| } |
| |
| private static LayoutAnimationController createLayoutAnimationFromXml(Context c, |
| XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException { |
| |
| LayoutAnimationController controller = null; |
| |
| int type; |
| int depth = parser.getDepth(); |
| |
| while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) |
| && type != XmlPullParser.END_DOCUMENT) { |
| |
| if (type != XmlPullParser.START_TAG) { |
| continue; |
| } |
| |
| String name = parser.getName(); |
| |
| if ("layoutAnimation".equals(name)) { |
| controller = new LayoutAnimationController(c, attrs); |
| } else if ("gridLayoutAnimation".equals(name)) { |
| controller = new GridLayoutAnimationController(c, attrs); |
| } else { |
| throw new RuntimeException("Unknown layout animation name: " + name); |
| } |
| } |
| |
| return controller; |
| } |
| |
| /** |
| * Make an animation for objects becoming visible. Uses a slide and fade |
| * effect. |
| * |
| * @param c Context for loading resources |
| * @param fromLeft is the object to be animated coming from the left |
| * @return The new animation |
| */ |
| public static Animation makeInAnimation(Context c, boolean fromLeft) { |
| Animation a; |
| if (fromLeft) { |
| a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_left); |
| } else { |
| a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_right); |
| } |
| |
| a.setInterpolator(new DecelerateInterpolator()); |
| a.setStartTime(currentAnimationTimeMillis()); |
| return a; |
| } |
| |
| /** |
| * Make an animation for objects becoming invisible. Uses a slide and fade |
| * effect. |
| * |
| * @param c Context for loading resources |
| * @param toRight is the object to be animated exiting to the right |
| * @return The new animation |
| */ |
| public static Animation makeOutAnimation(Context c, boolean toRight) { |
| Animation a; |
| if (toRight) { |
| a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_out_right); |
| } else { |
| a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_out_left); |
| } |
| |
| a.setInterpolator(new AccelerateInterpolator()); |
| a.setStartTime(currentAnimationTimeMillis()); |
| return a; |
| } |
| |
| |
| /** |
| * Make an animation for objects becoming visible. Uses a slide up and fade |
| * effect. |
| * |
| * @param c Context for loading resources |
| * @return The new animation |
| */ |
| public static Animation makeInChildBottomAnimation(Context c) { |
| Animation a; |
| a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_child_bottom); |
| a.setInterpolator(new AccelerateInterpolator()); |
| a.setStartTime(currentAnimationTimeMillis()); |
| return a; |
| } |
| |
| /** |
| * Loads an {@link Interpolator} object from a resource |
| * |
| * @param context Application context used to access resources |
| * @param id The resource id of the animation to load |
| * @return The animation object reference by the specified id |
| * @throws NotFoundException |
| */ |
| public static Interpolator loadInterpolator(Context context, int id) throws NotFoundException { |
| XmlResourceParser parser = null; |
| try { |
| parser = context.getResources().getAnimation(id); |
| return createInterpolatorFromXml(context, parser); |
| } catch (XmlPullParserException ex) { |
| NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + |
| Integer.toHexString(id)); |
| rnf.initCause(ex); |
| throw rnf; |
| } catch (IOException ex) { |
| NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + |
| Integer.toHexString(id)); |
| rnf.initCause(ex); |
| throw rnf; |
| } finally { |
| if (parser != null) parser.close(); |
| } |
| |
| } |
| |
| private static Interpolator createInterpolatorFromXml(Context c, XmlPullParser parser) |
| throws XmlPullParserException, IOException { |
| |
| Interpolator interpolator = null; |
| |
| // Make sure we are on a start tag. |
| int type; |
| int depth = parser.getDepth(); |
| |
| while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) |
| && type != XmlPullParser.END_DOCUMENT) { |
| |
| if (type != XmlPullParser.START_TAG) { |
| continue; |
| } |
| |
| AttributeSet attrs = Xml.asAttributeSet(parser); |
| |
| String name = parser.getName(); |
| |
| |
| if (name.equals("linearInterpolator")) { |
| interpolator = new LinearInterpolator(c, attrs); |
| } else if (name.equals("accelerateInterpolator")) { |
| interpolator = new AccelerateInterpolator(c, attrs); |
| } else if (name.equals("decelerateInterpolator")) { |
| interpolator = new DecelerateInterpolator(c, attrs); |
| } else if (name.equals("accelerateDecelerateInterpolator")) { |
| interpolator = new AccelerateDecelerateInterpolator(c, attrs); |
| } else if (name.equals("cycleInterpolator")) { |
| interpolator = new CycleInterpolator(c, attrs); |
| } else if (name.equals("anticipateInterpolator")) { |
| interpolator = new AnticipateInterpolator(c, attrs); |
| } else if (name.equals("overshootInterpolator")) { |
| interpolator = new OvershootInterpolator(c, attrs); |
| } else if (name.equals("anticipateOvershootInterpolator")) { |
| interpolator = new AnticipateOvershootInterpolator(c, attrs); |
| } else if (name.equals("bounceInterpolator")) { |
| interpolator = new BounceInterpolator(c, attrs); |
| } else { |
| throw new RuntimeException("Unknown interpolator name: " + parser.getName()); |
| } |
| |
| } |
| |
| return interpolator; |
| |
| } |
| } |