示例
博客
補(bǔ)間動(dòng)畫(huà)的使用很簡(jiǎn)單,如下面代碼,讓圖片旋轉(zhuǎn)360度:
animation = new RotateAnimation(0,360);
animation.setDuration(3000);
iv.startAnimation(animation);

那么補(bǔ)間動(dòng)畫(huà)說(shuō)怎么執(zhí)行的,插值器又是怎么用上的能?
動(dòng)畫(huà)的啟動(dòng) View
從動(dòng)畫(huà)啟動(dòng)開(kāi)始吧,看View的startAnimation方法:
/frameworks/base/core/java/android/view/View.java:
public void startAnimation(Animation animation) {
animation.setStartTime(Animation.START_ON_FIRST_FRAME);
setAnimation(animation);
invalidateParentCaches();
invalidate(true);
}
public void setAnimation(Animation animation) {
mCurrentAnimation = animation;
if (animation != null) {
......
}
}
這里面沒(méi)幾行代碼,首先把動(dòng)畫(huà)設(shè)置給內(nèi)部的一個(gè)變量,然后調(diào)用invalidate(true)方法。
這個(gè)方法會(huì)引起View的draw()方法的執(zhí)行,并且是整個(gè)View的重繪。
/frameworks/base/core/java/android/view/View.java:
public Animation getAnimation() {
return mCurrentAnimation;
}
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
Transformation transformToApply = null;
boolean concatMatrix = false;
//拿到動(dòng)畫(huà)
final Animation a = getAnimation();
//動(dòng)畫(huà)不為空,說(shuō)明有動(dòng)畫(huà)要執(zhí)行
if (a != null) {
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
concatMatrix = a.willChangeTransformationMatrix();
if (concatMatrix) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
//獲取Transformation,里面包換透明度和矩陣,這個(gè)Transformation的值在上面已經(jīng)設(shè)置好了
transformToApply = parent.getChildTransformation();
} else {
}
//根據(jù)Transformation進(jìn)行繪制。
return more;
}
調(diào)用本類的applyLegacyAnimation方法。
/frameworks/base/core/java/android/view/View.java:
private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime,
Animation a, boolean scalingRequired) {
Transformation invalidationTransform;
final int flags = parent.mGroupFlags;
final boolean initialized = a.isInitialized();
if (!initialized) {
//初始化動(dòng)畫(huà)
a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight());
a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop);
if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler);
onAnimationStart();
}
//注意這里也是通過(guò)這個(gè)方法拿到一個(gè)Transformation。
final Transformation t = parent.getChildTransformation();
//獲取動(dòng)畫(huà)的變化值,并返回是否還有下一幀。
boolean more = a.getTransformation(drawingTime, t, 1f);
if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
if (parent.mInvalidationTransformation == null) {
parent.mInvalidationTransformation = new Transformation();
}
invalidationTransform = parent.mInvalidationTransformation;
a.getTransformation(drawingTime, invalidationTransform, 1f);
} else {
invalidationTransform = t;
}
if (more) {
//如果有下一幀,就繼續(xù)刷新繪制
}
return more;
}
動(dòng)畫(huà)的動(dòng)畫(huà)值計(jì)算設(shè)置
/frameworks/base/core/java/android/view/animation/Animation.java:
public boolean getTransformation(long currentTime, Transformation outTransformation) {
if (mStartTime == -1) {
mStartTime = currentTime;
}
//轉(zhuǎn)化為標(biāo)準(zhǔn)時(shí)間
final long startOffset = getStartOffset();
final long duration = mDuration;
float normalizedTime;
if (duration != 0) {
normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
(float) duration;
}
if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
......
//通過(guò)標(biāo)準(zhǔn)時(shí)間,用插值器計(jì)算插值器轉(zhuǎn)換之后的值
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
//調(diào)用子類實(shí)現(xiàn)的方法,
applyTransformation(interpolatedTime, outTransformation);
}
return mMore;
}
protected void applyTransformation(float interpolatedTime, Transformation t) {
}
首先計(jì)算標(biāo)準(zhǔn)時(shí)間。標(biāo)準(zhǔn)時(shí)間是0到1的值,表示時(shí)間的進(jìn)度。通過(guò)這個(gè)進(jìn)度計(jì)算動(dòng)畫(huà)的進(jìn)度。計(jì)算方法是,先用開(kāi)始時(shí)間和延遲開(kāi)始時(shí)間計(jì)算動(dòng)畫(huà)真正的開(kāi)始時(shí)間,然后用當(dāng)前時(shí)間減去動(dòng)畫(huà)真正開(kāi)始的時(shí)間,算出動(dòng)畫(huà)已經(jīng)運(yùn)行的時(shí)間。用這個(gè)時(shí)間除以動(dòng)畫(huà)的總運(yùn)行時(shí)間久得到當(dāng)前動(dòng)畫(huà)的進(jìn)度。
插值器
插值器改變的就是改變不同時(shí)間進(jìn)度上的值,時(shí)間的流逝是線性的,速度是不變的,但是插值器通過(guò)改變不同時(shí)間上動(dòng)畫(huà)的值,達(dá)到控制動(dòng)畫(huà)的目的。
默認(rèn)是加速加速插值器,里面是一個(gè)余弦曲線,隨著標(biāo)準(zhǔn)時(shí)間從0到1,返回的數(shù)值是先加速再減速的,動(dòng)畫(huà)就會(huì)先變快在變慢,第一個(gè)效果圖中看的很明顯。
/frameworks/base/core/java/android/view/animation/AccelerateDecelerateInterpolator.java:
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
下面可以看一下線性插值器,他就原原本本的返回了標(biāo)準(zhǔn)時(shí)間,不做任何改變,所以動(dòng)畫(huà)就會(huì)勻速執(zhí)行:
/frameworks/base/core/java/android/view/animation/LinearInterpolator.java
public float getInterpolation(float input) {
return input;
}
Animation的子類實(shí)現(xiàn)applyTransformation方法
Animation的applyTransformation方法是一個(gè)空方法,需要子類去實(shí)現(xiàn)。下面看一啊AlphaAnimation的實(shí)現(xiàn),現(xiàn)獲取透明度總共要變化的值,然后通過(guò)傳進(jìn)來(lái)的插值器計(jì)算出的進(jìn)度值,算出這個(gè)時(shí)間點(diǎn)上透明度應(yīng)該是多少,然后設(shè)置給Transformation:
/Users/sunlinlin/Documents/AndroidSourcePart/frameworks/base/core/java/android/view/animation/AlphaAnimation.java
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float alpha = mFromAlpha;
t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
}
AlphaAnimation里給傳進(jìn)來(lái)的Transformation設(shè)置了alpha值。Transformation主要有兩個(gè)量,一個(gè)透明度,一個(gè)是矩陣。繪制時(shí)根據(jù)這個(gè)類里面存儲(chǔ)的量來(lái)繪制,達(dá)到動(dòng)畫(huà)的效果。
例如旋轉(zhuǎn)動(dòng)畫(huà)RotateAnimation,改變的就是矩陣,先算出動(dòng)畫(huà)一共要旋轉(zhuǎn)的角度,然后根據(jù)插值器計(jì)算的進(jìn)度值算出當(dāng)前時(shí)間點(diǎn)上應(yīng)該旋轉(zhuǎn)到什么角度,然后設(shè)置給Transformation。
/frameworks/base/core/java/android/view/animation/RotateAnimation.java
@Override
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);
}
}
這時(shí),回到上面View的draw()方法里,就會(huì)根據(jù)后面對(duì)Transformation的設(shè)置進(jìn)行畫(huà)面的繪制,一幀一幀的繪制就成了動(dòng)畫(huà)。
這也能解釋為什么補(bǔ)間動(dòng)畫(huà)不會(huì)改變控件的真正位置了,因?yàn)檫@個(gè)動(dòng)畫(huà)只是重新對(duì)空間進(jìn)行了draw,改變的只是看起來(lái)的樣子,所以點(diǎn)擊事件還得點(diǎn)擊原來(lái)的地方。
簡(jiǎn)單的自定義插值器
插值器前面說(shuō)的作用就是,在決定不同時(shí)間進(jìn)度上的動(dòng)畫(huà)進(jìn)度。時(shí)間進(jìn)度是從0到1,而動(dòng)畫(huà)進(jìn)度不一定非要從0到1.
比如,就已開(kāi)始設(shè)置那個(gè)動(dòng)畫(huà),3秒時(shí)間從0度旋轉(zhuǎn)到360度,那么正常的他的動(dòng)畫(huà)進(jìn)度就是:時(shí)間從0到3秒,角度從0到360.
動(dòng)畫(huà)的進(jìn)度是可以再插值器中隨便設(shè)置的,大于1也沒(méi)可以。
下面是例子
自定義一個(gè)插值器
public class MyInterpolator implements Interpolator {
@Override
public float getInterpolation(float input) {
return 2*input;
}
}
傳進(jìn)來(lái)的標(biāo)準(zhǔn)時(shí)間是0到1 ,返回的動(dòng)畫(huà)進(jìn)度從0到2. 寫(xiě)的是轉(zhuǎn)到360度,但是動(dòng)畫(huà)會(huì)從0轉(zhuǎn)到720度,轉(zhuǎn)兩圈,速度是勻速。
animation = new RotateAnimation(0,360);
animation.setDuration(3000);
animation.setInterpolator(new MyInterpolator());
iv.startAnimation(animation);
