/* * Copyright (C) 2006 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.graphics.drawable; import java.io.IOException; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.content.res.Resources; import android.content.res.TypedArray; import android.os.SystemClock; import android.util.AttributeSet; /** * * An object used to create frame-by-frame animations, defined by a series of Drawable objects, * which can be used as a View object's background. *
* The simplest way to create a frame-by-frame animation is to define the animation in an XML * file, placed in the res/drawable/ folder, and set it as the background to a View object. Then, call * {@link #start()} to run the animation. *
* An AnimationDrawable defined in XML consists of a single <animation-list>
element,
* and a series of nested <item>
tags. Each item defines a frame of the animation.
* See the example below.
*
spin_animation.xml file in res/drawable/ folder:
*<!-- Animation frames are wheel0.png -- wheel5.png files inside the * res/drawable/ folder --> * <animation-list android:id="selected" android:oneshot="false"> * <item android:drawable="@drawable/wheel0" android:duration="50" /> * <item android:drawable="@drawable/wheel1" android:duration="50" /> * <item android:drawable="@drawable/wheel2" android:duration="50" /> * <item android:drawable="@drawable/wheel3" android:duration="50" /> * <item android:drawable="@drawable/wheel4" android:duration="50" /> * <item android:drawable="@drawable/wheel5" android:duration="50" /> * </animation-list>* *
Here is the code to load and play this animation.
** // Load the ImageView that will host the animation and * // set its background to our AnimationDrawable XML resource. * ImageView img = (ImageView)findViewById(R.id.spinning_wheel_image); * img.setBackgroundResource(R.drawable.spin_animation); * * // Get the background, which has been compiled to an AnimationDrawable object. * AnimationDrawable frameAnimation = (AnimationDrawable) img.getBackground(); * * // Start the animation (looped playback by default). * frameAnimation.start(); ** *
For more information about animating with {@code AnimationDrawable}, read the * Drawable Animation * developer guide.
*Starts the animation, looping if necessary. This method has no effect * if the animation is running. Do not call this in the {@link android.app.Activity#onCreate} * method of your activity, because the {@link android.graphics.drawable.AnimationDrawable} is * not yet fully attached to the window. If you want to play * the animation immediately, without requiring interaction, then you might want to call it * from the {@link android.app.Activity#onWindowFocusChanged} method in your activity, * which will get called when Android brings your window into focus.
* * @see #isRunning() * @see #stop() */ public void start() { if (!isRunning()) { run(); } } /** *Stops the animation. This method has no effect if the animation is * not running.
* * @see #isRunning() * @see #start() */ public void stop() { if (isRunning()) { unscheduleSelf(this); } } /** *Indicates whether the animation is currently running or not.
* * @return true if the animation is running, false otherwise */ public boolean isRunning() { return mCurFrame > -1; } /** *This method exists for implementation purpose only and should not be * called directly. Invoke {@link #start()} instead.
* * @see #start() */ public void run() { nextFrame(false); } @Override public void unscheduleSelf(Runnable what) { mCurFrame = -1; super.unscheduleSelf(what); } /** * @return The number of frames in the animation */ public int getNumberOfFrames() { return mAnimationState.getChildCount(); } /** * @return The Drawable at the specified frame index */ public Drawable getFrame(int index) { return mAnimationState.getChildren()[index]; } /** * @return The duration in milliseconds of the frame at the * specified index */ public int getDuration(int i) { return mAnimationState.mDurations[i]; } /** * @return True of the animation will play once, false otherwise */ public boolean isOneShot() { return mAnimationState.mOneShot; } /** * Sets whether the animation should play once or repeat. * * @param oneShot Pass true if the animation should only play once */ public void setOneShot(boolean oneShot) { mAnimationState.mOneShot = oneShot; } /** * Add a frame to the animation * * @param frame The frame to add * @param duration How long in milliseconds the frame should appear */ public void addFrame(Drawable frame, int duration) { mAnimationState.addFrame(frame, duration); if (mCurFrame < 0) { setFrame(0, true, false); } } private void nextFrame(boolean unschedule) { int next = mCurFrame+1; final int N = mAnimationState.getChildCount(); if (next >= N) { next = 0; } setFrame(next, unschedule, !mAnimationState.mOneShot || next < (N - 1)); } private void setFrame(int frame, boolean unschedule, boolean animate) { if (frame >= mAnimationState.getChildCount()) { return; } mCurFrame = frame; selectDrawable(frame); if (unschedule) { unscheduleSelf(this); } if (animate) { scheduleSelf(this, SystemClock.uptimeMillis() + mAnimationState.mDurations[frame]); } } @Override public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException { TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.AnimationDrawable); super.inflateWithAttributes(r, parser, a, com.android.internal.R.styleable.AnimationDrawable_visible); mAnimationState.setVariablePadding(a.getBoolean( com.android.internal.R.styleable.AnimationDrawable_variablePadding, false)); mAnimationState.mOneShot = a.getBoolean( com.android.internal.R.styleable.AnimationDrawable_oneshot, false); a.recycle(); int type; final int innerDepth = parser.getDepth()+1; int depth; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { if (type != XmlPullParser.START_TAG) { continue; } if (depth > innerDepth || !parser.getName().equals("item")) { continue; } a = r.obtainAttributes(attrs, com.android.internal.R.styleable.AnimationDrawableItem); int duration = a.getInt( com.android.internal.R.styleable.AnimationDrawableItem_duration, -1); if (duration < 0) { throw new XmlPullParserException( parser.getPositionDescription() + ":