使用动画的方式有两种:一种是xml形式、另一种是java代码。使用起来都比较简单。还有一种AnimationSet,它是动画集合,将几种动画合在一起使用,下面AnimationSet来写动画。
创建缩放/透明动画
//创建AnimationSet对象
aSet = new AnimationSet(false);
//创建动画对象
sAnim = new ScaleAnimation(1, 0.5f, 1, 0.5f);
//设置动画执行时间
sAnim.setDuration(3000);
//添加动画到集合中
aSet.addAnimation(sAnim);
aAnim = new AlphaAnimation(1, 0.5f);
aAnim.setDuration(3000);
aSet.addAnimation(aAnim);
开始执行动画
aSet.start() ;
btn_sys.setAnimation(aSet) ;
就这样,完成了一个动画集合的小例子,其它几种动画的使用方法类似。
分析源码之前,我们需要知道这4种动画其实都是Animation的子类,而如果想要实现动画效果,则必须重写applyTransformation()方法,这点可以从这个方法的注释可以看出。
/**
* 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) {
}
OK,这里我们分析ScaleAnimation,其它三个可以自行分析。
首先,我们看下构造函数
public ScaleAnimation(float fromX, float toX, float fromY, float toY,
float pivotX, float pivotY) {
mResources = null;
mFromX = fromX;
mToX = toX;
mFromY = fromY;
mToY = toY;
mPivotXType = ABSOLUTE;
mPivotYType = ABSOLUTE;
mPivotXValue = pivotX;
mPivotYValue = pivotY;
initializePivotPoint();
}
源码很简单,只是将传递过来的变量赋值,然后调用initializePivotPoint()方法
private void initializePivotPoint() {
if (mPivotXType == ABSOLUTE) {
mPivotX = mPivotXValue;
}
if (mPivotYType == ABSOLUTE) {
mPivotY = mPivotYValue;
}
}
在构造函数执行完之后,马上就会执行initialize(),获取缩放中心点坐标
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mFromX = resolveScale(mFromX, mFromXType, mFromXData, width, parentWidth);
mToX = resolveScale(mToX, mToXType, mToXData, width, parentWidth);
mFromY = resolveScale(mFromY, mFromYType, mFromYData, height, parentHeight);
mToY = resolveScale(mToY, mToYType, mToYData, height, parentHeight);
mPivotX = resolveSize(mPivotXType, mPivotXValue, width, parentWidth);
mPivotY = resolveSize(mPivotYType, mPivotYValue, height, parentHeight);
}
紧接着,当调用animation.start()方法,
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);
}
这个只是开启设置下时间,重要的是view.setAnimation()这个方法。如果上面的例子代码不书写view.setAnimation(),则不会出现动画效果,而如果书写.start()方法则依然可以执行,只不过再第二次执行时候的是在第一次执行的基础上,不是我们想要的结果。看源码
/**
* Sets the next animation to play for this view.
* If you want the animation to play immediately, use
* {@link #startAnimation(android.view.animation.Animation)} instead.
* This method provides allows fine-grained
* control over the start time and invalidation, but you
* must make sure that 1) the animation has a start time set, and
* 2) the view's parent (which controls animations on its children)
* will be invalidated when the animation is supposed to
* start.
*
* @param animation The next animation, or null.
*/
public void setAnimation(Animation animation) {
mCurrentAnimation = animation;
if (animation != null) {
// If the screen is off assume the animation start time is now instead of
// the next frame we draw. Keeping the START_ON_FIRST_FRAME start time
// would cause the animation to start when the screen turns back on
if (mAttachInfo != null && !mAttachInfo.mScreenOn &&
animation.getStartTime() == Animation.START_ON_FIRST_FRAME) {
animation.setStartTime(AnimationUtils.currentAnimationTimeMillis());
}
animation.reset();
}
}
从注释中可以看出,和start方法调用的是同一个方法。接着执行的是applyTransformation()这个方法,此方法是自行实现的,而且冲该方法的注释可以看出这个方法是是Helper for getTransformation.查看getTransformation()
/**
* 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;
}
这个方法中有一个特别重要的代码: applyTransformation(interpolatedTime, outTransformation);故它会调用applyTransformation方法来通过矩阵实现变换。
上面整体分析了Animation的执行流程,现在就具体来分析下ScaleAnimation是怎么做到缩放的。其实整个缩放动画一共就不到300行代码,而真正起决定作用的又只有几十行代码。
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float sx = 1.0f;
float sy = 1.0f;
float scale = getScaleFactor();
if (mFromX != 1.0f || mToX != 1.0f) {
sx = mFromX + ((mToX - mFromX) * interpolatedTime);
}
if (mFromY != 1.0f || mToY != 1.0f) {
sy = mFromY + ((mToY - mFromY) * interpolatedTime);
}
if (mPivotX == 0 && mPivotY == 0) {
t.getMatrix().setScale(sx, sy);
} else {
t.getMatrix().setScale(sx, sy, scale * mPivotX, scale * mPivotY);
}
}
由源码可知最后动画还是通过矩阵变换来实现的。这里的interpolatedTime表示差值器,这个概念后面会提到,首先,获取缩放比例,然后,再根据不同时间段获取不同的sx值,最后通过矩阵变换来设置。 其实这个方法是会和前面提到过的getTransformation()这个方法一起执行起作用的,两个方法一直执行都动画结束。下面将会写一个Demo来演示这点。
前面使用AnimationSet将ScaleAnimation和AlphaAnimation结合起来,那么我们可不可以自定义一个Animation来实现这个效果呢?答案是肯定的。
a.首先,继承Animation
public class ScaleAndAlphaAnimation extends Animation
b.接着就是利用构造函数将需要的参数传递进来
public ScaleAndAlphaAnimation(float fromX, float toX, float fromY, float toY,float fromAlpha, float toAlpha){
this.mFromX = fromX ;
this.mFromY = fromY ;
this.mToX = toX;
this.mToY = toY ;
this.mFromAlpha = fromAlpha ;
this.mToAlpha = toAlpha ;
System.out.println("ScaleAndAlphaAnimation.ScaleAndAlphaAnimation()");
}
c.最后就是复写applyTransformation方法了,这里我是参照ScaleAnimation和AlphaAnimation源码来写的
@SuppressLint("NewApi") @Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
//缩放动画设置
float sx = 1.0f;
float sy = 1.0f;
float scale = getScaleFactor();
if (mFromX != 1.0f || mToX != 1.0f) {
sx = mFromX + ((mToX - mFromX) * interpolatedTime);
}
if (mFromY != 1.0f || mToY != 1.0f) {
sy = mFromY + ((mToY - mFromY) * interpolatedTime);
}
t.getMatrix().setScale(sx, sy);
//透明度动画设置
final float alpha = mFromAlpha;
t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
System.out.println("ScaleAndAlphaAnimation.applyTransformation()");
// 这在scaleanimation源码中代表缩放中心掉的位置
// if (mPivotX == 0 && mPivotY == 0) {
// t.getMatrix().setScale(sx, sy);
// }
// else {
// t.getMatrix().setScale(sx, sy, scale * mPivotX, scale * mPivotY);
// }
}
全部代码
public class ScaleAndAlphaAnimation extends Animation{
private float mFromX ;
private float mFromY ;
private float mToX ;
private float mToY ;
private float mFromAlpha ;
private float mToAlpha ;
//缩放比例
private float scale = 1 ;
public ScaleAndAlphaAnimation(float fromX, float toX, float fromY, float toY,float fromAlpha, float toAlpha){
this.mFromX = fromX ;
this.mFromY = fromY ;
this.mToX = toX;
this.mToY = toY ;
this.mFromAlpha = fromAlpha ;
this.mToAlpha = toAlpha ;
System.out.println("ScaleAndAlphaAnimation.ScaleAndAlphaAnimation()");
}
@SuppressLint("NewApi") @Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
//缩放动画设置
float sx = 1.0f;
float sy = 1.0f;
float scale = getScaleFactor();
if (mFromX != 1.0f || mToX != 1.0f) {
sx = mFromX + ((mToX - mFromX) * interpolatedTime);
}
if (mFromY != 1.0f || mToY != 1.0f) {
sy = mFromY + ((mToY - mFromY) * interpolatedTime);
}
t.getMatrix().setScale(sx, sy);
//透明度动画设置
final float alpha = mFromAlpha;
t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
System.out.println("ScaleAndAlphaAnimation.applyTransformation()");
// 这在scaleanimation源码中代表缩放中心掉的位置
// if (mPivotX == 0 && mPivotY == 0) {
// t.getMatrix().setScale(sx, sy);
// }
// else {
// t.getMatrix().setScale(sx, sy, scale * mPivotX, scale * mPivotY);
// }
}
@Override
public boolean getTransformation(long currentTime,
Transformation outTransformation) {
System.out.println("ScaleAndAlphaAnimation.getTransformation()");
return super.getTransformation(currentTime, outTransformation);
}
@SuppressLint("NewApi") @Override
protected float getScaleFactor() {
return 0.5f;
}
@Override
public void initialize(int width, int height, int parentWidth,
int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
System.out.println("ScaleAndAlphaAnimation.initialize()");
}
}
log输出如下:从这里可以看出,这几个方法执行的顺序是
构造函数–>initialize()–>接着就是applyTransformation()和getTransformation()的重复执行到动画结束了。(先applyTransformation()后getTransformation())
07-12 06:42:57.958: I/System.out(4992): ScaleAndAlphaAnimation.ScaleAndAlphaAnimation()
07-12 06:42:57.958: I/System.out(4992): ScaleAndAlphaAnimation.initialize()
07-12 06:42:57.958: I/System.out(4992): ScaleAndAlphaAnimation.applyTransformation()
07-12 06:42:57.958: I/System.out(4992): ScaleAndAlphaAnimation.getTransformation()
07-12 06:42:57.958: I/System.out(4992): ScaleAndAlphaAnimation.applyTransformation()
07-12 06:42:57.968: I/System.out(4992): ScaleAndAlphaAnimation.getTransformation()
07-12 06:42:57.968: I/System.out(4992): ScaleAndAlphaAnimation.applyTransformation()
07-12 06:42:57.998: I/System.out(4992): ScaleAndAlphaAnimation.getTransformation()
07-12 06:42:57.998: I/System.out(4992): ScaleAndAlphaAnimation.applyTransformation()
07-12 06:42:58.028: I/System.out(4992): ScaleAndAlphaAnimation.getTransformation()
OK,这篇就介绍到这里,下篇继续分析动画。
源码将会在下篇一起给。