Android動畫機制源碼分析(待完善)
本文着重講解Android3.0後推出的屬性動畫框架Property Animation——Animator的相關源碼分析
概述
3.0之前已有的動畫框架——Animation存在一些侷限性, Animation框架定義了透明度,旋轉,縮放和位移幾種常見的動畫,而且控制的是整個View,實現原理是每次繪製視圖時View所在的ViewGroup中的drawChild函數獲取該View的Animation的Transformation值,然後調用canvas.concat(transformToApply.getMatrix()),通過矩陣運算完成動畫幀,如果動畫沒有完成,繼續調用invalidate()函數,啓動下次繪製來驅動動畫,動畫過程中的幀之間間隙時間是繪製函數所消耗的時間,可能會導致動畫消耗比較多的CPU資源,最重要的是,動畫改變的只是顯示,並不能改變相應事件作用的位置。3.0之前的動畫我們一般稱爲View動畫.
而在Animator框架中使用最多的是AnimatorSet和ObjectAnimator配合,使用ObjectAnimator進行更精細化控制,只控制一個對象的一個屬性值,多個ObjectAnimator組合到AnimatorSet形成一個動畫。而且ObjectAnimator能夠自動驅動,可以調用setFrameDelay(longframeDelay)設置動畫幀之間的間隙時間,調整幀率,減少動畫過程中頻繁繪製界面,而在不影響動畫效果的前提下減少CPU資源消耗。因此,Anroid推出的強大的屬性動畫框架,基本可以實現所有的動畫效果。
簡單實例
在分析源碼之前先列出幾個常用的實例.後面的源碼分析也會用到其中的實例.
實例1:監聽動畫的過程
public void changMultiplePropertyByValueAnimator(final View v) {
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) v
.getLayoutParams();
PropertyValuesHolder pvh_left = PropertyValuesHolder.ofFloat(
"margin_left", params.leftMargin, 500);
PropertyValuesHolder pvh_top = PropertyValuesHolder.ofFloat(
"margin_top", params.topMargin, 500);
ValueAnimator ani = ValueAnimator.ofPropertyValuesHolder(pvh_left,
pvh_top).setDuration(1000);
ani.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float margin_left = (Float) valueAnimator
.getAnimatedValue("margin_left");
float margin_top = (Float) valueAnimator
.getAnimatedValue("margin_top");
RelativeLayout.LayoutParams p = (LayoutParams) v.getLayoutParams();
p.leftMargin = (int) margin_left;
p.topMargin = (int) margin_top;
v.setLayoutParams(p);
}
});
ani.start();
}
實例2:使用ObjectAnimator動畫改變背景色
/**
* 使用ObjectAnimator直接設置屬性和值
*/
public void changOneProperty(View v) {
ValueAnimator colorAnimator = ObjectAnimator.ofInt(v,
"backgroundColor", 0xffff8080 /*red*/ , 0xff8080ff /*Blue*/ );
colorAnimator.setDuration(1000);
colorAnimator.setEvaluator(new ArgbEvaluator());
colorAnimator.setRepeatCount(ValueAnimator.INFINITE);
colorAnimator.setRepeatMode(ValueAnimator.REVERSE);// ValueAnimator.RESTART
colorAnimator.start();
}
實例3:使用AnimatorSet改變多個屬性.
public void changMultiplePropertyByValueAnimator2(View v) {
/**
* 支持屬性: translationX and translationY: These properties control where
* the View is located as a delta from its left and top coordinates
* which are set by its layout container.
*
* rotation, rotationX, and rotationY: These properties control the
* rotation in 2D (rotation property) and 3D around the pivot point.
*
* scaleX and scaleY: These properties control the 2D scaling of a View
* around its pivot point.
*
* pivotX and pivotY: These properties control the location of the pivot
* point, around which the rotation and scaling transforms occur. By
* default, the pivot point is located at the center of the object.
*
* x and y: These are simple utility properties to describe the final
* location of the View in its container, as a sum of the left and top
* values and translationX and translationY values.
*
* alpha: Represents the alpha transparency on the View. This value is 1
* (opaque) by default, with a value of 0 representing full transparency
* (not visible).
*/
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(v, "rotation", 0, -90f),
ObjectAnimator.ofFloat(v, "rotationX", 0, 360f),
ObjectAnimator.ofFloat(v, "rotationY", 0, 360f),
ObjectAnimator.ofFloat(v, "translationX", 0, 200f),
ObjectAnimator.ofFloat(v, "translationY", 0, 200f),
ObjectAnimator.ofFloat(v, "scalY", 1, 1.5f),
ObjectAnimator.ofFloat(v, "scalX", 1, 2.0f),
ObjectAnimator.ofFloat(v, "alpan", 1, 0.25f, 1),
ObjectAnimator.ofFloat(v, "translationY", 0, 200f));
//set.playSequentially(animator1,animator2,animator3);
//set.play(ObjectAnimator.ofFloat(v, "rotation", 0, -90f)).with(anim)
set.setDuration(1000);
set.start();
}
實例4:使用PropertyValuesHolder和ObjectAnimator改變多個屬性.
/**
* 使用PropertyValuesHolder: scal alpha
* @param view
*/
public void propertyValuesHolder(View view) {
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 1f,0f, 1f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 1f,0, 1f);
PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 1f,0, 1f);
PropertyValuesHolder pvhColor = PropertyValuesHolder.ofInt("backgroundColor", 0xffff8080,0xff8080ff, 0xffff8080);
ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY, pvhZ,pvhColor)
.setDuration(1000).start();
}
實例5:寫包裝類爲不具有get/set方法的屬性提供修改方法
private static class ViewWrapper {
private View mTarget;
public ViewWrapper(View target) {
mTarget = target;
}
public int getWidth() {
Log.d("ViewWrapper","getWidth");
return mTarget.getLayoutParams().width;
}
public void setWidth(int width) {
Log.d("ViewWrapper","setWidth");
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}
public void addProperty(View v){
ViewWrapper wrapper = new ViewWrapper(v);
ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(5000).start();
}
對於一個View ,如果想要用屬性動畫,那個屬性就要對於set方法,比如設置的屬性是aaa,那麼我們就要在這個View裏面添加setAaa()方法,這個對自定義的view做動畫效果時很方便.
上面的幾個實例是平時常用到的幾種簡單實例.主要是對ObjectAnimator和PropertyValuesHolder的使用,其中ValueAnimator是ValueAnimator的父類.
源碼分析
分析源碼之前先列出幾個主要的類
ValueAnimator
ObjectAnimator
PropertyValuesHolder
其中, ObjectAnimator 是ValueAnimator 的子類. PropertyValuesHolder還有2個內部靜態子類
FloatPropertyValuesHolder和IntPropertyValuesHolder
後續的源碼分析就以上面的實例4爲參考,同時相關分析是基於Android 5 的源碼
public void propertyValuesHolder(View view) {
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 1f,0f, 1f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 1f,0, 1f);
PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 1f,0, 1f);
PropertyValuesHolder pvhColor = PropertyValuesHolder.ofInt("backgroundColor", 0xffff8080,0xff8080ff, 0xffff8080);
ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY, pvhZ,pvhColor)
.setDuration(1000).start();
}
上面的第2行調用了PropertyValuesHolder的.ofFloat(),第5行調用了ofInt().這2個方法其實會分別創建一個FloatPropertyValuesHolder和IntPropertyValuesHolder.同時將全部的參數也都傳遞給FloatPropertyValuesHolder和IntPropertyValuesHolder.
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
return new IntPropertyValuesHolder(propertyName, values);
}
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
return new FloatPropertyValuesHolder(propertyName, values);
}
這裏只看一下FloatPropertyValuesHolder 的實現.
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName);
setFloatValues(values);
}
-
@Override
public void setFloatValues(float... values) {
super.setFloatValues(values);
mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}
從上面的代碼可以看到FloatPropertyValuesHolder 的構造函數調用其父類的構造函數並調用自己的setFloatValues()方法.其父類的構造函數
PropertyValuesHolder()將屬性名稱記錄在了PropertyValuesHolder 的成員變量mPropertyName裏面.
setFloatValues也會先調用它父類的這個方法,它父類的這個方法會將前面傳遞進來的參數類型保存在變量mValueType裏面,同時根據這些參數生成動畫效果的關鍵幀.如果插入的是float類型的參數生成mKeyframes就是FloatKeyframeSet,int類型對應的就是IntKeyframeSet, FloatKeyframeSet和IntKeyframeSet都是KeyframeSet的子類.
private PropertyValuesHolder(String propertyName) {
mPropertyName = propertyName;
}
public void setFloatValues(float... values) {
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
KeyframeSet會更具傳入的參數個數執行不同的流程,如果只是參數一個值那麼這個值就作爲最後一幀.如果傳入了多個參數,也會根據參數的個數來設置.
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);
}
上面代碼中的第15行的Keyframe.ofFloat()會返回一個FloatKeyframe,並作爲數組keyframes[]其中的一個值,最好把這個數組用來傳遞給FloatKeyframeSet的構造函數.
到了這裏我們還是回到前面繼續分析上面的實例的下一行代碼.
實例中的代碼調用先調用ofPropertyValuesHolder()再調用start().那我們就看看ofPropertyValuesHolder().
public static ObjectAnimator ofPropertyValuesHolder(Object target,
PropertyValuesHolder... values) {
ObjectAnimator anim = new ObjectAnimator();
anim.setTarget(target);
anim.setValues(values);
return anim;
}
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;
}
ofPropertyValuesHolder()裏面創建了一個ObjectAnimator,target參數的中的視圖View,參數中的...values就是實例中的幾個ValuePropertyHolder對象.ObjectAnimator會使用setValues()將這些ValuePropertyHolder對象保存到一個HashMap中.
接下來就是要看看start()方法了.
ObjectAnimator的start()中前面部分在我們討論的實例中是不會執行,這裏我們分析一般的情況就可以了.所以就不關注前面部分,只看後面一行就可以了,其實ObjectAnimator的start()主要是調用它父類ValueAnimator來完成.
@Override
public void start() {
start(false);
}
private void start(boolean playBackwards) {//當前情況是false
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
mReversing = playBackwards;
mPlayingBackwards = playBackwards;//false
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();
}
ValueAnimator的無參數的start()方法就直接調用帶參數的start(boolean)方法,傳入的參數也是false.
在上面帶參數的start(boolean)方法中,
12到23行我們不需要關注,因爲if判斷不能pass,
24到33行我們暫時也不用關注,因爲我們當前實例沒有設置重複執行相關的狀態,上面實例2會執行這些代碼,我們這裏爲了流程簡單明瞭就不考慮這些.
34到38行主要是設置一些狀態值.其中pervPlayingState的默認值是STOPPED,
39行處理動畫時間長度相關,我們的實例目前並沒有執行setDurationScale,也不用擔心這些.
42行的if會pass,因爲我們沒有設置延遲,44的if也會pass,前面已經說了這個pervPlayingState默認是STOPPED.
49行是將設置的相關監聽動畫開始執行的AnimatorListener相關回調函數onAnimationStart執行起來.
所以剩下的重點就是在45行的setCurrentPlayTime(0)和最後一個的animationHandler.start();
先開始分析setCurrentPlayTime(0)的執行.
public void setCurrentPlayTime(long playTime) {
float fraction = mUnscaledDuration > 0 ? (float) playTime / mUnscaledDuration : 1;
setCurrentFraction(fraction);
}
public void setCurrentFraction(float fraction) {
initAnimation();
if (fraction < 0) {
fraction = 0;
}
int iteration = (int) fraction;
if (fraction == 1) {
iteration -= 1;
} else if (fraction > 1) {
if (iteration < (mRepeatCount + 1) || mRepeatCount == INFINITE) {
if (mRepeatMode == REVERSE) {
mPlayingBackwards = (iteration % 2) != 0;
}
fraction = fraction % 1f;
} else {
fraction = 1;
iteration -= 1;
}
} else {
mPlayingBackwards = mReversing;
}
mCurrentIteration = iteration;
long seekTime = (long) (mDuration * fraction);
long currentTime = AnimationUtils.currentAnimationTimeMillis();
mStartTime = currentTime - seekTime;
if (mPlayingState != RUNNING) {
mSeekFraction = fraction;
mPlayingState = SEEKED;
}
if (mPlayingBackwards) {
fraction = 1f - fraction;
}
animateValue(fraction);//調用子類ObjectAnimator重寫的方法
}
由於setCurrentPlayTime的參數爲0,而我們前面已經提到過動畫的時長不是0,所以setCurrentFraction()的參數也會是0.這裏我們直接看setCurrentFraction()的最後一行.但是這裏需要注意是這裏是執行的ValueAnimator的子類ObjectAnimator的animateValue().
ObjectAnimator.java
@Override
void animateValue(float fraction) {
final Object target = getTarget();
if (mTarget != null && target == null) {
// We lost the target reference, cancel and clean up.
cancel();
return;
}
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setAnimatedValue(target);
}
}
主要上面代碼的13行並不是執行的ValuePropertyHolder的setAnimateValue方法,而且其對於的子類FloatProperyHolder的方法.
ValueAnimator.java
這裏主要是執行監聽動畫執行中屬性改變的回調函數onAnimationUpdate.上面的實例1可以參考.
其中下面代碼的第6行是根據當前參數fraction來計算當前幀的值.
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].calculateValue(fraction);
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
FloatPropertyHolder.java
@Override
void setAnimatedValue(Object target) {
if (mFloatProperty != null) {
mFloatProperty.setValue(target, mFloatAnimatedValue);
return;
}
if (mProperty != null) {
mProperty.set(target, mFloatAnimatedValue);
return;
}
if (mJniSetter != 0) {
nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
return;
}
if (mSetter != null) {
try {
mTmpValueArray[0] = mFloatAnimatedValue;
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
在我們當前的實例中,第5和9行並不會執行,因爲我們在實例是直接使用String類型的屬性,並沒有去封裝.那麼關鍵就是滴13行和17行的if判斷了,
根據實際添加的log來看,第17行的mSetter會是null.第13行的mJniSetter不等於null.也就是是最終會調用本地函數nCallFloatMethod.也就是說試了JNI了.其實我很疑惑,有些屬性在對應的View對象裏面明明有對應的get和set方法,爲什麼mSetter還是null.還要去執行JNI.疑惑啊,暫時沒有搞明白,後續有時間再分析記錄.
接着回到前面分析,我們要開始ValueAnimator的start(boolean)的最後一行
private void start(boolean playBackwards) {//當前情況是false
//....
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();
}
這裏的AnimationHandler其實是一個實現了Runnable接口的類,並不是一個Handler.它的start()會調用scheduleAnimation().最後會執行Runnable的run()方法.run()方法又會轉調scheduleAnimation(),從而實現動畫效果的逐步實現.這個過程比較複雜,暫時不做分析.
筆記待完善點
1. 上面FloatPropertyHolder中的爲什麼是執行的JNI不是直接執行對應視圖的set方法.
2.AnimationHandler 的start()流程
鏈接: