源码
/**
* 可应用于View,Surfaces,Object的抽象动画类
*/
public abstract class Animation implements Cloneable {
/**
* 无限期重复动画
*/
public static final int INFINITE = -1;
/**
* 当动画到达结尾并且重复计数设为INFINITE_REPEAT,或者到达某个固定值时,动画重新开始。
*/
public static final int RESTART = 1;
/**
* 当动画到达结尾并且重复计数设为INFINITE_REPEAT,或者到达某个固定值时,动画倒序执行。
*/
public static final int REVERSE = 2;
/**
* 当getTransformation(long, Transformation)调用第一帧动画时,用来记录当前时间,在短动画中比较有用
*/
public static final int START_ON_FIRST_FRAME = -1;
/**
* 以绝对像素量作为基准尺寸
*/
public static final int ABSOLUTE = 0;
/**
* 指定浮动维度,相对自身变化,应该以一个float类型浮点数乘以自身的width和height
*/
public static final int RELATIVE_TO_SELF = 1;
/**
* 相对于父窗口的变化,应该以float类型乘以父窗口的width和height
*/
public static final int RELATIVE_TO_PARENT = 2;
/**
* 当前正在播放的动画保持Z轴顺序
*/
public static final int ZORDER_NORMAL = 0;
/**
* 指定正在播放的动画位于其他动画上方(在屏幕最前方)
*/
public static final int ZORDER_TOP = 1;
/**
* 指定正在播放的动画位于其他动画下方(在屏幕最后方)
*/
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;
/**
*当重复模式为REVERSE时设置该值
*/
boolean mCycleFlip = false;
/**
* 这个值必须通过initialize函数设置为真,表明动画已经初始化完毕并且可以开始演示
*/
boolean mInitialized = false;
/**
* 当动画播放完毕时是否回到动画执行前的状态,只有mFillEnable为真时才考虑该值的状态,其默认值为真
*/
boolean mFillBefore = true;
/**
*动画执行完后保持在执行完毕状态
*/
boolean mFillAfter = false;
/**
* 表面fillbefore是否应该被考虑
*/
boolean mFillEnabled = false;
/**
* 动画开始时间(毫秒级)
*/
long mStartTime = -1;
/**
* 动画开始后的延迟时间(毫秒级),如果延迟时间>0,那么动画实际的开始时间是startTime+startOffset
*/
long mStartOffset;
/**
* 动画播放时间(毫秒级)
*/
long mDuration;
/**
* 动画重复次数,默认是一直重复
*/
int mRepeatCount = 0;
/**
* 动画已经重复了多少次
*/
int mRepeated = 0;
/**
* 重复模式 RESTART 或者 REVERSE
*/
int mRepeatMode = RESTART;
/**
* Interpolartor接口,
*/
Interpolator mInterpolator;
/**
* 监听动画的开始,结束,重复
*/
AnimationListener mListener;
/**
* 设置Z轴方向的顺序模式
*/
private int mZAdjustment;
/**
* 设置动画背景颜色
*/
private int mBackgroundColor;
/**
* 缩放因子
* 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;
/**
* 声明一个新的动画,持续时间为0,默认的interpolator接口,fillbefore模式
*/
public Animation() {
ensureInterpolator();
}
/**
* 用指定上下文和参数集创建一个动画
* @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;
}
/**
* 重置动画初始状态
* @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;
}
/**
* 取消动画,如果设置了监听器,则监听会通知动画结束的动作。
* 如果手动取消动画,那么下次开始动画之前必须调用reset方法
* @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();
}
}
/**
* 动画是否初始化完成
* @return Has this animation been initialized.
* @see #initialize(int, int, int, int)
*/
public boolean isInitialized() {
return mInitialized;
}
/**
* 初始化动画对象和其父对象的尺寸
*当知道动画尺寸和其父控件尺寸时调用该方法,且需要在getTransformation之前调用
*
* @param width 动画对象的宽度
* @param height 动画对象的高度
* @param parentWidth 父控件的宽度
* @param parentHeight 父控件的高度
*/
public void initialize(int width, int height, int parentWidth, int parentHeight) {
reset();
mInitialized = true;
}
/**
* 设置监听器句柄
* @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;
}
/**
*设置加速曲线,插值器从上下文和资源ID选取
*
* @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));
}
/**
* 设置加速曲线,使用默认的线性插值器
*
* @param i The interpolator which defines the acceleration curve
* @attr ref android.R.styleable#Animation_interpolator
*/
public void setInterpolator(Interpolator i) {
mInterpolator = i;
}
/**
* 设置动画开始的偏移时间,用于多个动画在不同时间开始的复杂情况
*
* @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;
}
/**
* 设置动画持续时间,不能是负值。
*
* @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;
}
/**
* 调整运行时间,确保动画持续时间不会比设置的durationMills还长,同时也确保重复计数的运行时间也小鱼durationMills
*
* @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;
}
}
}
/**
* 按照一定比例缩放运行时间
*
* @param scale The amount to scale the duration.
*/
public void scaleCurrentDuration(float scale) {
mDuration = (long) (mDuration * scale);
mStartOffset = (long) (mStartOffset * scale);
}
/**
* 设置动画的开始时间,如果时间设置为START_ON_FIRST_FRAME,那么动画将在getTransformation调用时第一次启动
*
* @param startTimeMillis the start time in milliseconds
*/
public void setStartTime(long startTimeMillis) {
mStartTime = startTimeMillis;
mStarted = mEnded = false;
mCycleFlip = false;
mRepeated = 0;
mMore = true;
}
/**
* 当动画开始时间是START_ON_FIRST_FRAME时的快捷启动方法
*
*/
public void start() {
setStartTime(-1);
}
/**
* 在当前时间播放动画,播放的一种快捷使用方法
*/
public void startNow() {
setStartTime(AnimationUtils.currentAnimationTimeMillis());
}
/**
* 设置重复模式
*
* @param repeatMode {@link #RESTART} or {@link #REVERSE}
* @attr ref android.R.styleable#Animation_repeatMode
*/
public void setRepeatMode(int repeatMode) {
mRepeatMode = repeatMode;
}
/**
* 设置重复次数,0表示不重复.
*
* @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;
}
/**
* 设置Z方向顺序
*
* @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;
}
/**
* 设置背景颜色
*
* @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 <code>getTransformation</code>. 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;
}
/**
* 是否带墙纸播放
*
* @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;
}
/**
* 获取动画开始播放的时间,如果动画还没有开始播放,返回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;
}
/**
* 获取动画播放的持续时间
*
* @return the duration in milliseconds of the animation
* @attr ref android.R.styleable#Animation_duration
*/
public long getDuration() {
return mDuration;
}
/**
* 获取偏移时间
*
* @return the start offset in milliseconds
* @attr ref android.R.styleable#Animation_startOffset
*/
public long getStartOffset() {
return mStartOffset;
}
/**
* 获取重复模式
*
* @return either one of {@link #REVERSE} or {@link #RESTART}
* @attr ref android.R.styleable#Animation_repeatMode
*/
public int getRepeatMode() {
return mRepeatMode;
}
/**
* 获取重复次数,默认为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;
}
/**
* 指示该动画是否会影响转换矩阵,例如淡出效果的动画不会影响矩阵
*
* @return true if this animation will change the transformation matrix
*/
public boolean willChangeTransformationMatrix() {
// assume we will change the matrix
return true;
}
/**
*
* @return true if this animation will change the view's bounds
*/
public boolean willChangeBounds() {
// assume we will change the bounds
return true;
}
/**
* 设置监听
*
* @param listener the animation listener to be notified
*/
public void setAnimationListener(AnimationListener listener) {
mListener = listener;
}
/**
* 确保动画有一个插值器,如果用户没有设置插值器,该函数将会设置插值器为AccelerateDecelerate
*/
protected void ensureInterpolator() {
if (mInterpolator == null) {
mInterpolator = new AccelerateDecelerateInterpolator();
}
}
/**
* 计算动画可能持续的时间。可以将时间设置为与该时间不同的时间,通常这个计算出来的时间是比较准确的
*/
public long computeDurationHint() {
return (getStartOffset() + getDuration()) * (getRepeatCount() + 1);
}
/**
* 获取当前时间的转换对象。该方法的实现需要一直将当前对象的转换对象赋值给Transformation。
* 将当前时间转换为占durationMills的百分比,传递给Transformation。
*
* @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);
}
/**
* <p>Indicates whether this animation has started or not.</p>
*
* @return true if the animation has started, false otherwise
*/
public boolean hasStarted() {
return mStarted;
}
/**
* <p>Indicates whether this animation has ended or not.</p>
*
* @return true if the animation has ended, false otherwise
*/
public boolean hasEnded() {
return mEnded;
}
/**
* getTransformation助手,子类应该通过给出插值器来实现这个方法.
*
* @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:
* <ol>
* <li>An absolute size. This is represented by a number.</li>
* <li>A size relative to the size of the object being animated. This
* is represented by a number followed by "%".</li> *
* <li>A size relative to the size of the parent of object being
* animated. This is represented by a number followed by "%p".</li>
* </ol>
* @param value The typed value to parse
* @return The parsed version of the description
*/
static Description parseValue(TypedValue value) {
Description d = new Description();
if (value == null) {
d.type = ABSOLUTE;
d.value = 0;
} else {
if (value.type == TypedValue.TYPE_FRACTION) {
d.type = (value.data & TypedValue.COMPLEX_UNIT_MASK) ==
TypedValue.COMPLEX_UNIT_FRACTION_PARENT ?
RELATIVE_TO_PARENT : RELATIVE_TO_SELF;
d.value = TypedValue.complexToFloat(value.data);
return d;
} else if (value.type == TypedValue.TYPE_FLOAT) {
d.type = ABSOLUTE;
d.value = value.getFloat();
return d;
} else if (value.type >= TypedValue.TYPE_FIRST_INT &&
value.type <= TypedValue.TYPE_LAST_INT) {
d.type = ABSOLUTE;
d.value = value.data;
return d;
}
}
d.type = ABSOLUTE;
d.value = 0.0f;
return d;
}
}
/**
* <p>An animation listener receives notifications from an animation.
* Notifications indicate animation related events, such as the end or the
* repetition of the animation.</p>
*/
public static interface AnimationListener {
/**
* <p>Notifies the start of the animation.</p>
*
* @param animation The started animation.
*/
void onAnimationStart(Animation animation);
/**
* <p>Notifies the end of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* @param animation The animation which reached its end.
*/
void onAnimationEnd(Animation animation);
/**
* <p>Notifies the repetition of the animation.</p>
*
* @param animation The animation which was repeated.
*/
void onAnimationRepeat(Animation animation);
}
}
常量:
INFINITE: 无限重复动画
RESTART: 当动画需要重复播放时,正向重复
REVERSE:当动画需要重复播放时,反向重复
START_ON_FIRST_FRAME:可以作为动画开始的时间,当getTransformation第一次被调用时启动动画
ABSOLUTE:指定尺寸为绝对像素
RELATIVE_TO_SELF:指定浮动维度,相对于自身,用float*自身宽高
RELATIVE_TO_PARENT:指定浮动维度,相对于父控件,用float*父控件宽高
ZORDER_NORMAL:正在播放的动画保证正常的z轴顺序
ZORDER_TOP:正在播放的动画在z轴最前端
ZORDER_BOTTOM:正在播放的动画在z轴最后端
BOOL变量
boolean mEnded = false; 动画结束时由getTransformation方法设定
boolean mStarted = false;动画开始时由getTransformation方法设定
boolean mCycleFlip = false;当动画重复模式为REVERSE时,由getTransformation方法设定
boolean mInitialized = false;这个值必须由initialize设置为true,表示动画完成初始化可以播放了
boolean mFillBefore = true;动画播放完成(动画开始播放时)是否回到动画开始播放的那一帧,当mFillEnabled=true时才关注这个值
boolean mFillAfter = false;动画播放完成时保持在最后一帧(保持最后状态)
boolean mFillEnabled = false; 决定mFillBefore是否被考虑
变量
long mStartTime = -1;动画开始的毫秒级时间
long mStartOffset; 动画开始的偏移时间
long mDuration;动画播放的持续时间
int mRepeatCount = 0;动画重复次数,默认为一直重复
int mRepeated = 0;动画已经重复了多少次
int mRepeatMode = RESTART;重复模式,RESTART或者REVERSE
private int mZAdjustment;设置z轴模式
private int mBackgroundColor;动画的背景颜色
private float mScaleFactor = 1f;缩放因子
Interpolator mInterpolator;插值器
AnimationListener mListener;监听器,当动画开始,重复,结束是会得到通知
常用方法
XML:android:DetachWallpaper
相关方法 setDetachWallpaper(Boolean)
是否脱离墙纸运行
XML:android:duration
相关方法 setDuration(long)
设置动画运行时间,不能小于0
XML:android:fillAfter
相关方法 setFillAfter(boolean)
如果为true,动画执行完毕后保留在其执行完毕的状态
XML:android:fillBefore
相关方法 setFillBefore(boolean)
如果为true,动画执行完毕后回到动画执行前的状态
XML:android:fillEnable
相关方法 setFillEnable(boolean)
如果为true,考虑fillBefore的状态
XML:android:interpolator
相关方法 setInterpolator(Interpolator)
设置动画的变化速率
XML:android:repeatCount
相关方法 setRepeatCount(int)
设置重复次数
XML:android:repeatMode
相关方法 setRepeatMode(int)
设置重复模式
相关方法 setStartOffset(long)
设置开始时间偏移量
XML:android:zAdjustment
相关方法 setZAdjustment(int)
设置Z轴顺序模式
常用方法
public boolean getTransformation(long currentTime, Transformation outTransformation)
获取当前时间,并转化为durationMills的百分比,将该百分比传递给Transformation,实现动画
private void reset()
重置动画的初始状态
private void cancel()
取消动画播放,如果设置了监听器,监听器会通知取消状态。如果手动停止动画播放,那么再一次启动播放前,必须调用reset方法。
public boolean isInitialized()
判断初始化是否完成
public void initialize(int width, int height, int parentWidth, int parentHeight)
初始化
width:动画的宽度
height:动画的高度
parentWidth:父控件的宽度
parentHeight:父控件的高度
public void setListenerHandler(Handler handler)
设置监听器句柄
public void restrictDuration(long durationMillis)
确保动画的运行时间小于等于durationMillis,如果大于durationMillis,那么系统会减少重复次数。
public void setStartTime(long startTimeMillis)
设置开始时间,如果时间是START_ON_FIRST_FRAME,调用getTransformation时开始第一次执行
public void start()
将开始时间设为START_ON_FIRST_FRAME的一种快捷写法
public void startNow()
立刻开始
public void setAnimationListener(AnimationListener listener)
设置监听
详细看一下getTransformation 方法
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);
}
}
首先把当前时间转为一个float类型数据,该数据是当前时间占总时间的百分比,然后将该时间传递给Interpolator。该百分比是随时间变化的一个直线型关系,经过插值器转换之后回变成曲线关系,然后将这个转换后的值传递给applyTransformation
applyTransformation
这是一个空方法,具体实现在其子类中,该方法时真正处理动画的过程。看一下其子类的实现方法
RotateAnimation
protected void applyTransformation(float interpolatedTime, Transformation t) {
float degrees = mFromDegrees + ((mToDegrees - mFromDegrees) * interpolatedTime);
float scale = getScaleFactor();
if (mPivotX == 0.0f && mPivotY == 0.0f) {
t.getMatrix().setRotate(degrees);
} else {
t.getMatrix().setRotate(degrees, mPivotX * scale, mPivotY * scale);
}
}
可见applyTransformation是实现动画的主要方法,在执行过程中,系统将以非常高的频率来调用该函数,经过在该函数起始位置和结束位置添加获取时间函数,可发现系统每16ms调用一次。
XML:android:fillAfter
相关方法 setFillAfter(boolean)
如果为true,动画执行完毕后保留在其执行完毕的状态
XML:android:fillAfter
相关方法 setFillAfter(boolean)
如果为true,动画执行完毕后保留在其执行完毕的状态