Android動畫解析

Android動畫分類:

1.Tween Animation:補間動畫
    補間動畫分類:a.Alpha:漸變透明度的動畫
		b.Scale:漸變尺寸縮放動畫
		c.Translate:位置移動動畫
		d.Rotate:旋轉動畫
	他們共有屬性:
	 (1)Duration:動畫持續時間(單位:毫秒)
          (2)fillAfter:設置爲true,動畫轉化在動畫結束後被應用
         (3)fillBefore:設置爲true,動畫轉化在動畫開始前被應用
         (4)interpolator:動畫插入器(加速、減速插入器)
         (5)repeatCount:動畫重複次數
          (6)repeatMode:順序重複/倒序重複
         (7)startOffset:動畫之間的時間間隔
2.Frame Animation:幀動畫,將一組圖片按一定的時間間隔顯示出來
3.Layout Animation:佈局動畫
4.Property Animation:屬性動畫

補間動畫實現步驟:
	(1)配置文件
	    (/res/anim)-->alpha、Scale、Translate、rotate
	    以<set></set>作爲根佈局
         (2)java代碼實現
            加載配置文件:Animation animation=AnimationUtils.loadAnimation(context,R.anim.alpha_animation)
            view.startAnimation(animation);
各個動畫的具體實現可以到以下鏈接上下載看看,有詳細的註釋:
http://download.csdn.net/detail/luck_136/9586997
補間動畫實現的原理:
	當我們給View控件設置動畫效果之後,並且保持View控件在動畫結束的位置,這個時候給view控件添加點擊事件我們會發現,View控件根本不會響應,而當我們
  點擊View控件原有位置,會發現點擊事件被響應了。這表明,View控件的LayoutParams參數根本沒有發生改變,所以Animation設置的動畫只是一種假象,那麼這是怎麼回事呢?
先看view.startAnimation(animation)的startAnimation(animation)的源碼:

/**
 * Start the specified animation now.
 *
 * @param animation the animation to start now
 */
public void startAnimation(Animation animation) {
    animation.setStartTime(Animation.START_ON_FIRST_FRAME);
    // 這個方法,會給 view 自己內部設置一個 Animation 對象(內部變量爲 mCurrentAnimation)
    setAnimation(animation);
    invalidateParentCaches();
    // 通知 View 進行刷新界面
    invalidate(true);
}

我們可以看到:這裏實際上是給View設置了一個animation。


再看invalidateParentCaches()的源碼:
protected void invalidateParentCaches() {
    if (mParent instanceof View) {
        ((View) mParent).mPrivateFlags |= PFLAG_INVALIDATED;
    }
}

這裏是當前View的父控件,其實是通過ParentView不斷的調整ChildView的畫布座標實現的。以平移動畫爲例。假設在動畫開始時 ChildView 在 ParentView 中的初始位置在 (100,200) 處,這時 ParentView 會根據這個座標來設置 ChildView 的畫布,在 ParentView 的 dispatchDraw 中它發現 ChildView 有一個平移動畫,而且當前的平移位置是 (100, 200),於是它通過調用畫布的函數 traslate(100, 200) 來告訴 ChildView 在這個位置開始畫,這就是動畫的第一幀。如果 ParentView 發現 ChildView 有動畫,就會不斷的調用 invalidate() 這個函數,這樣就會導致自己會不斷的重畫,就會不斷的調用 dispatchDraw 這個函數,這樣就產生了動畫的後續幀,當再次進入 dispatchDraw 時,ParentView 根據平移動畫產生出第二幀的平移位置 (500, 200),然後繼續執行上述操作,然後產生第三幀,第四幀,直到動畫播完。
父控件(ViewGroup)中dispatchDraw()源碼:


只要子view可見或者子view設置了動畫,那麼就會對該子view調用drawChild(canvas, child, drawingTime)

這其中又涉及到兩個重要的類型,Animation 和 Transformation,這兩個類是實現動畫的主要的類,Animation 中主要定義了動畫的一些屬性比如開始時間、持續時間、是否重複播放等,這個類主要有兩個重要的函數:getTransformation 和 applyTransformation,在 getTransformation 中 Animation 會根據動畫的屬性來產生一系列的差值點,然後將這些差值點傳給 applyTransformation,這個函數將根據這些點來生成不同的 Transformation,Transformation 中包含一個矩陣和 alpha 值,矩陣是用來做平移、旋轉和縮放動畫的,而 alpha 值是用來做 alpha 動畫的(簡單理解的話,alpha 動畫相當於不斷變換透明度或顏色來實現動畫),以上面的平移矩陣爲例子,當調用 dispatchDraw 時會調用 getTransformation 來得到當前的 Transformation。

這個 Transformation 中的矩陣如下:

矩陣變換圖

矩陣變換圖

所以具體的動畫只需要重載 applyTransformation 這個函數即可

用戶可以定義自己的動畫類,只需要繼承 Animation 類,然後重載 applyTransformation 這個函數。對動畫來說其行爲主要靠差值點來決定的,比如,我們想開始動畫是逐漸加快的或者逐漸變慢的,或者先快後慢的,或者是勻速的,這些功能的實現主要是靠差值函數來實現的,Android 提供了 一個 Interpolator 的基類,你要實現什麼樣的速度可以重載其函數 getInterpolation,在 Animation 的 getTransformation 中生成差值點時,會用到這個函數。

從上面的動畫機制的分析可知某一個 View 的動畫的繪製並不是由他自己完成的而是由它的父 view 完成,所有我們要注意上面 TextView 旋轉一週的動畫示例程序中動畫的效果並不是由 TextView 來繪製的,而是由它的父 View 來做的。

總結:invalidate(true)實際上是調用父控件來不斷的重新繪製自己,它把動畫的播放 / 繪製交給父 View 去處理而不是讓子 View 本身去繪製,這種從更高的層次上去控制的方式便於把動畫機制做成一個易用的框架,如果用戶要在某個 view 中使用動畫,只需要在 xml 描述文件或代碼中指定就可以了,從而把動畫的實現和 View 本身內容的繪製(象 TextView 裏面的文字顯示)分離開了,起到了減少耦合和提高易用性的效果。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章