Android - Animation(一) 一文總結了android中的補間動畫(View Animation/Tween Animation)和幀動畫(Drawable Animation/Frame Animation)的使用html
本篇文章主要解析屬性動畫(Property Animation,android3.0引入)的實現原理java
下篇 屬性動畫的實現原理android
先來看屬性動畫的最簡單實現:數組
第一種方式:先在 /res/animator/目錄下建立translate.xml文件定義動畫,再在Java文件中引用並開啓
//首先在/res/animator/目錄下建立translate.xml文件,內容以下:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="translationX" //設置動畫須要改變的屬性名稱
android:duration="2000" //設置動畫的持續時間
android:valueFrom="0.0" //設置動畫的初始值(對應上邊設置的屬性)
android:valueTo="20.0"> //設置動畫的結束值(對應上邊設置的屬性)
</objectAnimator>
//而後在Java文件中進行引用,代碼以下:
mButton1 = (Button) findViewById(R.id.button1);
mButton1.setOnClickListener(this);
//加載xml文件中的動畫資源
mObjectAnimator = AnimatorInflater.loadAnimator(this, R.animator.translate);
mObjectAnimator.setTarget(mButton1);//設置動畫的做用對象
public void onClick(View v) {
switch(v.getId()){
case R.id.button1:
mObjectAnimator.start(); //開啓動畫
break;
}
}
第二種方式:直接在Java文件中建立並使用動畫緩存
mButton1 = (Button) findViewById(R.id.button1);
mButton1.setOnClickListener(this);
public void onClick(View v) {
//利用ofFloat()方法建立動畫、指定須要改變的屬性的名稱等並開啓動畫
ObjectAnimator.ofFloat(mButton1, "translationX", 0.0f,20.0f).setDuration(3000).start();
}
簡單地說,屬性動畫就是在指定的時間內改變對象的屬性值。ide
上述的例子,從代碼上看,設置了動畫執行的時間、做用的目標對象mButton1及其屬性translationX以及屬性值的初始值和最終值,但從效果上看,mButton1在2秒鐘以內在屏幕上勻速的移動了一段距離,因而咱們能夠猜測:函數
在start()方法執行以後,是否是會不斷地計算出一個值並賦給目標對象的屬性?
在屬性動畫中,是否是也有和補間動畫裏相似的插值器來改變更畫的執行速率?
若是有這樣的一個插值器的話,須要賦給目標對象的屬性的那個值的計算是否是也和這個插值器有關?
... ...
帶着這些猜測,咱們就以 在Java文件中建立動畫的方式 爲例來梳理屬性動畫的實現原理。
首先是ObjectAnimator類的靜態方法ofFloat
/*
Constructs and returns an ObjectAnimator that animates between float values. A single value implies that that value is the one being animated to. Two values imply a starting and ending values. More than two values imply a starting value, values to animate through along the way, and an ending value (these values will be distributed evenly across the duration of the animation).
建立並返回 一個基於float 類型數值的ObjectAnimator 對象,一個value值表明動畫的終點,兩個value 值,則一個是起點,
另外一個是終點,若是是多個值,
則中間的值表明動畫將要通過的點 ,而且這些點會均勻地分佈在動畫的執行過程當中
*/
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new
ObjectAnimator
(target, propertyName);
❶
anim.setFloatValues(values);
return anim;
}
ObjectAnimator的構造函數:oop
private ObjectAnimator(Object target, String propertyName) {
//爲ObjectAnimator的成員變量private Object mTarget賦值,mTarget的註釋爲:
//The target object on which the property exists, set in the constructor
mTarget = target;
setPropertyName(propertyName);
}
setPropertyName方法: 動畫
public void setPropertyName(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.
//mValues是ObjectAnimator的父類ValueAnimator的成員變量PropertyValuesHolder[] mValues;
//第一次執行爲null
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setPropertyName(propertyName);
mValuesMap.remove(oldName);
mValuesMap.put(propertyName, valuesHolder);
}
//爲ObjectAnimator的成員變量private String mPropertyName賦值
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
//mInitialized 是ObjectAnimator的父類ValueAnimator的成員變量boolean mInitialized;
//它的註釋爲:Flag that denotes whether the animation is set up and ready to go.
//Used to set up animation that has not yet been started.
//一個Flag用於標示動畫是否開始準備執行,它被用於動畫尚未正式開始start以前。默認值爲false
mInitialized = false;
}
因此,第一次執行ObjectAnimator anim = new ObjectAnimator(target, propertyName);只作了兩件事情:ui
一、爲ObjectAnimator 的成員變量mTarget和mPropertyName賦值
二、將mInitialized(定義在ObjectAnimator 的父類 ValueAnimator 中)的值設爲false
接下來是上文中❶處的anim.setFloatValues(values)方法:
@Override
public void setFloatValues(float... values) {
if (mValues == null || mValues.length == 0) {
//第一次執行mValues == null
// No values yet - this animator is being constructed piecemeal. Init the values with whatever the current propertyName is
// mValues目前還沒有賦值——當前的animator 正在構建中,將經過傳入的values初始化mValues
if (mProperty != null) {
//mProperty 爲ObjectAnimator的成員變量 private Property mProperty,第一次執行時也爲null
setValues(
PropertyValuesHolder.ofFloat
(mProperty, values));
} else {
// 第一次執行時,下列函數將被調用:
❷setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
} else {
super.setFloatValues(values);
}
}
在分析
setValues()方法以前先來看Pro
pertyValuesHolder的ofFloat()方法:
/**
* Constructs and returns a PropertyValuesHolder with a given property name and set of float values.
*/
// 經過給定的propertyName 和 values建立並返回一個PropertyValuesHolder 對象
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
❸
return new FloatPropertyValuesHolder(propertyName, values);
}
接着FloatPropertyValuesHolder的構造函數:
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName);
❹
setFloatValues(values);
}
首先執行的是FloatPropertyValuesHolder 的父類 PropertyValuesHolder的構造函數:
private PropertyValuesHolder(String propertyName) {
// 爲成員變量String mPropertyName(定義在PropertyValuesHolder類中)賦值
mPropertyName = propertyName;
}
咱們注意到,FloatPropertyValuesHolder 、IntPropertyValuesHolder都是PropertyValuesHolder的靜態內部類
來看一下PropertyValuesHolder的類定義:
/**
* This class holds information about a property and the values that that property should take on during an animation.
* PropertyValuesHolder objects can be used to create animations with ValueAnimator or ObjectAnimator that operate on several
* different properties in parallel.
*/
//這個類維持着一個property 和它的 值,用以在動畫中呈現出來,
//咱們能夠認爲它是爲實現屬性動畫而對property及其值作的一個包裝
public class PropertyValuesHolder implements Cloneable { }
接下來是上文中❹處setFloatValues()方法:
@Override
public void setFloatValues(float... values) {
❺
super.setFloatValues(values);
mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
}
首先執行的又是父類的方法:
public void setFloatValues(float... values) {
// 爲成員變量
Class mValueType(定義在父類PropertyValuesHolder中)賦值
mValueType = float.class;
❻
mKeyframeSet = KeyframeSet.ofFloat(values);
}
而後,mKeyframeSet = KeyframeSet.ofFloat(values),先來看KeyframeSet類的定義:
/**
* This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate
* values between those keyframes for a given animation. The class internal to the animation
* package because it is an implementation detail of how Keyframes are stored and used.
*/
//這個類維持着一個Keyframe 對象的集合,
//ValueAnimator使用它來爲一個動畫計算那些 keyframe 之間的值 ... ...
class KeyframeSet { }
對KeyframeSet有一個大概瞭解以後,再來看一下Keyframe類的定義:
/**
* This class holds a time/value pair for an animation. The Keyframe class is used by {@link ValueAnimator} to define the values that
* the animation target will have over the course of the animation. As the time proceeds from one keyframe to the other, the value of
* the target object will animate between the value at the previous keyframe and the value at the next keyframe. Each keyframe also
* holds an optional {@link TimeInterpolator} object, which defines the time interpolation over the intervalue preceding the keyframe.
*/
//簡單說,這就是個 對動畫過程當中的一幀的時間和屬性的值進行封裝的類
public abstract class Keyframe implements Cloneable { }
來看上文中❻處的KeyframeSet的ofFloat(values)方法:
public static KeyframeSet ofFloat(float... values) {
boolean badValue = false;//初始化一個標示,
以後用於標示values[i]是否是一個數字
int numKeyframes = values.length;//獲取傳入的參數的個數
//初始化一個
FloatKeyframe 類型的數組,數組的長度爲numKeyframes和2之間的較大者,這個比較容易理解
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
//若是咱們只傳入了一個參數,那麼這個參數將用於構建
keyframes[1],keyframes[0]的值則由ofFloat(0f)來構建,以下:
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
// Constructs a Keyframe object with the given time. The value at this time will be derived
from the target object when the animation first starts ... ...
// 使用給定的time構造一個Keyframe 對象,在動畫第一次執行時,這個給定的時間對應的value將利用動畫的目標對象去得到 ... ...
public static Keyframe ofFloat(float fraction) {
//
FloatKeyframe和IntKeyframe都是Keyframe 的靜態內部類,這個和PropertyValuesHolder結構是相似的
return new FloatKeyframe(fraction);
FloatKeyframe(float fraction) {
// 爲成員變量float mFraction(定義在
Keyframe 中)賦值,
注意,在此時,成員變量mValue的值尚未設置
mFraction = fraction;
//
爲成員變量Class mValueType(定義在Keyframe 中)賦值
mValueType = float.class;
}
}
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
/**
* Constructs a Keyframe object with the given time and value. The time defines the time, as a proportion of an overall
* animation's duration, at which the value will hold true for the animation ... ...
// 使用給定的time和value構造一個Keyframe 對象,time做爲整個動畫執行過程當中的一個時間比例,是一個0到1之間的值 ... ...
public static Keyframe ofFloat(float fraction, float value) {
return new FloatKeyframe(fraction, value);
FloatKeyframe(float fraction, float value) {
mFraction = fraction;
mValue = value;
mValueType = float.class;
mHasValue = true;
}
}
if (Float.isNaN(values[0])) {
badValue = true; // 若是傳入的值不是一個數值,將標示badValue改成true
}
//從以上分析能夠看到,當咱們只傳入一個值時,將用1
(表明時間終點)和這個值構建出動畫執行的最後一幀的Keyframe
//
對象,
而動畫的第一幀對應的
Keyframe
對象,則默認由0(表明時間的起點)來構建,這一幀對應的值將在動畫第一次
//
執行時由動畫
做用的對象來得到,若是咱們傳入的參數大於1個,好比2個或者多個,則執行下邊的邏輯:
} else {
//邏輯比較簡單,若是傳入的參數大於1,則,用
0f和values[0]構建出動畫的第一幀對應的Keyframe對象並賦值給keyframes[0],
//表明第一個value對應動畫的起始值
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
// 而後,進行遍歷,利用
values[i] 和 其所對應的幀在整個動畫執行過程當中應該處於的時間比例——
// (float) i / (numKeyframes - 1) 來構建每個
Keyframe對象,通常咱們傳入的參數是兩個,因此第二個參數就對應了
// 動畫執行的最後一幀的屬性值,其實,在這裏,屬性動畫實現的原理已經開始有所體現了。
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;
// 同上,若是傳入的值不是一個數值,將標示badValue改成true
}
}
}
if (badValue) {
Log.w("Animator", "Bad value (NaN) in float animator");
// 若是傳入的值不是一個數值,執行此邏輯
}
//以上邏輯主要就是建立
keyframes數組,該數組中放的是根據傳入的value值建立出來的動畫執行過程當中的關鍵幀對象
//即
(主要是)
將一個
mKeyframes 成員變量完成初始化的FloatKeyframeSet對象返回
return new FloatKeyframeSet(keyframes);
public FloatKeyframeSet(FloatKeyframe... keyframes) {
// 走的是父類的構造函數
super(keyframes);
public KeyframeSet(Keyframe... keyframes) {
//這個邏輯就比較簡單了
mNumKeyframes = keyframes.length;//爲成員變量int mNumKeyframes()賦值
mKeyframes = new ArrayList<Keyframe>();
//將接收到的
keyframes數組中的元素添加到
成員變量ArrayList<Keyframe> mKeyframes集合中
mKeyframes.addAll(Arrays.asList(keyframes));
mFirstKeyframe = mKeyframes.get(0);// 初始化第一幀對應的成員變量Keyframe mFirstKeyframe
mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
// 初始化最後一幀對應的成員變量Keyframe mLastKeyframe
// 初始化插值器對應的成員變量TimeInterpolator mInterpolator,目前爲null
mInterpolator = mLastKeyframe.getInterpolator();
private TimeInterpolator mInterpolator = null;
public TimeInterpolator getInterpolator() {
return mInterpolator;
}
}
}
}
至此,KeyframeSet的ofFloat(values)方法完成了,主要的邏輯是:
根據傳入的value值建立出動畫執行過程當中的關鍵幀對象,將這些對象放在一個數組中,new一個FloatKeyframeSet對象,而後將數組中的這些元素,放在FloatKeyframeSet對象的成員變量ArrayList<Keyframe> mKeyframes中,並將FloatKeyframeSet對象返回。
上邊第❻步將這個FloatKeyframeSet對象賦值給PropertyValuesHolder的成員變量KeyframeSet mKeyframeSet,因而,第❺步也執行完了,接着,在setFloatValues()方法中,執行完第❺步後:
mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
//將mKeyframeSet向下轉型爲FloatKeyframeSet賦值給FloatPropertyValuesHolder
//的成員變量FloatKeyframeSet mFloatKeyframeSet
因而,第❹步也執行完了,第❸步把完成初始化的FloatPropertyValuesHolder對象返回,第❷步將利用第❸步返回的對象做爲參數,執行setValues()方法,(該方法在ObjectAnimator的父類ValueAnimator類中定義),具體邏輯以下:
public void setValues(PropertyValuesHolder... values) {
int numValues = values.length;
// 爲PropertyValuesHolder[] mValues(在ObjectAnimator父類ValueAnimator中定義)賦值
mValues = values;
// 爲成員變量HashMap<String, PropertyValuesHolder> mValuesMap賦值,
//如下是關於mValuesMap的註釋:
//A hashmap of the PropertyValuesHolder objects. This map is used to lookup
//animated values by property name during calls to getAnimatedValue(String).
//一個用於存儲PropertyValuesHolder對象的集合,在調用getAnimatedValue(String)方法
//時經過property name來查找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;
}
至此,用於建立屬性動畫對象的ObjectAnimator類的靜態方法ofFloat的大致邏輯分析完成了。簡單總結一下:
ObjectAnimator.ofFloat(Object target, String propertyName, float... values) 建立了一個ObjectAnimator對象,而且:
一、將 target 賦給 ObjectAnimator 的成員變量
private Object mTarget
二、將
propertyName 賦給
ObjectAnimator 的成員變量
private String mPropertyName
三、建立一個 FloatPropertyValuesHolder 對象
3.一、將
propertyName 賦給
FloatPropertyValuesHolder 的
成員變量
String mPropertyName(在
FloatPropertyValuesHolder
的父類
PropertyValuesHolder中定義
)
3.二、
將
float.class 賦給
FloatPropertyValuesHolder 的
成員變量 Class mValueType(在
FloatPropertyValuesHolder
的父類
PropertyValuesHolder中定義
)
3.三、建立一個 FloatKeyframeSet對象
3.3.一、建立一個 FloatKeyframe 類型的數組 keyframes
3.3.二、根據傳入的 values 構造出
keyframes 數組中的每一項(關鍵幀對象
Keyframe
)
3.3.三、將
keyframes 數組中的每一項添加到
FloatKeyframeSet 對象的成員變量
ArrayList<Keyframe>mKeyframes(在
FloatKeyframeSet
的父類
KeyframeSet中定義
)中
3.四、將
FloatKeyframeSet 對象
賦給
PropertyValuesHolder 的成員變量
KeyframeSet mKeyframeSet
3.五、將 mKeyframeSet 向下轉型爲 FloatKeyframeSet 類型賦給
FloatPropertyValuesHolder 的成員變量 FloatKeyframeSet mFloatKeyframeSet
四、將
FloatPropertyValuesHolder 對象 賦給ObjectAnimator 的成員變量 PropertyValuesHolder[] mValues
(在
ObjectAnimator
的父類
ValueAnimator中定義
)
五、利用已完成初始化的
FloatPropertyValuesHolder
對象及其
mPropertyName屬性,
完成成員變量HashMap<String, PropertyValuesHolder> mValuesMap(在ValueAnimator中定義
)的
初始化
動畫開始執行以前,還有一個關鍵的方法 — setDuration(long)
public ObjectAnimator setDuration(long duration) {
// 執行的是父類 ValueAnimator 的setDuration()方法
super.setDuration(duration);
private static float sDurationScale = 1.0f;
// How long the animation should last in ms 默認時間是300毫秒
private long mDuration = (long)(300 * sDurationScale);
private long mUnscaledDuration = 300;
public ValueAnimator setDuration(long duration) {
if (duration < 0) {
// 若傳入的值小於零,拋出異常
throw new IllegalArgumentException("Animators cannot have negative duration: " +
duration);
}
mUnscaledDuration = duration;
mDuration = (long)(duration * sDurationScale);
return this;
}
return this;
}
另外,在 setDuration( )方法中,咱們能夠看到 成員變量 mDuration 的值最終是由咱們調用 setDuration( )方法時傳入的 duration 乘以 sDurationScale 得出的,sDurationScale 默認值爲 1.0f ; 而且 ValueAnimator 類中提供了靜態方法 setDurationScale() 供咱們使用
public static void setDurationScale(float durationScale) {
sDurationScale = durationScale;
}
咱們能夠利用這個方法改變更畫的速度
下邊來看屬性動畫的執行過程 —— start( )方法
從上文的分析,咱們注意到,使用ObjectAnimator類的靜態方法ofFloat來建立動畫對象的過程當中,ObjectAnimator類只是複寫了父類ValueAnimator的一部分方法,一樣也只擁有部分只屬於本身的成員變量,事實上,咱們在使用屬性動畫時,所涉及到的類的繼承關係以下:
/**
* This is the superclass for classes which provide basic support for animations which can be
* started, ended, and have <code>AnimatorListeners</code> added to them.
*/
//這是那些爲動畫提供最基礎的支持以讓動畫能被開啓、結束和被增長監聽的類的超類
public abstract class Animator implements Cloneable { }
Animator 是屬性動畫體系的超類,它定義了諸如 start()、cancel()、end()、setDuration(long duration)、setInterpolator(TimeInterpolator value)、isRunning()、addListener(AnimatorListener listener)、removeAllListeners() 等方法
AnimatorSet 在屬性動畫中的用法和在補間動畫中相似,不細講了
ValueAnimator 和 ObjectAnimator 是屬性動畫的實現類,它們的區別在哪裏?分析完start( ) 方法,再結合上邊的 ofFloat( ) 方法進行總結
ObjectAnimator 的 start() 方法:
public void start() {
// See if any of the current active/pending animators need to be canceled
AnimationHandler handler = sAnimationHandler.get();
if (handler != null) { ... ... } // 第一次進入這裏 handler 應該是 null
super.start(); // 執行父類的start()方法
}
ValueAnimator 的 start() 方法:
public void start() {
start(false);
}
/**
* Start the animation playing. This version of start() takes a boolean flag that indicates
whether the animation should play in
*
reverse.
The flag is usually false, but may be set
to true if called from the reverse() method.
* <p>The animation started by calling this method will be run on the thread that called
this method. This thread should have
*
a Looper
on it (a runtime exception will be thrown if
this is not the case). Also, if the animation will animate
properties
of
* objects in the view hierarchy, then the calling thread should be the UI
thread for that view hierarchy.</p>
*/
// 開始執行動畫,這個
start() 方法 有一個boolean型的參數用於標示該動畫是否須要反轉,該參數
通常
爲false,可是也可能
// 被設置爲true若是start() 方法是從 reverse() 方法中調用的,該動畫將運行在調用 start() 方法的線程裏,這個線程須要擁有
//
一個
Looper 對象,不然會發生異常 ... ...
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
mPlayingBackwards = playBackwards; // 動畫是否反轉的標示
// This variable tracks the current iteration that is playing. When mCurrentIteration exceeds the repeatCount
// (if repeatCount!=INFINITE), the animation ends 這個變量用於記錄當前動畫的循環次數,當mCurrentIteration 超過了
// repeatCount(若是repeatCount 不等於 -1),動畫將被終止,該變量默認值爲0
mCurrentIteration = 0;
mPlayingState = STOPPED;//標示動畫的狀態,如下是ValueAnimator中對動畫狀態的定義:
/**
* Values used with internal variable mPlayingState to indicate the current state of an animation.
*/
// 變量mPlayingState 使用這些值來標示動畫的狀態
static final int STOPPED = 0; // Not yet playing 還沒有開始
static final int RUNNING = 1; // Playing normally 正常進行中
static final int SEEKED = 2; // Seeked to some time value
mStarted = true;
// Tracks whether a startDelay'd animation has begun playing through the startDelay.
mStartedDelay = false;
// Whether this animator is currently in a paused state.
mPaused = false;
AnimationHandler animationHandler = getOrCreateAnimationHandler();
private static AnimationHandler getOrCreateAnimationHandler() {
AnimationHandler handler = sAnimationHandler.get();
if (handler == null) {// 第一次執行,走下邊的邏輯
handler = new AnimationHandler();
sAnimationHandler.set(handler);// 此處涉及ThreadLocal的使用,暫不細說
}
return handler;
}
// 將此動畫對象添加到AnimationHandler的mPendingAnimations集合中
animationHandler.mPendingAnimations.add(this);
if (mStartDelay == 0) {
// The amount of time in ms to delay starting the animation after start() is called 調用start()方法以後延遲多少時間播放動畫
private long mStartDelay = 0;// 默認爲零,執行下邊的邏輯
// This sets the initial value of the animation, prior to actually starting it running
setCurrentPlayTime(0);
mPlayingState = STOPPED;
mRunning = true;
notifyStartListeners();
}
animationHandler.start();
}
在ValueAnimator的start( )方法中,須要重點分析的就是setCurrentPlayTime(0)和animationHandler.start()這兩個方法
setCurrentPlayTime(0)方法:
public void setCurrentPlayTime(long playTime) {
❼
initAnimation();
long currentTime = AnimationUtils.currentAnimationTimeMillis();
if (mPlayingState != RUNNING) {
mSeekTime = playTime;
mPlayingState = SEEKED;
}
mStartTime = currentTime - playTime;
❽
doAnimationFrame(currentTime);
}
上邊❼處的initAnimation()方法:
// ObjectAnimator 複寫了父類的initAnimation()方法
void initAnimation() {
if (!mInitialized) { // 此時
mInitialized的值爲false,執行下邊邏輯
// mValueType may change due to setter/getter setup; do this before calling super.init(),
// which uses mValueType to set up the default type evaluator.
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
❾
mValues[i].setupSetterAndGetter(mTarget);
}
❿
super.initAnimation();
}
}
上邊第❾步執行的是PropertyValuesHolder的setupSetterAndGetter()方法,來看具體邏輯:
void setupSetterAndGetter(Object target) {
if (mProperty != null) {
// check to make sure that mProperty is on the class of target
try {
Object testValue = mProperty.get(target);
for (Keyframe kf : mKeyframeSet.mKeyframes) {
if (!kf.hasValue()) {
kf.setValue(mProperty.get(target));
}
}
return;
} catch (ClassCastException e) {
Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
") on target object " + target + ". Trying reflection instead");
mProperty = null;
}
}
// 從上文中屬性動畫的建立的分析來看,此時的mProperty 爲null,從下邊開始執行
Class targetClass = target.getClass();
if (mSetter == null) {
// PropertyValuesHolder 的成員變量 Method mSetter,默認爲null
setupSetter(targetClass);
}
for (Keyframe kf : mKeyframeSet.mKeyframes) {
if (!kf.hasValue()) {
if (mGetter == null) {
setupGetter(targetClass);
if (mGetter == null) {
// PropertyValuesHolder 的成員變量 private Method mGetter,默認爲null
// Already logged the error - just return to avoid NPE
return;
}
}
try {
kf.setValue(mGetter.invoke(target));
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
}
setupSetterAndGetter()方法中,重點分析setupSetter(targetClass)、setupGetter(targetClass)以及kf.setValue(mGetter.invoke(target))方法
setupSetter(targetClass)方法
void setupSetter(Class targetClass) {
// 能夠看到,該方法的主要做用就是給PropertyValuesHolder 的成員變量 Method mSetter 賦值
mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);
}
setupGetter(Class targetClass)方法
private void setupGetter(Class targetClass) {
// 能夠看到,該方法的主要做用就是給 PropertyValuesHolder 的成員變量 Method mGetter 賦值
mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
}
setupSetter(targetClass) 和 setupGetter(targetClass) 方法都調用了setupSetterOrGetter 方法,只是參數有所不一樣,第一個和第三個好理解,第二個參數是一個集合,其定義以下:
//These maps hold all property entries for a particular class. This map is used to speed up property/setter/getter lookups for a given
//class/property combination. No need to use reflection on the combination more than once. 也比較簡單明瞭
private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap =
new HashMap<Class, HashMap<String, Method>>();
private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap =
new HashMap<Class, HashMap<String, Method>>();
第四個參數,對於setupSetter()方法來說,傳入的是mValueType(咱們在建立動畫對象時已爲其賦值),而對於setupGetter()方法來說,傳入的是null,來看setupSetterOrGetter 方法的主要邏輯:
/**
* Returns the setter or getter requested. This utility function checks whether the requested method exists in the propertyMapMap
* cache. If not, it calls another utility function to request the Method from the targetClass directly.
*/
// 首先看註釋,檢查propertyMapMap集合是否有所請求的 setter or getter 方法,若是有,返回,若是沒有,經過targetClass
// 得到他們,將它們緩存起來並返回
private Method setupSetterOrGetter(Class targetClass,
HashMap<Class, HashMap<String, Method>> propertyMapMap,
String prefix, Class valueType) {
Method setterOrGetter = null; // 定義臨時變量
try {
// Have to lock property map prior to reading it, to guard against
// another thread putting something in there after we've checked it
// but before we've added an entry to it
mPropertyMapLock.writeLock().lock();
HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
if (propertyMap != null) {// 第一次執行,此處propertyMap 爲 null
setterOrGetter = propertyMap.get(mPropertyName);
}
if (setterOrGetter == null) {
// Determine the setter or getter function using the JavaBeans convention of setFoo or getFoo for a property
// named 'foo'. This function figures out what the name of the function should be and uses reflection to find the Method
// with that name on the target object.利用反射獲取set和get方法
setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
if (propertyMap == null) {
propertyMap = new HashMap<String, Method>();
propertyMapMap.put(targetClass, propertyMap);
}
// 將得到的方法緩存起來
propertyMap.put(mPropertyName, setterOrGetter);
}
} finally {
mPropertyMapLock.writeLock().unlock();
}
return setterOrGetter;
}
setupSetterAndGetter()方法中,setupSetter(targetClass)、setupGetter(targetClass)方法大體分析完了,它完成了對mSetter和mGetter的初始化,接下來,對KeyframeSet的成員變量ArrayList<Keyframe> mKeyframes(上文分析過,在屬性動畫的對象建立時,就以完成對mKeyframes的初始化,mKeyframes裏邊放的是根據傳入的value構造出的動畫執行過程當中的幀對象)進行遍歷,具體邏輯是:
for (Keyframe kf : mKeyframeSet.mKeyframes) {
if (!kf.hasValue()) { //
hasValue()方法定義以下:
public boolean hasValue() {
return mHasValue; // Keyframe的成員變量,
boolean mHasValue ,默認是 false
// 上文咱們在講動畫建立過程當中
根據傳入的 values 構造出 keyframes 數組中的每一項(關鍵幀對象Keyframe)時,已經講過,
//
若是咱們只傳入了一個參數,那麼這個參數將用於構建
keyframes[1],走下邊的第一個構造函數
// keyframes[0]的值則由ofFloat(0f)來構建,走下邊的第二個構造函數,即此關鍵幀對象的mHasValue爲默認值false
FloatKeyframe(float fraction, float value) {
mFraction = fraction;
mValue = value;
mValueType = float.class;
mHasValue = true; // 標示一個幀對象是否已有value值
}
FloatKeyframe(float fraction) {
mFraction = fraction;
mValueType = float.class;
}
}
if (mGetter == null) {
setupGetter(targetClass); // 上文已分析過了
if (mGetter == null) {
// Already logged the error - just return to avoid NPE
return;
}
}
try {
kf.setValue(mGetter.invoke(target));
// 該方法利用經過反射得到的get方法爲mKeyframes集合中尚未value值的幀對象賦值
// 上文中,講到
Keyframe
對象的建立時,構造函數
public static Keyframe ofFloat(float fraction)的註釋爲:
//
Constructs a Keyframe object with the given time. The value at this time will be derived
from the target object when
// the animation first starts ... ... 指的就是這個地方
public void setValue(Object value) {
if (value != null && value.getClass() == Float.class) {
mValue = ((Float)value).floatValue();
mHasValue = true;
}
}
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
至此,上邊第❾步執行完了,它主要是對動畫對象的成員變量PropertyValuesHolder[] mValues作更進一步的初始化,接下來執行上文中的第❿步,父類ValueAnimator中定義的initAnimation()方法
/**
* This function is called immediately before processing the first animation frame of an animation. If there is a nonzero
* <code>startDelay</code>, the function is called after that delay ends.It takes care of the final initialization steps for the
* animation. ... ...
*/
// 這個方法在執行動畫的第一幀以前被調用,若是有一個不爲零的startDelay值,該方法將在相應的延遲時間執後被執行
// 這是一個動畫最後的初始化步驟 ... ...
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
/**
* Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
to calculate animated values.
*/
// 邏輯很簡單,就是根據mValueType的值設置成員變量TypeEvaluator mEvaluator
的值,用來
calculate animated values
void init() {
if (mEvaluator == null) {
// We already handle int and float automatically, but not their Object
// equivalents
mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
(mValueType == Float.class) ? sFloatEvaluator :
null;
}
if (mEvaluator != null) {
// KeyframeSet knows how to evaluate the common types - only give it a custom
// evaluator if one has been set on this class
mKeyframeSet.setEvaluator(mEvaluator);
}
}
}
// 如今,動畫最後的初始化已經完成,就將
mInitialized 的值設爲 true 了
mInitialized = true;
}
}
到如今,上文中第❼步也完成了,咱們能夠看到,這一步是在作進一步的初始化,其中對set和get方法的初始化和爲沒有value值得幀對象賦值的操做是在ObjectAnimator中完成的,而對用來計算動畫的value值的TypeEvaluator的初始化則是在ValueAnimator中完成的
稍後再來分析上文中第❽步的doAnimationFrame(currentTime)方法,所以,在ValueAnimator的start( )方法中,須要重點分析的兩個方法之一setCurrentPlayTime(0)就到此爲止,接着看後邊的animationHandler.start():
animationHandler.start()方法最終會致使AnimationHandler的run方法的執行(此處細節省略):
public void run() {
mAnimationScheduled = false;
// 這是AnimationHandler類中的doAnimationFrame()方法
doAnimationFrame(mChoreographer.getFrameTime());
}
doAnimationFrame( )方法:
private void doAnimationFrame(long 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);
// 其實,上述代碼最主要的就是執行了一句 handler.mAnimations.add(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();
}
// 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();
}
}
能夠看到,在AnimationHandler類中,有如下幾個集合:
//The per-thread list of all active animations
protected final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
//Used in doAnimationFrame() to avoid concurrent modifications of mAnimations
private final ArrayList<ValueAnimator> mTmpAnimations = new ArrayList<ValueAnimator>();
//The per-thread set of animations to be started on the next animation frame
protected final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>();
protected final ArrayList<ValueAnimator> mDelayedAnims = new ArrayList<ValueAnimator>();
private final ArrayList<ValueAnimator> mEndingAnims = new ArrayList<ValueAnimator>();
private final ArrayList<ValueAnimator> mReadyAnims = new ArrayList<ValueAnimator>();
在AnimationHandler類的doAnimationFrame( )方法中,會根據動畫的屬性值的變化,用這些集合來管理動畫對象,而且在這個過程當中,會調用到最核心的ValueAnimator類的doAnimationFrame()方法(第 ⓫ 步),當mAnimations.contains(anim)而且doAnimationFrame()方法的返回值爲true時,就會執行mEndingAnims.add(anim);將動畫對象添加到mEndingAnims集合中,接着,遍歷mEndingAnims集合,執行 mEndingAnims.get(i).endAnimation(this);主要是將mAnimations、mPendingAnimations、mDelayedAnims集合中的對象清空以及改變一些標示。標示着動畫的結束。
若是doAnimationFrame()方法的返回值爲false,
則在知足條件(!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) 時,執行scheduleAnimation(),即至關於調用animationHandler.start()繼續循環。
那麼doAnimationFrame()方法的邏輯是什麼?
又回到了在上文中,當時咱們暫時放下沒有分析的第❽步中的doAnimationFrame(currentTime)方法上:
final boolean doAnimationFrame(long frameTime) {
if (mPlayingState == STOPPED) { }
if (mPaused) { }
else if (mResumed) { }
// 主要是下邊的代碼
final long currentTime = Math.max(frameTime, mStartTime);
return animationFrame(currentTime);
}
接着是animationFrame()方法:
boolean animationFrame(long currentTime) {
boolean done = false;
switch (mPlayingState) {
case RUNNING:
case SEEKED:
float fraction
= mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
if (fraction >= 1f) {
if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
// Time to repeat
if (mListeners != null) {
int numListeners = mListeners.size();
for (int i = 0; i < numListeners; ++i) {
mListeners.get(i).onAnimationRepeat(this);
}
}
if (mRepeatMode == REVERSE) {
mPlayingBackwards = !mPlayingBackwards;
}
mCurrentIteration += (int)fraction;
fraction = fraction % 1f;
mStartTime += mDuration;
} else {
done = true;
fraction = Math.min(fraction, 1.0f);
}
}
if (mPlayingBackwards) {
fraction = 1f - fraction;
}
⓬
animateValue(fraction);
break;
}
return done;
}
咱們看到,在每一次調用該方法時,都會根據動畫對象的一些和時間相關的屬性的值來計算fraction的值,來判斷要返回true仍是false。
從代碼中,能夠看出,fraction表明的就是動畫執行過程當中的每一幀在整個動畫執行過程當中所處的時間的比率。
分析到此,整個屬性動畫的實現原理基本清楚了,還剩最後一點 ——
每一次調用animationFrame方法時,怎麼利用計算出來的fraction來改變更畫做用對象的屬性值以達到動畫的效果?答案是上文中⓬處的animateValue(fraction)方法,須要注意的是,ObjectAnimator類重寫了父類的animateValue(fraction)方法,來看具體邏輯:
void animateValue(float fraction) {
super.animateValue(fraction); // 首先調用父類的方法
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setAnimatedValue(mTarget);
void setAnimatedValue(Object target) {
if (mProperty != null) {
mProperty.set(target, getAnimatedValue());
}
if (mSetter != null) {
try {
mTmpValueArray[0] = getAnimatedValue();
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
}
}
父類ValueAnimator的animateValue(fraction)方法:
void animateValue(float fraction) {
// 此時,咱們在文章的開頭提到的插值器登場了
fraction = mInterpolator.getInterpolation(fraction);
private TimeInterpolator mInterpolator = sDefaultInterpolator;
private static final TimeInterpolator sDefaultInterpolator = new AccelerateDecelerateInterpolator();
// 能夠看到,若是不進行設置的話,默認的插值器就是 AccelerateDecelerateInterpolator
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);
}
}
}
至此,屬性動畫實現原理基本清楚了。