對于android手機上的動畫實現(xiàn)主要有三種,一種是幀動畫,一種是View動畫,以及3.0以上提供的屬性動畫,所有View動畫可以實現(xiàn)的動畫都可以使用屬性動畫來實現(xiàn),最簡單的如漸變,平移,縮小和放大。
這里對于屬性動畫做個學(xué)習(xí)的總結(jié)與整理,以前總是學(xué)習(xí)過后直接使用,過一段時間后又忘記了如何去使用一個屬性動畫來設(shè)計自己的動畫效果,因此產(chǎn)生了這篇文章。
本篇文章分如下幾個部分:
- ObjectAnimator介紹
- ValueAnimator介紹
- TypeEvaluator(估值器)和TimeInterpolator(插值器)的學(xué)習(xí)
- 關(guān)于源碼的淺析
<h3>1.ObjectAnimator介紹</h3>
ObjectAnimator類是我們在設(shè)計動畫的時候經(jīng)常需要使用到的類,它可以直接對任意對象的任意屬性進行動畫操作,ObjectAnimator提供靜態(tài)的動畫實現(xiàn)方法,具體的實現(xiàn)方法有如下幾種:
下述的靜態(tài)方法中,傳入的參數(shù)一是目標(biāo)View,參數(shù)二是進行改變的屬性,參數(shù)三是變化的過程區(qū)間
請對照例子查看。
//顏色漸變動畫
public static ObjectAnimator ofArgb(Object target, String propertyName, int... values)
//int類型值動畫
public static ObjectAnimator ofInt(Object target, String propertyName, int... values)
//float類型值動畫
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
//Object類型值動畫
public static ObjectAnimator ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object... values)
....
上述列舉了幾個基本的方法來使用ObjectAnimator類,下面舉兩個例子整理一下大概實現(xiàn)代碼的思路:
1.translationX
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
float s=imageView.getTranslationX();
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "translationX",s,-500f,s+100 );
objectAnimator.setDuration(4000).start();
}});
效果圖
上述實現(xiàn)的是一個基本是平移動畫,可以看出實現(xiàn)的效果跟View動畫如出一轍
alpha
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "alpha", 1f, 0f, 1f);
objectAnimator.setDuration(5000).start();
}});
效果圖
ObjectAnimator提供了不少方法設(shè)計動畫的執(zhí)行過程,下面舉出常用的一些方法:
//設(shè)置動畫執(zhí)行時長
setDuration(4000);
//設(shè)置動畫監(jiān)聽
objectAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}});
//設(shè)置動畫循環(huán)播放的次數(shù),默認(rèn)為0,-1表示無限循環(huán)。
setRepeatCount();
//循環(huán)播放的模式,循環(huán)模式包括RESTART和REVERSE兩種,分別表示連續(xù)重新播放和倒序播放的意思
setRepeatMode();
上述的兩個例子只是針對于單個動畫的效果,如果想要組合起各個動畫,這也是各個復(fù)雜動畫產(chǎn)生的過程,那該如何使用呢?答案是AnimatorSet類的使用,AnimatorSet可以把動畫集合起來,既可以依次播放,也可以一起播放,可以定義各個動畫的播放順序,使用例子如下圖所示:
其代碼如下所示:
float x=view.getTranslationX();
ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(imageView, "translationX",x,500,x );
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "alpha", 1f, 0f, 1f);
AnimatorSet animatorSet=new AnimatorSet();
animatorSet.playTogether(objectAnimator,objectAnimator2);
animatorSet.setDuration(5000);
animatorSet.start();
關(guān)于AnimatorSet的使用,有如下主要幾個常用的方法:
after(Animator anim) 將現(xiàn)有動畫插入到傳入的動畫之后執(zhí)行
after(long delay) 將現(xiàn)有動畫延遲指定毫秒后執(zhí)行
before(Animator anim) 將現(xiàn)有動畫插入到傳入的動畫之前執(zhí)行
with(Animator anim) 將現(xiàn)有動畫和傳入的動畫同時執(zhí)行
playTogether(Animator... items)將所有傳入動畫同時執(zhí)行
playSequentially(Animator... items)所有動畫依次執(zhí)行
通過組合不同的動畫,我們就可以寫出不同的復(fù)雜的動畫效果。
一個問題
為何在ObjectAnimator在第二個參數(shù)中傳入一個String值,目標(biāo)View就會執(zhí)行相對應(yīng)的動畫呢?如我們傳入一個alpha參數(shù),為什么就行了呢?
引用郭霖大神的解釋:其實ObjectAnimator內(nèi)部的工作機制并不是直接對我們傳入的屬性名進行操作的,而是會去尋找這個屬性名對應(yīng)的get和set方法,因此alpha屬性所對應(yīng)的get和set方法應(yīng)該就是:
public void setAlpha(float value);
public float getAlpha();
那么textview對象中是否有這兩個方法呢?確實有,并且這兩個方法是由View對象提供的,也就是說不僅TextView可以使用這個屬性來進行淡入淡出動畫操作,任何繼承自View的對象都可以的。
既然alpha是這個樣子,相信大家一定已經(jīng)明白了,前面我們所用的所有屬性都是這個工作原理,那么View當(dāng)中一定也存在著setRotation()、getRotation()、setTranslationX()、getTranslationX()、setScaleY()、getScaleY()這些方法。
那假設(shè)我們不傳入一個規(guī)定的值,我們隨便傳入一個字符串,如"aaaaa",他的效果會怎樣的?一般情況下動畫沒有響應(yīng),而嚴(yán)重的情況則可能造成程序的crash,因為屬性動畫是要求動畫對象提供get和set的方法,屬性動畫根據(jù)外界傳遞的屬性的廚師值和最終值,以動畫的效果多次的去調(diào)用set方法,每次傳遞給set的方法,隨著時間的沖突,所傳遞的值最終越來越接近最終值,因此我們隊object做動畫,如果想要讓動畫生效,要滿足兩個條件:
object提供setAbc方法,如果動畫時沒有傳遞初始值,那么還要提供getAbc方法,因為系統(tǒng)要去取abc屬性的值(不滿足此條,程序會直接Crash)。
object的setAbc對屬性abc所做的改變必須通過某種方法映射出來,如帶來UI的改變之類的(不滿足此條,動畫無效果但不會Crash)。
ValueAnimator
如果你大概略過一遍關(guān)于ObjectAnimator的源碼,你會發(fā)現(xiàn)其實ObjectAnimator是繼承自ValueAnimator的,也就是說其實屬性動畫的實現(xiàn)最核心的類應(yīng)該是ValueAnimator,而ObjectAnimator只是對其的一個封裝繼承而已,下面就先來了解一下關(guān)于ValueAnimator把。
下面代碼重新的實現(xiàn)了透明度的變化從1f->0f->1f的過程,跟ObjectAnimator第一個示例的效果一樣,這里就不貼gif圖片了:
public void onClick(View v) {
valueAnimator=ValueAnimator.ofFloat(1F,0F,1F);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float result= (float) animation.getAnimatedValue();
imageView.setAlpha(result); }
});
valueAnimator.setDuration(4000);
valueAnimator.start();
}
});
ValueAnimator并不負(fù)責(zé)持有需要做動畫的View對象的引用,而只是執(zhí)行了計算的過程,但是我們可以在addUpdateListener監(jiān)聽事件中進行設(shè)置View對象的變化過程,從而實現(xiàn)動畫的過程。所有使用ObjectAnimator實現(xiàn)的效果,我們都可以通過ValueAnimator來實現(xiàn)這個過程,這里只是提供了一種ValueAnimator的使用情況,在實際開發(fā)中,我們還是使用ObjectAnimator偏多。
關(guān)于ValueAnimator的常用方法,只要ObjectAnimator擁有的方法,ValueAnimator都有,這里就不一一列舉說明,有興趣的朋友可以詳細(xì)查看一下源碼。
<h4>TimeInterpolator(插值器)</h4>
TimeInterpolator中文譯為時間插值器,他的作用是根據(jù)時間流逝的百分比來計算出當(dāng)前屬性改變的百分比,系統(tǒng)中有預(yù)置的三種插值器:
- LinearInterpolator(線性插值器),勻速的動畫
- DecelerateInterpolator(減速插值器),動畫越來越慢
- AccelerateDecelerateInterpolator(加速減速插值器),兩頭慢中間快
<h4>TypeEvaluator(估值器)</h4>
TypeEvaluator作用是根據(jù)當(dāng)前屬性改變的百分比來計算改變后的屬性值,其是要跟TimeInterpolator配對使用的。android內(nèi)置的估值器有:
- IntEvaluator Int類型估值器,返回int類型的屬性改變
- FloatEvaluator Float類型估值器,返回Float類型屬性改變
- ArgbEvaluator 顏色類型估值器
TypeEvaluator源碼:
public interface TypeEvaluator<T> {
public T evaluate(float fraction, T startValue, T endValue);
}
evaluate()方法當(dāng)中傳入了三個參數(shù),第一個參數(shù)fraction非常重要,這個參數(shù)用于表示動畫的完成度的,我們應(yīng)該根據(jù)它來計算當(dāng)前動畫的值應(yīng)該是多少,第二第三個參數(shù)分別表示動畫的初始值和結(jié)束值。
下面以勻速動畫實現(xiàn)過程來研究估值器和插值器在其中所扮演的角色,關(guān)于勻速動畫實現(xiàn)的插值器源碼和估值器源碼如下所示:
時間插值器
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public LinearInterpolator() { }
public LinearInterpolator(Context context, AttributeSet attrs) { }
public float getInterpolation(float input) { return input; }
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createLinearInterpolator();
}
}
類型估值器
public class IntEvaluator implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
比如一個移動的線性動畫需要在1s中完成400dp的移動過程,那么在0.4s時候,時間插值器的值是多少呢,主要是看其getInterpolation()方法,我們可以看到,過了0.4s,流逝的時間比即是0.4/1=0.4,所以屬性改變百分比的值為0.4,然后交付給估值器(這里假設(shè)使用Int類型估值器)進行對當(dāng)前對象屬性的改變操作。估值器獲取到當(dāng)前屬性改變的百分比0.4后,在IntEvaluator中會將第一個參數(shù)賦值為0.4,計算得到結(jié)果后在進行屬性的改變,當(dāng)前過程會一直重復(fù),知道動畫完成為止。
Android系統(tǒng)默認(rèn)提供的插值器和估值器滿足大部分使用場景,但是在某些情況下,我們可能需要重寫估值器或者插值器來達到使用動畫的目的,下面以重寫一個估值器為例:
定義一個動畫,使得View圍繞某一點做圓周運動,如圖所示:
當(dāng)然,直接通過重寫View的onDraw()實現(xiàn)起來更加簡單點,這里只是做個例子,在例子中,系統(tǒng)內(nèi)置的估值器并不能滿足我們的需求,所以這里進行重新定義,代碼如下:
自定義的估值器:
public class CircleTypeInterpolator implements TypeEvaluator {
private float radius;
public CircleTypeInterpolator(float radius){
this.radius=radius;
}
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
Point point=new Point();
Point startPoint= (Point) startValue;
//旋轉(zhuǎn)的角度
float angle=fraction*360;
float longY=(float) ((radius*Math.sin(Math.toRadians(angle))));
point.setY(startPoint.getY()+longY );
float longX= (float) (radius-radius*Math.cos(Math.toRadians(angle))); point.setX(startPoint.getX()+longX);
return point;
}
}
這里主要涉及到關(guān)于計算的問題,計算的思路是通過起始點通過三角計算來得到新的點的坐標(biāo)。
自定義View:
public class CirlceView extends View {
private Paint paint;
private Point point;
Point startPoint;
public static final float RADIUS=30f;
public CirlceView(Context context) {
super(context);
init();
}
public CirlceView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
paint=new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLUE);
}
@Override
protected void onDraw(Canvas canvas) {
if (point==null){
point=new Point(getMeasuredWidth()/2,getMeasuredHeight()/2);
canvas.drawCircle(point.x,point.y,RADIUS,paint); startAnim();
}else {
canvas.drawCircle(point.getX(),point.getY(),RADIUS,paint);
}
}
private void startAnim() {
try {
startPoint = (Point) point.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
ValueAnimator animator=ValueAnimator.ofObject(new CircleTypeInterpolator(100f),startPoint,startPoint);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Point result= (Point) animation.getAnimatedValue();
point.setX(result.getX());
point.setY(result.getY());
invalidate();
}
});
animator.setDuration(2000);
animator.setRepeatCount(-1);
animator.start();
}
}
自定義對象:
public class Point implements Cloneable {
public float x;
public float y;
public Point(float x, float y) {
this.x = x; this.y = y;
}
public Point() { }
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Point point=null;
point= (Point) super.clone();
return point;
}
}
<h3>源碼淺析</h3>
上面整理了屬性的動畫的使用的具體的一些方法以及例子,這里再對于屬性動畫進行源碼方面的研究,不求有多精通深入,希望能對動畫源碼有一個大體的認(rèn)識,方便日后研究。
屬性動畫工作原理
屬性動畫要求動畫作用的對象提供該屬性的set方法,蘇弧形動畫會根據(jù)你傳遞的該屬性的初始值和最終值,以動畫的效果多次調(diào)用set方法。每次傳遞個set方法的值東不一樣,隨著時間的推移,所傳遞的值越來越接近最終值。如果動畫沒有傳遞初始值,那么還要提供get方法,因為系統(tǒng)要去獲取屬性的初始值。
從ObjectAnimator的為起點進行分析,選擇ofFloat(...)進行代碼研究著手點:
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
//這里設(shè)置目標(biāo)target,并且做了判斷,如果當(dāng)前的target已經(jīng)啟動了動畫,則會將動畫取消。
public void setTarget(@Nullable Object target) {
final Object oldTarget = getTarget();
if (oldTarget != target) {
if (isStarted()) {
cancel();
}
mTarget = target == null ? null : new WeakReference<Object>(target);
// New target should cause re-initialization prior to starting
mInitialized = false;
}
}
//設(shè)置屬性名
public void setPropertyName(@NonNull String propertyName) {
// mValues could be null if this is being constructed piecemeal. Just record the
// propertyName to be used later when setValues() is called if so.
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setPropertyName(propertyName);
mValuesMap.remove(oldName);
mValuesMap.put(propertyName, valuesHolder);
}
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
這里構(gòu)建了一個ObjectAnimator對象,在ObjectAnimator對象中傳入了目標(biāo)target以及屬性名propertyName,然后在調(diào)用setFloatValues方法:
@Override
public void setFloatValues(float... values) {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) {
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
} else {
super.setFloatValues(values);
}
}
這里ObjectAnimator通過設(shè)置浮點值來調(diào)用父類ValueAnimator的setVaules函數(shù),先查看傳入的值:
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
return new FloatPropertyValuesHolder(propertyName, values);
}
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName);
setFloatValues(values);
}
@Override
public void setFloatValues(float... values) {
super.setFloatValues(values);
mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}
//對應(yīng) super.setFloatValues(values)初始化的東東
public void setFloatValues(float... values) {
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
這里返回了一個FloatPropertyValuesHolder對象,F(xiàn)loatPropertyValuesHolder調(diào)用了setFloatValues()方法,在其中設(shè)置了mFloatKeyframes這個變量,mFloatKeyframes通過調(diào)用KeyframeSet.ofFloat(values)方法獲得,這里查看一下KeyframeSet.ofFloat(values):
public static KeyframeSet ofFloat(float... values) {
boolean badValue = false;
int numKeyframes = values.length;
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
if (Float.isNaN(values[0])) {
badValue = true;
}
} else {
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i) {
keyframes[i] =
(FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
if (Float.isNaN(values[i])) {
badValue = true;
}
}
}
if (badValue) {
Log.w("Animator", "Bad value (NaN) in float animator");
}
return new FloatKeyframeSet(keyframes);
}
這里的keyframe成為關(guān)鍵幀,而KeyframeSet則是存儲Keyframe的集合,代碼中根據(jù)values的長度來構(gòu)建一個FloatKeyframe keyframes[]數(shù)組,然后通過Keyframe.ofFloat()方法去構(gòu)造一個keyframe對象:
FloatKeyframe(float fraction, float value) {
mFraction = fraction;
mValue = value;
mValueType = float.class;
mHasValue = true;
}
FloatKeyframe(float fraction) {
mFraction = fraction;
mValueType = float.class;
}
在初始化完成后返回keyframes數(shù)組,將其傳入到FloatKeyframeSet中進行構(gòu)建,查看FloatKeyframeSet的構(gòu)建方法:
public KeyframeSet(Keyframe... keyframes) {
mNumKeyframes = keyframes.length;
// immutable list
mKeyframes = Arrays.asList(keyframes);
mFirstKeyframe = keyframes[0];
mLastKeyframe = keyframes[mNumKeyframes - 1];
mInterpolator = mLastKeyframe.getInterpolator();
}
FloatKeyframeSet最終只是調(diào)用了父類的初始化方法,在父類的初始化方法中存儲了所有的關(guān)鍵幀,開始幀,結(jié)束幀以及時間插值器。到這里,PropertyValuesHolder.ofFloat()初始化完成,重新回溯到ObjectAnimator.setFloatValue()中,調(diào)用其setValues(PropertyValuesHolder.ofFloat(mPropertyName, values))方法:
public void setValues(PropertyValuesHolder... values) {
int numValues = values.length;
mValues = values;
mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
for (int i = 0; i < numValues; ++i) {
PropertyValuesHolder valuesHolder = values[i];
mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
}
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
首先記錄了mValues,這里的values是PropertyValuesHolder[]類型的,然后通過一個mValueMap記錄:key為屬性的名稱,值為PropertyValuesHolder 。
總結(jié)一下初始化的過程(摘自鴻洋csdn):ofFloat就是記錄了target,propName,values(是將我們傳入的int型values,輾轉(zhuǎn)轉(zhuǎn)化成了PropertyValuesHolder),以及一個mValueMap,這個map的key是propName,value是PropertyValuesHolder,在PropertyValuesHolder內(nèi)部又存儲了proprName, valueType , keyframeSet等等。
這里關(guān)于ObjectAnimator.ofFloat()方法的初始化就到此完成.
接下來著手研究一下核心的start()方法:
public void start() {
// See if any of the current active/pending animators need to be canceled
AnimationHandler handler = sAnimationHandler.get();
if (handler != null) {
//當(dāng)前動畫
int numAnims = handler.mAnimations.size();
for (int i = numAnims - 1; i >= 0; i--) {
if (handler.mAnimations.get(i) instanceof ObjectAnimator) {
ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i);
if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
anim.cancel();
}
}
}
//準(zhǔn)備執(zhí)行的動畫
numAnims = handler.mPendingAnimations.size();
for (int i = numAnims - 1; i >= 0; i--) {
if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) {
ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i);
if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
anim.cancel();
}
}
}
//延遲的動畫
numAnims = handler.mDelayedAnims.size();
for (int i = numAnims - 1; i >= 0; i--) {
if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) {
ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i);
if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
anim.cancel();
}
}
}
}
.....
super.start();
}
//是否存在相同的屬性和包名
private boolean hasSameTargetAndProperties(@Nullable Animator anim) {
if (anim instanceof ObjectAnimator) {
PropertyValuesHolder[] theirValues = ((ObjectAnimator) anim).getValues();
if (((ObjectAnimator) anim).getTarget() == getTarget() &&
mValues.length == theirValues.length) {
for (int i = 0; i < mValues.length; ++i) {
PropertyValuesHolder pvhMine = mValues[i];
PropertyValuesHolder pvhTheirs = theirValues[i];
if (pvhMine.getPropertyName() == null ||
!pvhMine.getPropertyName().equals(pvhTheirs.getPropertyName())) {
return false;
}
}
return true;
}
}
return false;
}
在start()方法中通過AnimationHandler來獲得到當(dāng)前/準(zhǔn)備/刪除的動畫,通過hasSameTargetAndProperties()來判斷如果當(dāng)前/準(zhǔn)備/刪除動畫中有和當(dāng)前動畫相同的,那么就取消掉相同的動畫。
然后調(diào)用父類的start()方法,由于ObjectAnimator的父類是ValueAnimator,所以我們找到源碼:
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
mReversing = playBackwards;
mPlayingBackwards = playBackwards;
if (playBackwards && mSeekFraction != -1) {
if (mSeekFraction == 0 && mCurrentIteration == 0) {
// special case: reversing from seek-to-0 should act as if not seeked at all
mSeekFraction = 0;
} else if (mRepeatCount == INFINITE) {
mSeekFraction = 1 - (mSeekFraction % 1);
} else {
mSeekFraction = 1 + mRepeatCount - (mCurrentIteration + mSeekFraction);
}
mCurrentIteration = (int) mSeekFraction;
mSeekFraction = mSeekFraction % 1;
}
if (mCurrentIteration > 0 && mRepeatMode == REVERSE &&
(mCurrentIteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) {
// if we were seeked to some other iteration in a reversing animator,
// figure out the correct direction to start playing based on the iteration
if (playBackwards) {
mPlayingBackwards = (mCurrentIteration % 2) == 0;
} else {
mPlayingBackwards = (mCurrentIteration % 2) != 0;
}
}
int prevPlayingState = mPlayingState;
mPlayingState = STOPPED;
mStarted = true;
mStartedDelay = false;
mPaused = false;
updateScaledDuration(); // in case the scale factor has changed since creation time
AnimationHandler animationHandler = getOrCreateAnimationHandler();
animationHandler.mPendingAnimations.add(this);
if (mStartDelay == 0) {
// This sets the initial value of the animation, prior to actually starting it running
if (prevPlayingState != SEEKED) {
setCurrentPlayTime(0);
}
mPlayingState = STOPPED;
mRunning = true;
notifyStartListeners();
}
animationHandler.start();
}
Animators需要在擁有Looper的線程中執(zhí)行,如果要更新UI,則必須在UI線程中使用。
在animationHandler(這是一個Runnable)中把當(dāng)前動畫加入到準(zhǔn)備動畫當(dāng)中,并且初始化一些動畫的狀態(tài)變量,最后調(diào)用animationHandler.start()方法啟動動畫,start()源碼:
public void start() {
scheduleAnimation();
}
private void scheduleAnimation() {
if (!mAnimationScheduled) {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
mAnimationScheduled = true;
}
}
最終會調(diào)用到Choreographer類,這是與底層交互的類,animationHandler是Runnable的子類,而 mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);類似與handler發(fā)送消息,最終執(zhí)行這個Runnable的run方法。所以只要查看run方法即可:
public void run() {
mAnimationScheduled = false;
doAnimationFrame(mChoreographer.getFrameTime());
}
doAnimationFrame()方法:
void doAnimationFrame(long frameTime) {
mLastFrameTime = frameTime;
// mPendingAnimations holds any animations that have requested to be started
// We're going to clear mPendingAnimations, but starting animation may
// cause more to be added to the pending list (for example, if one animation
// starting triggers another starting). So we loop until mPendingAnimations
// is empty.
while (mPendingAnimations.size() > 0) {
ArrayList<ValueAnimator> pendingCopy =
(ArrayList<ValueAnimator>) mPendingAnimations.clone();
mPendingAnimations.clear();
int count = pendingCopy.size();
for (int i = 0; i < count; ++i) {
ValueAnimator anim = pendingCopy.get(i);
// If the animation has a startDelay, place it on the delayed list
if (anim.mStartDelay == 0) {
anim.startAnimation(this);
} else {
mDelayedAnims.add(anim);
}
}
}
// Next, process animations currently sitting on the delayed queue, adding
// them to the active animations if they are ready
int numDelayedAnims = mDelayedAnims.size();
for (int i = 0; i < numDelayedAnims; ++i) {
ValueAnimator anim = mDelayedAnims.get(i);
if (anim.delayedAnimationFrame(frameTime)) {
mReadyAnims.add(anim);
}
}
int numReadyAnims = mReadyAnims.size();
if (numReadyAnims > 0) {
for (int i = 0; i < numReadyAnims; ++i) {
ValueAnimator anim = mReadyAnims.get(i);
anim.startAnimation(this);
anim.mRunning = true;
mDelayedAnims.remove(anim);
}
mReadyAnims.clear();
}
// Now process all active animations. The return value from animationFrame()
// tells the handler whether it should now be ended
int numAnims = mAnimations.size();
for (int i = 0; i < numAnims; ++i) {
mTmpAnimations.add(mAnimations.get(i));
}
for (int i = 0; i < numAnims; ++i) {
ValueAnimator anim = mTmpAnimations.get(i);
if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
mEndingAnims.add(anim);
}
}
mTmpAnimations.clear();
if (mEndingAnims.size() > 0) {
for (int i = 0; i < mEndingAnims.size(); ++i) {
mEndingAnims.get(i).endAnimation(this);
}
mEndingAnims.clear();
}
// Schedule final commit for the frame.
mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null);
// If there are still active or delayed animations, schedule a future call to
// onAnimate to process the next frame of the animations.
if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
scheduleAnimation();
}
}
在while循環(huán)當(dāng)中,首先將所有準(zhǔn)備動畫取出,然后清空準(zhǔn)備動畫的List,接著通過是否延遲播放來判斷立即執(zhí)行還是延遲執(zhí)行。將animationHandler的mAnimations集合中的每個anim,加入到mTmpAnimations中,依次調(diào)用mTmpAnimations中的anim,anim.doAnimationFrame(frameTime),doAnimationFrame(frameTime)上面已經(jīng)分析過了,如果返回true,即doAnimationFrame的done為true,則將該動畫加入到結(jié)束動畫集合。
循環(huán)調(diào)用mEndingAnims, mEndingAnims.get(i).endAnimation(this);內(nèi)部,會將動畫移除mAnimations,回調(diào)動畫監(jiān)聽接口onAnimationEnd;以及重置各種標(biāo)志變量。如果mAnimations不為null,則再次調(diào)用scheduleAnimation(),這里有調(diào)用到了scheduleAnimation,上述分析這是一個與底層交互的入口,因此循環(huán)調(diào)用使得view的屬性變化,直到動畫執(zhí)行完成為止。
總結(jié)
引用鴻洋大大的吧(博客鏈接):
ofInt中實例化了一個ObjectAnimator對象,然后設(shè)置了target,propName,values(PropertyValuesHolder) ;然后分別在setInterpolator,setDuration設(shè)置了Interpolator
和duration。其中setEvaluator是給PropertyValuesHolder,以及keyframeSet設(shè)置估值算法。
PropertyValueHolder實際上是IntPropertyValueHolder類型對象,包含propName,valueType,keyframeSet .
keyframeset中存了Keyframe集合,keyframe中存儲了(fraction , valuetype , value , hasValue)。
上述其實都是設(shè)置各種值什么的。真正核心要看start~
start()中:
首先,步驟1:更新動畫各種狀態(tài),然后初步計算fraction為(currentTime - mStartTime) / mDuration;然后將這個fraction交給我們的插值器計算后得到新的fraction,再將新的fraction交給我們的估值算法,估值算法根據(jù)開始、結(jié)束、fraction得到當(dāng)前屬性(動畫作用的屬性)應(yīng)該的值,最大調(diào)用反射進行設(shè)置;
當(dāng)然了:start中還會根據(jù)動畫的狀態(tài),如果沒有結(jié)束,不斷的調(diào)用scheduleAnimation();該方法內(nèi)部利用mChoreographer不斷的去重復(fù)我們的上述步驟1。
參考文章: