Android - Animation(一) 一文總結了Android中的補間動畫(View Animation/Tween Animation)和幀動畫(Drawable Animation/Frame Animation)的使用java
本篇文章主要解析屬性動畫(Property Animation,android3.0引入)的實現原理android
下篇 屬性動畫的實現原理數組
先來看屬性動畫的最簡單實現:緩存
第一種方式:先在 /res/animator/文件夾下建立translate.xml文件定義動畫。再在Java文件裏引用並開啓
-
- <?
xml version="1.0" encoding="utf-8"?ide
> 函數
- <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>
-
-
- mButton1 = (Button) findViewById(R.id.button1);
- mButton1.setOnClickListener(this);
-
- 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) {
-
- ObjectAnimator.ofFloat(mButton1, "translationX", 0.0f,20.0f).setDuration(3000).start();
- }
簡單地說,屬性動畫就是在指定的時間內改變對象的屬性值。oop
上述的樣例。從代碼上看,設置了動畫運行的時間、做用的目標對象mButton1及其屬性translationX以及屬性值的初始值和終於值,但從效果上看,mButton1在2秒鐘以內在屏幕上勻速的移動了一段距離。因而咱們可以猜測:post
在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的構造函數:
- private ObjectAnimator(Object target, String propertyName) {
-
-
- mTarget = target;
- setPropertyName(propertyName);
- }
setPropertyName方法:
- public void setPropertyName(String propertyName) {
-
-
-
-
- if (mValues != null) {
- PropertyValuesHolder valuesHolder = mValues[0];
- String oldName = valuesHolder.getPropertyName();
- valuesHolder.setPropertyName(propertyName);
- mValuesMap.remove(oldName);
- mValuesMap.put(propertyName, valuesHolder);
- }
-
- mPropertyName = propertyName;
-
-
-
-
-
- mInitialized = false;
- }
因此。
第一次運行ObjectAnimator anim = new ObjectAnimator(target, propertyName);僅僅作了兩件事情:
一、爲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()方法以前先來看PropertyValuesHolder的ofFloat()方法:ui
/**
* 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) {
-
- mPropertyName = propertyName;
- }
咱們注意到,FloatPropertyValuesHolder 、IntPropertyValuesHolder都是PropertyValuesHolder的靜態內部類
來看一下PropertyValuesHolder的類定義:
-
-
-
-
-
-
-
- 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類的定義:
對KeyframeSet有一個大概瞭解以後。再來看一下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之間的較大者,這個比較easy理解
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;
-
-
因而。第❹步也運行完了,第❸步把完畢初始化的FloatPropertyValuesHolder對象返回,第❷步將利用第❸步返回的對象做爲參數。運行setValues()方法。(該方法在ObjectAnimator的父類ValueAnimator類中定義),詳細邏輯例如如下:
- 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);
- }
-
- 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的一部分方法,相同也僅僅擁有部分僅僅屬於本身的成員變量,其實。咱們在使用屬性動畫時。所涉及到的類的繼承關係例如如下:
-
-
-
-
-
- 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() {
-
- AnimationHandler handler = sAnimationHandler.get();
- if (handler != null) { ... ... }
- super.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) {
-
- 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;
- }
- }
-
- Class targetClass = target.getClass();
- if (mSetter == null) {
-
- setupSetter(targetClass);
- }
- for (Keyframe kf : mKeyframeSet.mKeyframes) {
- if (!kf.hasValue()) {
- if (mGetter == null) {
- setupGetter(targetClass);
- if (mGetter == null) {
-
-
- 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) {
-
- mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);
- }
setupGetter(Class targetClass)方法
- private void setupGetter(Class targetClass) {
-
- mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
- }
setupSetter(targetClass) 和 setupGetter(targetClass) 方法都調用了
setupSetterOrGetter 方法,僅僅是參數有所不一樣,第一個和第三個好理解,第二個參數是一個集合,其定義例如如下:
-
-
- 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 方法的主要邏輯:
-
-
-
-
-
-
- private Method setupSetterOrGetter(Class targetClass,
- HashMap<Class, HashMap<String, Method>> propertyMapMap,
- String prefix, Class valueType) {
- Method setterOrGetter = null;
- try {
-
-
-
- mPropertyMapLock.writeLock().lock();
- HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
- if (propertyMap != null) {
- setterOrGetter = propertyMap.get(mPropertyName);
- }
- if (setterOrGetter == null) {
-
-
-
- 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.
*/
// 邏輯很是easy。就是依據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;
-
- 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類中。有下面幾個集合:
-
- protected final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
-
- private final ArrayList<ValueAnimator> mTmpAnimations = new ArrayList<ValueAnimator>();
-
- 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);
}
}
}
至此,屬性動畫實現原理基本清楚了。