/* * 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.view.animation; import android.content.Context; import android.content.res.TypedArray; import android.graphics.RectF; import android.os.Handler; import android.os.SystemProperties; import android.util.AttributeSet; import android.util.TypedValue; import dalvik.system.CloseGuard; /** * Abstraction for an Animation that can be applied to Views, Surfaces, or * other objects. See the {@link android.view.animation animation package * description file}. */ public abstract class Animation implements Cloneable { /** * Repeat the animation indefinitely. */ public static final int INFINITE = -1; /** * When the animation reaches the end and the repeat count is INFINTE_REPEAT * or a positive value, the animation restarts from the beginning. */ public static final int RESTART = 1; /** * When the animation reaches the end and the repeat count is INFINTE_REPEAT * or a positive value, the animation plays backward (and then forward again). */ public static final int REVERSE = 2; /** * Can be used as the start time to indicate the start time should be the current * time when {@link #getTransformation(long, Transformation)} is invoked for the * first animation frame. This can is useful for short animations. */ public static final int START_ON_FIRST_FRAME = -1; /** * The specified dimension is an absolute number of pixels. */ public static final int ABSOLUTE = 0; /** * The specified dimension holds a float and should be multiplied by the * height or width of the object being animated. */ public static final int RELATIVE_TO_SELF = 1; /** * The specified dimension holds a float and should be multiplied by the * height or width of the parent of the object being animated. */ public static final int RELATIVE_TO_PARENT = 2; /** * Requests that the content being animated be kept in its current Z * order. */ public static final int ZORDER_NORMAL = 0; /** * Requests that the content being animated be forced on top of all other * content for the duration of the animation. */ public static final int ZORDER_TOP = 1; /** * Requests that the content being animated be forced under all other * content for the duration of the animation. */ public static final int ZORDER_BOTTOM = -1; private static final boolean USE_CLOSEGUARD = SystemProperties.getBoolean("log.closeguard.Animation", false); /** * Set by {@link #getTransformation(long, Transformation)} when the animation ends. */ boolean mEnded = false; /** * Set by {@link #getTransformation(long, Transformation)} when the animation starts. */ boolean mStarted = false; /** * Set by {@link #getTransformation(long, Transformation)} when the animation repeats * in REVERSE mode. */ boolean mCycleFlip = false; /** * This value must be set to true by {@link #initialize(int, int, int, int)}. It * indicates the animation was successfully initialized and can be played. */ boolean mInitialized = false; /** * Indicates whether the animation transformation should be applied before the * animation starts. The value of this variable is only relevant if mFillEnabled is true; * otherwise it is assumed to be true. */ boolean mFillBefore = true; /** * Indicates whether the animation transformation should be applied after the * animation ends. */ boolean mFillAfter = false; /** * Indicates whether fillBefore should be taken into account. */ boolean mFillEnabled = false; /** * The time in milliseconds at which the animation must start; */ long mStartTime = -1; /** * The delay in milliseconds after which the animation must start. When the * start offset is > 0, the start time of the animation is startTime + startOffset. */ long mStartOffset; /** * The duration of one animation cycle in milliseconds. */ long mDuration; /** * The number of times the animation must repeat. By default, an animation repeats * indefinitely. */ int mRepeatCount = 0; /** * Indicates how many times the animation was repeated. */ int mRepeated = 0; /** * The behavior of the animation when it repeats. The repeat mode is either * {@link #RESTART} or {@link #REVERSE}. * */ int mRepeatMode = RESTART; /** * The interpolator used by the animation to smooth the movement. */ Interpolator mInterpolator; /** * The animation listener to be notified when the animation starts, ends or repeats. */ AnimationListener mListener; /** * Desired Z order mode during animation. */ private int mZAdjustment; /** * Desired background color behind animation. */ private int mBackgroundColor; /** * scalefactor to apply to pivot points, etc. during animation. Subclasses retrieve the * value via getScaleFactor(). */ private float mScaleFactor = 1f; /** * Don't animate the wallpaper. */ private boolean mDetachWallpaper = false; private boolean mMore = true; private boolean mOneMoreTime = true; RectF mPreviousRegion = new RectF(); RectF mRegion = new RectF(); Transformation mTransformation = new Transformation(); Transformation mPreviousTransformation = new Transformation(); private final CloseGuard guard = CloseGuard.get(); private Handler mListenerHandler; private Runnable mOnStart; private Runnable mOnRepeat; private Runnable mOnEnd; /** * Creates a new animation with a duration of 0ms, the default interpolator, with * fillBefore set to true and fillAfter set to false */ public Animation() { ensureInterpolator(); } /** * Creates a new animation whose parameters come from the specified context and * attributes set. * * @param context the application environment * @param attrs the set of attributes holding the animation parameters */ public Animation(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animation); setDuration((long) a.getInt(com.android.internal.R.styleable.Animation_duration, 0)); setStartOffset((long) a.getInt(com.android.internal.R.styleable.Animation_startOffset, 0)); setFillEnabled(a.getBoolean(com.android.internal.R.styleable.Animation_fillEnabled, mFillEnabled)); setFillBefore(a.getBoolean(com.android.internal.R.styleable.Animation_fillBefore, mFillBefore)); setFillAfter(a.getBoolean(com.android.internal.R.styleable.Animation_fillAfter, mFillAfter)); setRepeatCount(a.getInt(com.android.internal.R.styleable.Animation_repeatCount, mRepeatCount)); setRepeatMode(a.getInt(com.android.internal.R.styleable.Animation_repeatMode, RESTART)); setZAdjustment(a.getInt(com.android.internal.R.styleable.Animation_zAdjustment, ZORDER_NORMAL)); setBackgroundColor(a.getInt(com.android.internal.R.styleable.Animation_background, 0)); setDetachWallpaper(a.getBoolean(com.android.internal.R.styleable.Animation_detachWallpaper, false)); final int resID = a.getResourceId(com.android.internal.R.styleable.Animation_interpolator, 0); a.recycle(); if (resID > 0) { setInterpolator(context, resID); } ensureInterpolator(); } @Override protected Animation clone() throws CloneNotSupportedException { final Animation animation = (Animation) super.clone(); animation.mPreviousRegion = new RectF(); animation.mRegion = new RectF(); animation.mTransformation = new Transformation(); animation.mPreviousTransformation = new Transformation(); return animation; } /** * Reset the initialization state of this animation. * * @see #initialize(int, int, int, int) */ public void reset() { mPreviousRegion.setEmpty(); mPreviousTransformation.clear(); mInitialized = false; mCycleFlip = false; mRepeated = 0; mMore = true; mOneMoreTime = true; mListenerHandler = null; } /** * Cancel the animation. Cancelling an animation invokes the animation * listener, if set, to notify the end of the animation. * * If you cancel an animation manually, you must call {@link #reset()} * before starting the animation again. * * @see #reset() * @see #start() * @see #startNow() */ public void cancel() { if (mStarted && !mEnded) { fireAnimationEnd(); mEnded = true; guard.close(); } // Make sure we move the animation to the end mStartTime = Long.MIN_VALUE; mMore = mOneMoreTime = false; } /** * @hide */ public void detach() { if (mStarted && !mEnded) { mEnded = true; guard.close(); fireAnimationEnd(); } } /** * Whether or not the animation has been initialized. * * @return Has this animation been initialized. * @see #initialize(int, int, int, int) */ public boolean isInitialized() { return mInitialized; } /** * Initialize this animation with the dimensions of the object being * animated as well as the objects parents. (This is to support animation * sizes being specified relative to these dimensions.) * *
Objects that interpret Animations should call this method when
* the sizes of the object being animated and its parent are known, and
* before calling {@link #getTransformation}.
*
*
* @param width Width of the object being animated
* @param height Height of the object being animated
* @param parentWidth Width of the animated object's parent
* @param parentHeight Height of the animated object's parent
*/
public void initialize(int width, int height, int parentWidth, int parentHeight) {
reset();
mInitialized = true;
}
/**
* Sets the handler used to invoke listeners.
*
* @hide
*/
public void setListenerHandler(Handler handler) {
if (mListenerHandler == null) {
mOnStart = new Runnable() {
public void run() {
if (mListener != null) {
mListener.onAnimationStart(Animation.this);
}
}
};
mOnRepeat = new Runnable() {
public void run() {
if (mListener != null) {
mListener.onAnimationRepeat(Animation.this);
}
}
};
mOnEnd = new Runnable() {
public void run() {
if (mListener != null) {
mListener.onAnimationEnd(Animation.this);
}
}
};
}
mListenerHandler = handler;
}
/**
* Sets the acceleration curve for this animation. The interpolator is loaded as
* a resource from the specified context.
*
* @param context The application environment
* @param resID The resource identifier of the interpolator to load
* @attr ref android.R.styleable#Animation_interpolator
*/
public void setInterpolator(Context context, int resID) {
setInterpolator(AnimationUtils.loadInterpolator(context, resID));
}
/**
* Sets the acceleration curve for this animation. Defaults to a linear
* interpolation.
*
* @param i The interpolator which defines the acceleration curve
* @attr ref android.R.styleable#Animation_interpolator
*/
public void setInterpolator(Interpolator i) {
mInterpolator = i;
}
/**
* When this animation should start relative to the start time. This is most
* useful when composing complex animations using an {@link AnimationSet }
* where some of the animations components start at different times.
*
* @param startOffset When this Animation should start, in milliseconds from
* the start time of the root AnimationSet.
* @attr ref android.R.styleable#Animation_startOffset
*/
public void setStartOffset(long startOffset) {
mStartOffset = startOffset;
}
/**
* How long this animation should last. The duration cannot be negative.
*
* @param durationMillis Duration in milliseconds
*
* @throws java.lang.IllegalArgumentException if the duration is < 0
*
* @attr ref android.R.styleable#Animation_duration
*/
public void setDuration(long durationMillis) {
if (durationMillis < 0) {
throw new IllegalArgumentException("Animation duration cannot be negative");
}
mDuration = durationMillis;
}
/**
* Ensure that the duration that this animation will run is not longer
* than durationMillis. In addition to adjusting the duration
* itself, this ensures that the repeat count also will not make it run
* longer than the given time.
*
* @param durationMillis The maximum duration the animation is allowed
* to run.
*/
public void restrictDuration(long durationMillis) {
// If we start after the duration, then we just won't run.
if (mStartOffset > durationMillis) {
mStartOffset = durationMillis;
mDuration = 0;
mRepeatCount = 0;
return;
}
long dur = mDuration + mStartOffset;
if (dur > durationMillis) {
mDuration = durationMillis-mStartOffset;
dur = durationMillis;
}
// If the duration is 0 or less, then we won't run.
if (mDuration <= 0) {
mDuration = 0;
mRepeatCount = 0;
return;
}
// Reduce the number of repeats to keep below the maximum duration.
// The comparison between mRepeatCount and duration is to catch
// overflows after multiplying them.
if (mRepeatCount < 0 || mRepeatCount > durationMillis
|| (dur*mRepeatCount) > durationMillis) {
// Figure out how many times to do the animation. Subtract 1 since
// repeat count is the number of times to repeat so 0 runs once.
mRepeatCount = (int)(durationMillis/dur) - 1;
if (mRepeatCount < 0) {
mRepeatCount = 0;
}
}
}
/**
* How much to scale the duration by.
*
* @param scale The amount to scale the duration.
*/
public void scaleCurrentDuration(float scale) {
mDuration = (long) (mDuration * scale);
mStartOffset = (long) (mStartOffset * scale);
}
/**
* When this animation should start. When the start time is set to
* {@link #START_ON_FIRST_FRAME}, the animation will start the first time
* {@link #getTransformation(long, Transformation)} is invoked. The time passed
* to this method should be obtained by calling
* {@link AnimationUtils#currentAnimationTimeMillis()} instead of
* {@link System#currentTimeMillis()}.
*
* @param startTimeMillis the start time in milliseconds
*/
public void setStartTime(long startTimeMillis) {
mStartTime = startTimeMillis;
mStarted = mEnded = false;
mCycleFlip = false;
mRepeated = 0;
mMore = true;
}
/**
* Convenience method to start the animation the first time
* {@link #getTransformation(long, Transformation)} is invoked.
*/
public void start() {
setStartTime(-1);
}
/**
* Convenience method to start the animation at the current time in
* milliseconds.
*/
public void startNow() {
setStartTime(AnimationUtils.currentAnimationTimeMillis());
}
/**
* Defines what this animation should do when it reaches the end. This
* setting is applied only when the repeat count is either greater than
* 0 or {@link #INFINITE}. Defaults to {@link #RESTART}.
*
* @param repeatMode {@link #RESTART} or {@link #REVERSE}
* @attr ref android.R.styleable#Animation_repeatMode
*/
public void setRepeatMode(int repeatMode) {
mRepeatMode = repeatMode;
}
/**
* Sets how many times the animation should be repeated. If the repeat
* count is 0, the animation is never repeated. If the repeat count is
* greater than 0 or {@link #INFINITE}, the repeat mode will be taken
* into account. The repeat count is 0 by default.
*
* @param repeatCount the number of times the animation should be repeated
* @attr ref android.R.styleable#Animation_repeatCount
*/
public void setRepeatCount(int repeatCount) {
if (repeatCount < 0) {
repeatCount = INFINITE;
}
mRepeatCount = repeatCount;
}
/**
* If fillEnabled is true, this animation will apply the value of fillBefore.
*
* @return true if the animation will take fillBefore into account
* @attr ref android.R.styleable#Animation_fillEnabled
*/
public boolean isFillEnabled() {
return mFillEnabled;
}
/**
* If fillEnabled is true, the animation will apply the value of fillBefore.
* Otherwise, fillBefore is ignored and the animation
* transformation is always applied until the animation ends.
*
* @param fillEnabled true if the animation should take the value of fillBefore into account
* @attr ref android.R.styleable#Animation_fillEnabled
*
* @see #setFillBefore(boolean)
* @see #setFillAfter(boolean)
*/
public void setFillEnabled(boolean fillEnabled) {
mFillEnabled = fillEnabled;
}
/**
* If fillBefore is true, this animation will apply its transformation
* before the start time of the animation. Defaults to true if
* {@link #setFillEnabled(boolean)} is not set to true.
* Note that this applies when using an {@link
* android.view.animation.AnimationSet AnimationSet} to chain
* animations. The transformation is not applied before the AnimationSet
* itself starts.
*
* @param fillBefore true if the animation should apply its transformation before it starts
* @attr ref android.R.styleable#Animation_fillBefore
*
* @see #setFillEnabled(boolean)
*/
public void setFillBefore(boolean fillBefore) {
mFillBefore = fillBefore;
}
/**
* If fillAfter is true, the transformation that this animation performed
* will persist when it is finished. Defaults to false if not set.
* Note that this applies to individual animations and when using an {@link
* android.view.animation.AnimationSet AnimationSet} to chain
* animations.
*
* @param fillAfter true if the animation should apply its transformation after it ends
* @attr ref android.R.styleable#Animation_fillAfter
*
* @see #setFillEnabled(boolean)
*/
public void setFillAfter(boolean fillAfter) {
mFillAfter = fillAfter;
}
/**
* Set the Z ordering mode to use while running the animation.
*
* @param zAdjustment The desired mode, one of {@link #ZORDER_NORMAL},
* {@link #ZORDER_TOP}, or {@link #ZORDER_BOTTOM}.
* @attr ref android.R.styleable#Animation_zAdjustment
*/
public void setZAdjustment(int zAdjustment) {
mZAdjustment = zAdjustment;
}
/**
* Set background behind animation.
*
* @param bg The background color. If 0, no background. Currently must
* be black, with any desired alpha level.
*/
public void setBackgroundColor(int bg) {
mBackgroundColor = bg;
}
/**
* The scale factor is set by the call to getTransformation
. Overrides of
* {@link #getTransformation(long, Transformation, float)} will get this value
* directly. Overrides of {@link #applyTransformation(float, Transformation)} can
* call this method to get the value.
*
* @return float The scale factor that should be applied to pre-scaled values in
* an Animation such as the pivot points in {@link ScaleAnimation} and {@link RotateAnimation}.
*/
protected float getScaleFactor() {
return mScaleFactor;
}
/**
* If detachWallpaper is true, and this is a window animation of a window
* that has a wallpaper background, then the window will be detached from
* the wallpaper while it runs. That is, the animation will only be applied
* to the window, and the wallpaper behind it will remain static.
*
* @param detachWallpaper true if the wallpaper should be detached from the animation
* @attr ref android.R.styleable#Animation_detachWallpaper
*/
public void setDetachWallpaper(boolean detachWallpaper) {
mDetachWallpaper = detachWallpaper;
}
/**
* Gets the acceleration curve type for this animation.
*
* @return the {@link Interpolator} associated to this animation
* @attr ref android.R.styleable#Animation_interpolator
*/
public Interpolator getInterpolator() {
return mInterpolator;
}
/**
* When this animation should start. If the animation has not startet yet,
* this method might return {@link #START_ON_FIRST_FRAME}.
*
* @return the time in milliseconds when the animation should start or
* {@link #START_ON_FIRST_FRAME}
*/
public long getStartTime() {
return mStartTime;
}
/**
* How long this animation should last
*
* @return the duration in milliseconds of the animation
* @attr ref android.R.styleable#Animation_duration
*/
public long getDuration() {
return mDuration;
}
/**
* When this animation should start, relative to StartTime
*
* @return the start offset in milliseconds
* @attr ref android.R.styleable#Animation_startOffset
*/
public long getStartOffset() {
return mStartOffset;
}
/**
* Defines what this animation should do when it reaches the end.
*
* @return either one of {@link #REVERSE} or {@link #RESTART}
* @attr ref android.R.styleable#Animation_repeatMode
*/
public int getRepeatMode() {
return mRepeatMode;
}
/**
* Defines how many times the animation should repeat. The default value
* is 0.
*
* @return the number of times the animation should repeat, or {@link #INFINITE}
* @attr ref android.R.styleable#Animation_repeatCount
*/
public int getRepeatCount() {
return mRepeatCount;
}
/**
* If fillBefore is true, this animation will apply its transformation
* before the start time of the animation. If fillBefore is false and
* {@link #isFillEnabled() fillEnabled} is true, the transformation will not be applied until
* the start time of the animation.
*
* @return true if the animation applies its transformation before it starts
* @attr ref android.R.styleable#Animation_fillBefore
*/
public boolean getFillBefore() {
return mFillBefore;
}
/**
* If fillAfter is true, this animation will apply its transformation
* after the end time of the animation.
*
* @return true if the animation applies its transformation after it ends
* @attr ref android.R.styleable#Animation_fillAfter
*/
public boolean getFillAfter() {
return mFillAfter;
}
/**
* Returns the Z ordering mode to use while running the animation as
* previously set by {@link #setZAdjustment}.
*
* @return Returns one of {@link #ZORDER_NORMAL},
* {@link #ZORDER_TOP}, or {@link #ZORDER_BOTTOM}.
* @attr ref android.R.styleable#Animation_zAdjustment
*/
public int getZAdjustment() {
return mZAdjustment;
}
/**
* Returns the background color behind the animation.
*/
public int getBackgroundColor() {
return mBackgroundColor;
}
/**
* Return value of {@link #setDetachWallpaper(boolean)}.
* @attr ref android.R.styleable#Animation_detachWallpaper
*/
public boolean getDetachWallpaper() {
return mDetachWallpaper;
}
/**
*
Indicates whether or not this animation will affect the transformation * matrix. For instance, a fade animation will not affect the matrix whereas * a scale animation will.
* * @return true if this animation will change the transformation matrix */ public boolean willChangeTransformationMatrix() { // assume we will change the matrix return true; } /** *Indicates whether or not this animation will affect the bounds of the * animated view. For instance, a fade animation will not affect the bounds * whereas a 200% scale animation will.
* * @return true if this animation will change the view's bounds */ public boolean willChangeBounds() { // assume we will change the bounds return true; } /** *Binds an animation listener to this animation. The animation listener * is notified of animation events such as the end of the animation or the * repetition of the animation.
* * @param listener the animation listener to be notified */ public void setAnimationListener(AnimationListener listener) { mListener = listener; } /** * Gurantees that this animation has an interpolator. Will use * a AccelerateDecelerateInterpolator is nothing else was specified. */ protected void ensureInterpolator() { if (mInterpolator == null) { mInterpolator = new AccelerateDecelerateInterpolator(); } } /** * Compute a hint at how long the entire animation may last, in milliseconds. * Animations can be written to cause themselves to run for a different * duration than what is computed here, but generally this should be * accurate. */ public long computeDurationHint() { return (getStartOffset() + getDuration()) * (getRepeatCount() + 1); } /** * Gets the transformation to apply at a specified point in time. Implementations of this * method should always replace the specified Transformation or document they are doing * otherwise. * * @param currentTime Where we are in the animation. This is wall clock time. * @param outTransformation A transformation object that is provided by the * caller and will be filled in by the animation. * @return True if the animation is still running */ public boolean getTransformation(long currentTime, Transformation outTransformation) { if (mStartTime == -1) { mStartTime = currentTime; } final long startOffset = getStartOffset(); final long duration = mDuration; float normalizedTime; if (duration != 0) { normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) / (float) duration; } else { // time is a step-change with a zero duration normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f; } final boolean expired = normalizedTime >= 1.0f; mMore = !expired; if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f); if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) { if (!mStarted) { fireAnimationStart(); mStarted = true; if (USE_CLOSEGUARD) { guard.open("cancel or detach or getTransformation"); } } if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f); if (mCycleFlip) { normalizedTime = 1.0f - normalizedTime; } final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime); applyTransformation(interpolatedTime, outTransformation); } if (expired) { if (mRepeatCount == mRepeated) { if (!mEnded) { mEnded = true; guard.close(); fireAnimationEnd(); } } else { if (mRepeatCount > 0) { mRepeated++; } if (mRepeatMode == REVERSE) { mCycleFlip = !mCycleFlip; } mStartTime = -1; mMore = true; fireAnimationRepeat(); } } if (!mMore && mOneMoreTime) { mOneMoreTime = false; return true; } return mMore; } private void fireAnimationStart() { if (mListener != null) { if (mListenerHandler == null) mListener.onAnimationStart(this); else mListenerHandler.postAtFrontOfQueue(mOnStart); } } private void fireAnimationRepeat() { if (mListener != null) { if (mListenerHandler == null) mListener.onAnimationRepeat(this); else mListenerHandler.postAtFrontOfQueue(mOnRepeat); } } private void fireAnimationEnd() { if (mListener != null) { if (mListenerHandler == null) mListener.onAnimationEnd(this); else mListenerHandler.postAtFrontOfQueue(mOnEnd); } } /** * Gets the transformation to apply at a specified point in time. Implementations of this * method should always replace the specified Transformation or document they are doing * otherwise. * * @param currentTime Where we are in the animation. This is wall clock time. * @param outTransformation A transformation object that is provided by the * caller and will be filled in by the animation. * @param scale Scaling factor to apply to any inputs to the transform operation, such * pivot points being rotated or scaled around. * @return True if the animation is still running */ public boolean getTransformation(long currentTime, Transformation outTransformation, float scale) { mScaleFactor = scale; return getTransformation(currentTime, outTransformation); } /** *Indicates whether this animation has started or not.
* * @return true if the animation has started, false otherwise */ public boolean hasStarted() { return mStarted; } /** *Indicates whether this animation has ended or not.
* * @return true if the animation has ended, false otherwise */ public boolean hasEnded() { return mEnded; } /** * Helper for getTransformation. Subclasses should implement this to apply * their transforms given an interpolation value. Implementations of this * method should always replace the specified Transformation or document * they are doing otherwise. * * @param interpolatedTime The value of the normalized time (0.0 to 1.0) * after it has been run through the interpolation function. * @param t The Transformation object to fill in with the current * transforms. */ protected void applyTransformation(float interpolatedTime, Transformation t) { } /** * Convert the information in the description of a size to an actual * dimension * * @param type One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or * Animation.RELATIVE_TO_PARENT. * @param value The dimension associated with the type parameter * @param size The size of the object being animated * @param parentSize The size of the parent of the object being animated * @return The dimension to use for the animation */ protected float resolveSize(int type, float value, int size, int parentSize) { switch (type) { case ABSOLUTE: return value; case RELATIVE_TO_SELF: return size * value; case RELATIVE_TO_PARENT: return parentSize * value; default: return value; } } /** * @param left * @param top * @param right * @param bottom * @param invalidate * @param transformation * * @hide */ public void getInvalidateRegion(int left, int top, int right, int bottom, RectF invalidate, Transformation transformation) { final RectF tempRegion = mRegion; final RectF previousRegion = mPreviousRegion; invalidate.set(left, top, right, bottom); transformation.getMatrix().mapRect(invalidate); // Enlarge the invalidate region to account for rounding errors invalidate.inset(-1.0f, -1.0f); tempRegion.set(invalidate); invalidate.union(previousRegion); previousRegion.set(tempRegion); final Transformation tempTransformation = mTransformation; final Transformation previousTransformation = mPreviousTransformation; tempTransformation.set(transformation); transformation.set(previousTransformation); previousTransformation.set(tempTransformation); } /** * @param left * @param top * @param right * @param bottom * * @hide */ public void initializeInvalidateRegion(int left, int top, int right, int bottom) { final RectF region = mPreviousRegion; region.set(left, top, right, bottom); // Enlarge the invalidate region to account for rounding errors region.inset(-1.0f, -1.0f); if (mFillBefore) { final Transformation previousTransformation = mPreviousTransformation; applyTransformation(mInterpolator.getInterpolation(0.0f), previousTransformation); } } protected void finalize() throws Throwable { try { if (guard != null) { guard.warnIfOpen(); } } finally { super.finalize(); } } /** * Return true if this animation changes the view's alpha property. * * @hide */ public boolean hasAlpha() { return false; } /** * Utility class to parse a string description of a size. */ protected static class Description { /** * One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or * Animation.RELATIVE_TO_PARENT. */ public int type; /** * The absolute or relative dimension for this Description. */ public float value; /** * Size descriptions can appear inthree forms: *An animation listener receives notifications from an animation. * Notifications indicate animation related events, such as the end or the * repetition of the animation.
*/ public static interface AnimationListener { /** *Notifies the start of the animation.
* * @param animation The started animation. */ void onAnimationStart(Animation animation); /** *Notifies the end of the animation. This callback is not invoked * for animations with repeat count set to INFINITE.
* * @param animation The animation which reached its end. */ void onAnimationEnd(Animation animation); /** *Notifies the repetition of the animation.
* * @param animation The animation which was repeated. */ void onAnimationRepeat(Animation animation); } }