本篇涉及例子下載:Github 本篇講android 3.0引入的屬性動畫框架,上篇寫視圖動畫View Animation時就說過ViewAnimation的缺點,那就是動畫作用的是view本身的視覺部分,view實際屬性並沒有隨著動畫的改變而變化。很多時候就需要額外去出來由於動畫引起的事件不同步, ...
本篇涉及例子下載:Github
本篇講android 3.0引入的屬性動畫框架,上篇寫視圖動畫View Animation時就說過ViewAnimation的缺點,那就是動畫作用的是view本身的視覺部分,view實際屬性並沒有隨著動畫的改變而變化。很多時候就需要額外去出來由於動畫引起的事件不同步,比如ViewAnimation已經講View移出了屏幕,但View的事件觸發還在原地,這就需要額外處理了。 但是,PropertyAnimation的引入就完全解決了這個問題,它可以保證動畫和事件的變化總是一起的發生的。其本質是View是屬性變化帶動View重繪來完成動畫。
目錄
- 屬性動畫框架結構
- 個人理解
- TypeEvaluator與Interpolators
- ObjectAnimator
- ValueAnimator
- 動畫集: Keyframes於AnimatorSet
一、屬性動畫框架結構
個人覺得瞭解繼承結構對學習框架幫助挺大的,所以開始先說一下屬性動畫的框架,我自己用StartUML畫了下麵的類圖: 畫得比較糙(StartUML用的不好, -_-!),上面沒畫出TypeEvaluator和Interpolator,一來是怕複雜了不好看重點信息,二來是後面會重點講。大致講一下各個類的作用:
- Animator類:屬性動畫框架的基類,封裝了動畫時間、監聽器、動畫狀態的控制。
- AnimatorListener:動畫狀態介面,封裝了不同狀態需要調用的方法,有如下方法
- onAnimationStart() - 動畫開始時調用
- onAnimationEnd() - 動畫結束時調用
- onAnimationRepeat() - 動畫重覆時調用
- onAnimationCancel() - 動畫取消時調用,註意這個方法調用後會接著調用onAnimationEnd()
- AnimatorSet:類似於視圖動畫中的AnimationSet,它代表了一個動畫集,可以指定動畫集中各個動畫的順序,是實現複雜動畫必不可少的利器。
- ValueAnimation:屬性動畫的核心類,它能驅動View的屬性變化進而實現動畫。
- AnimationUpdateListenr:與ValueAnimation搭配使用,每次需要刷新動畫幀時會調用這個介面里的方法,進而對真正的View設置屬性完成動畫。它就包含一個介面方法:
- onAnimationUpdate()
- ObjectAnimator:ValueAnimator的便捷版本,也是平時使用最多的屬性動畫類,它通過反射調用自動完成View屬性的設置,非常簡單。
- TimeAnimator:可是用於同步動畫的輔助類,可以在觸發動畫幀時通過介面讓動畫執行指定的時間點。
以上便是屬性動畫框架的大致結構,關於ypeEvaluator和Interpolator後面有講到。
二、個人理解
1.屬性動畫時“真正的”動畫機制嗎? 我覺得不是(放下手頭的磚,讓我說完 ㄟ( ▔, ▔ )ㄏ),為什麼我說不是?它並不是直接生成一段動畫,它是通過在眾多動畫幀上改變View的屬性,然後View重繪,進而達到動畫的目的。或者把它理解成一個過渡值產生器更合適,它在每個動畫幀時間點上產生一個過渡值,然後把這個值設為相應的屬性。 2.屬性動畫VS視圖動畫: 屬性動畫相比於視圖動畫的優點是顯而易見的,因為動畫的來源是由於View的屬性變化引起的重繪,所以不存在事件與動畫不一致的情況。 視圖動畫最大的優點就是它簡單了,不需要添加各種監聽器就能實現動畫。 我還沒有兩種動畫性能的對比,不知道在實現相同效果時孰優孰劣,所以不敢妄下結論。留到後面討論吧! 3.ValueAnimator VS ObjectAnimator ObjectAnimator是ValueAnimator的便捷類,為了避免複雜的實現儘量選擇用ObjectAnimator。
三、TypeEvaluator與Interpolators
前面多次說道動畫幀(Frame),什麼叫Frame呢?相當於視圖動畫中的幀動畫,也就是每一個單獨的頁面,類似於膠卷電影中的一格。人眼的可察覺刷新頻率是24幀每秒,在低於這個頻率的動畫中我們看到影像就不是連續的。而熟悉動畫中用來指定動畫刷新頻率的是setFrameDelay()方法,預設情況下它的值是10ms,也就是100幀每秒。有了刷新頻率,如何講它映射到值呢?這就要用到TypeEvaluator和Interpolators了! 先說Interpolators(插值器):它的總用是怎樣將一個時間分數裝換為一個值分數,兩者的相對關係體現了動畫的形式:勻速、加速、先加速後減速,或者其它的數學變換。怎麼理解講一個時間分數裝換為值分數呢?來看一下介面吧!所以插值器都是TimeInterpolator介面的子類,其中聲明的方法:
abstract float getInterpolation(float input)
input是一個已經過去的時間占總時間的比例,也就是動畫已經完成的百分比。返回的也是個小數,代表了希望這個點完成的總動畫的百分數。假設有這樣一個插值器,它接受0.5,返回0.8。這意味著這個差值器在經過50%的時間時,希望動畫完成了80%。為什麼是希望呢?因為真正映射為屬性值是由TypeEvaluator來完成的。 TypeEvaluator:(翻譯成類型求值器吧!這樣比較貼合它的意思)它完成了差值器返回的值到實際值的映射?或許你會疑問為什麼需要這個值,不應該是下麵這個公式嗎?
startVal:起始值 endVal:結束值 percent:完成百分比 progress:當前值 progress = startVal + (endVal-startVal)*progress
對,的確是這樣的。內置的IntEvator、floatEvator也是這樣的,但是我們動畫的類型不一定都是這種數值類型的值吧!我們還可以動畫字元串,在不同的時間點顯示不同的字串,這時上述公式就不成立了。所以TypeEvaluator就是為了達到自定義裝換的需求的。要實現滿足自己需求的TypeEvaluator也很簡單,只需要實現TypeEvaluator介面就可以了,它聲明的方法如:
abstract T evaluate(float fraction, T startValue, T endValue)
T是要動畫的屬性類型,fraction是值分數,startValue、endValue是進行動畫屬性的起始值和終止值。返回映射的中間值。 到這,可以對屬性動畫的工作原理做個簡單的接受了。 動畫需要指定持續時間,屬性起始值,屬性終止值,幀延時。然後每過幀延時的時間間隔,觸發插值器,接著觸發值裝換器,接下來是觸發AnimationUpdateListenr中的onAnimationUpdate()方法重新設置View的屬性,接著View重繪,如此迴圈知道動畫結束。為了方便,我畫瞭如下流程圖:
ps:這符圖不是很嚴謹,因為有些內容是不好表達,比如動畫幀的計時是連續的,並不是等到上一幀完成了才開始計數下一幀。其次就是Animator本身的Listener是班法畫上去的,當Animator調用cance(),end()之類的方法是能打斷這個流程的。但是這圖對理解過程還是不錯的。
四、ObjectAnimator
先來講最常使用的屬性動畫,ObejectAnimator,通過置頂好動畫作用對象個對用屬性等設置值後,ObjectAnimator能夠通過反射區設置對象屬性達到動畫的目的。 ObjectAnimator提供了四種靜態方法來構造自己的對象,分別是:ofInt、ofFloat、ofArgb、ofObject 。它們動畫的值類型正如名字中說的那樣,如ofInt動畫作用於一個int域,offArgb註意與一個帶透明的顏色域。 來看一個用動畫改變欄背景的例子
public class MainActivity extends AppCompatActivity {
@Bind(R.id.btn_bg) Button mGradientBg;
ObjectAnimator mAnimator;
@TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final View view = findViewById(android.R.id.content);
final Drawable bgd =view.getBackground();
mAnimator = ObjectAnimator.ofArgb(view,"backgroundColor",0x881DDA38,0x88D48AB2);
mAnimator.setDuration(5*1000);
mAnimator.setRepeatMode(ObjectAnimator.REVERSE);
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
view.setBackground(bgd);
}
});
ButterKnife.bind(this);
}
@OnClick(R.id.btn_bg) void onGradientActionbarClicked() {
mAnimator.start();
}
}
效果如下:
上面就是ObjectAnimation使用過程,先通過四個靜態方法構造出一個ObjectAnimation對象,這裡使用的是ofArgb:
static ObjectAnimator ofArgb(Object target, String propertyName, int... values)
- 第一個是作用的對象
- 第二個是string指定的屬性名
- 第三個可變參數是動畫的起始值(可省略,省略以當前值算,且必須有getter)、終止值。
ObjectAnimator作用的域在對象是必須有相應的setter,getter(用於省略初始值的情況)方法,否則反射調用不了會報錯。至於沒有setter方法的域如何動畫,官方Guide提供瞭如下解決方案:
- 使用包裝類,加入setter,getter。
- 添加相應方法(自己定義View適合)
- 換用ValueAnimator。
講一下使用包裝類,假設有類A,且A類中有域a,我可以正常操作A.a(訪問和寫值),現在我要動畫A的a域,直接用ObjectAnimator是不行了,因為A類中沒有A.setA()這個方法,那該怎麼辦呢?使用包裝類,寫一個A的包裝類AWrapper,它接受一個A對象來構造自己,然後裡面有一個setter和getter方法來設置和讀取A的a域。現在就可以在AWrapper上使用ObjectAnimator了。
六、ValueAnimator
ValueAnimator提供的便利性大大強於ObejectAnimator,因為需要你自己去更新對象的值,ValueAnimator只是為了提供了一個過渡值。為了更新對象的值,你就必須去設置前面講的AnimationUpdateListenr介面。一個簡單的例子來完成ValueAnimator的使用:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
final float X = 0, Y = 500;
LogUtil.d("X,Y = "+X+","+Y);
mValueAnimator = ValueAnimator.ofFloat(0f, 1f);
mValueAnimator.setDuration(3 * 1000);
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override public void onAnimationUpdate(ValueAnimator animation) {
mChangelocation.setY(Y * (Float)animation.getAnimatedValue());
mChangelocation.invalidate();
}
});
mValueAnimator.addListener(new AnimatorListenerAdapter() {
@Override public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mChangelocation.setY(Y);
}
});
}
@OnClick(R.id.btn_cl) void onChangeLocationClicked() {
mValueAnimator.start();
}
}
七、動畫集: Keyframes於AnimatorSet
類似於ViewAnimation中的動畫集,參考資料:AnimatorSet
Keyframes實現動畫集:Keyframes
本篇涉及例子下載:Github