安卓屬性動畫之插值器(TimeInterpolator)和估值器(TypeEvaluator)

我們都知道對於屬性動畫可以對某個屬性做動畫,而 插值器(TimeInterpolator) 估值器(TypeEvaluator)在其中扮演了重要角色,下面先了解下 TimeInterpolator TypeEvaluator


TimeInterpolator(時間插值器):


作用:根據時間流逝的百分比計算出當前屬性值改變的百分比。


系統已有的插值器: 


  • LinearInterpolator(線性插值器):勻速動畫。 

  • AccelerateDecelerateInterpolator(加速減速插值器):動畫兩頭慢,中間快。 

  • DecelerateInterpolator(減速插值器):動畫越來越慢。


TypeEvaluator(類型估值算法,即估值器):


作用:根據當前屬性改變的百分比來計算改變後的屬性值。


系統已有的估值器: 


  • IntEvaluator:針對整型屬性 

  • FloatEvaluator:針對浮點型屬性 

  • ArgbEvaluator:針對Color屬性


那麼 TimeInterpolator和 TypeEvaluator是怎麼協同工作的呢?


它們是實現 非勻速動畫 的重要手段。屬性動畫是對屬性做動畫,屬性要實現動畫,首先由 TimeInterpolator(插值器)根據時間流逝的百分比計算出當前屬性值改變的百分比,並且 插值器 將這個百分比返回,這個時候 插值器 的工作就完成了。


比如 插值器 返回的值是0.5,很顯然我們要的不是0.5,而是當前屬性的值,即當前屬性變成了什麼值,這就需要 估值器 根據當前屬性改變的百分比來計算改變後的屬性值,根據這個屬性值,我們就可以設置當前屬性的值了。


源碼分析


上面的理論知識可能比較抽象,下面根據一個實例結合系統源碼來分析一下:


下圖表示一個勻速動畫,採用了線性插值器和整型估值算法,在40ms內,View的x屬性實現從0到40的變換。




由於動畫的默認刷新率爲10ms/幀,所以該動畫將分爲5幀進行。


當動畫進行到第三幀的時候,(x=20,t=20ms)當時間t=20ms 的時候,時間流逝的百分比是 0.5(20/40=0.5),即時間過了一半。這個百分比不是我們最終想要的,我們關心的是x的變化,那麼x應該變化多少呢?插值器和估值算法就起作用了。拿線性插值器 來說,線性插值器是實現 勻速動畫 的,先看一下線性插值器的源碼:




很顯然,線性插值器的返回值和輸入值一樣(看getInterpolation方法),因此t=20ms 插值器 返回的值是 0.5,這意味着 x屬性的改變是0.5。這個時候 插值器 的工作已經完成了,插值器的工作就是根據時間流逝的百分比計算出當前屬性值改變的百分比。


我們得到了當前屬性改變的百分比是0.5,即50%。下一步就是要算出x具體變爲了什麼值,這個時候 估值算法 就起作用了,它的作用就是根據當前屬性改變的百分比來計算改變後的屬性值。我們先看看系統提供的 整型估值算法 的源碼:




上述代碼中的 evaluate方法 的三個參數分別表示 估值小數(fraction)開始值(startValue)和 結束值(endValue)。對於我們的這個例子就是0.5,0和40。我們將這三個值代入求值,即:0+0.5*(40 - 0)=20。沒錯,這就是當t=20ms x=20 的由來。


其實對於 插值器和估值器 來說,除了系統提供的外,我們還可以自定義。實現方式也很簡單,因爲插值器和估值器都是一個接口,且內部都只有一個方法,我們只要實現接口就可以了,就可以做出很多絢麗的動畫了。其中,自定義插值器需要實現 Interpolator或者TimeInterpolator自定義估值器需要實現TypeEvaluator


但是一般來說,插值器使用系統的就足夠了,估值器自定義的可能會多一些,另外就是如果要對其他類型(非Int丶float丶color)做動畫,必須自定義類型估值算法。


代碼演示(一)


在這裏我們用兩種方式實現拋物線軌跡,一種是 固定時間的拋物線,一種是 固定長度的拋物線


球按照拋物線軌跡運行1.5秒(參照鴻洋大神的拋物線效果):


首先分析一下實現過程: 


要實現拋物線的效果,水平方向200px/s垂直方向加速度200px/s*s,你會發現我們只需要知道時間的變化,就可以求出某刻小球的水平位移(即x)和 數值位移(即y)。那麼公式就是:x = 200t ; y=200t²


但是還有一個問題就是,我們要同時記錄兩個值並進行 估值算法,所以我們可以創建一個 Point類 來保存這兩個值。前面我們使用過 ValueAnimator 的 ofFloat() 和 ofInt()方法,分別用於對 浮點型 整型 的數據進行動畫操作的,但實際上 ValueAnimator 中還有一個 ofObject() 方法,是用於對任意對象進行動畫操作的。


相比於浮點型或整型數據,對象的動畫操作明顯要更復雜一些,因爲系統將完全無法知道如何從初始對象過度到結束對象,因此這個時候我們就需要實現一個自己的 TypeEvaluator 來告知系統如何進行過度。這裏我們要過度的對象就是 Point對象


首先,定義一個 Point類,如下所示:


/**
* 保存小球座標的類
*/
public class Point {
   float x;
   float y;
   
   public Point(){}
   
   public Point(float x,float y){
       this.x = x;
       this.y = y;    } }


Point類非常簡單,x和y兩個變量用於記錄座標的位置,並且提供了兩個構造方法。


接下來 自定義估值算法,來告知系統如何實現初始對象到結束對象的過度(對於 插值器 來說,我們就使用 系統的線性插值器即可,無須自定義):




對於自定義估值算法,你需要指定一個泛型,這裏我們操作的是Point 對象,當我們指定Point 爲泛型時,evaluate方法的返回值也是Point ,並且startValue endValue也是 Point 類型,這樣就對了,在自定義估值算法中,我們就可以直接使用Point 對象了。


但是你會發現一個問題就是,我們並沒有用到 startValue endValue,而是使用了一個寫死的1.5f,因爲沒必要,我們的拋物線是與時間掛鉤的,不用他們也是完全可以的,這樣寫的話,出來的效果就是:球運行1.5s的軌跡圖。(下面我們還會有一個例子,它的自定義估值算法就是以startValue和endValue來計算的)。


佈局很簡單,一個 RelativeLayout 包含一個 顯示小球的ImageView 以及一個 用於操控的Button。這裏就不貼出了。


下面看 MainActivity 的代碼:




通過代碼發現,我們調用 ValueAnimator的ofObject() 方法來構建 ValueAnimator 的實例,這裏需要注意的是,ofObject()方法要求多傳入一個 TypeEvaluator 參數,這裏我們只需要傳入剛纔定義好的 MyTypeEvaluator 的實例就可以了。


還有我們設置了 1500ms 的持續時間,這裏的 1500ms和自定義估值器裏面的1.5f不是一回事,也就是說我們就算設置的是2000ms的持續時間,得到的小球運行軌跡是一樣的,因爲自定義估值器裏面的1.5f是寫死的


運行效果:




我們發現小球確實是拋物線運行,但是仔細看代碼你會發現,我們並沒有指定小球的落點位置,即 Point對象的終止屬性,那麼小球落點的位置是哪裏呢?其實我們可以算出來,即 x=200*1.5=300;y=200*1.5*1.5=450。但是現在我們更換一下需求,要求小球剛好落在屏幕的右邊緣,而不是求具體時間小球的軌跡。


小球呈拋物線軌跡運行到屏幕的右邊緣(自己嘗試的拋物線效果):


我們知道 y=ax^2 也是拋物線的一個公式,但是仔細看這個公式你會發現,它不是以t爲基準了,而是y與x的關係,也就是說y的變化直接與x掛鉤。並且對於 y=ax^2,我們知道a越小,開口越大,所以爲了拋物線效果好看,我們此次使用的 a=0.001。 


要實現這個效果,其實可以 不用ofObject() 方法,也就是說不用自定義估值算法,直接 使用ofFloat() 也是可以實現的,即直接使用系統的估值器。我們可以爲 x屬性設置一個勻速動畫,然後設置監聽器來實現爲x和y設置屬性值。


代碼如下:




運行效果:




確實,小球在碰到右邊部就停止了,是我們想要的效果。


其實要實現拋物線效果方法應該還有很多。結合數學公式還可以做出很多效果,比如正選曲線運動等。


代碼演示(二)


上面我們演示了拋物線效果,下面我們再來一個效果,這次我們要 自定義估值算法並且使用startValue和endValue參數 來做算法。


自定義估值算法:




代碼如下:




理一下思路:


  1. 首先創建Point類用來保存座標信息,Point對象即是我們要操作的對象 。


  2. 自定義估值算法 MyTypeEvaluator,並制定 泛型爲Point類型,在 evaluate方法 中進行估值算法,爲point對象的x和y賦值並將該對象返回。


  3. MainActivity 中調用 ValueAnimator.ofObject() 方法獲得 ValueAnimator 對象,並傳入 自定義估值器對象 和 Point的初始對象與終止對象


  4. ValueAnimator 對象設置 AnimatorUpdateListener 監聽,在 onAnimationUpdate() 中通過 Point point = (Point) animation.getAnimatedValue();即可獲得在估值算法中返回的Point對象,併爲小球設置新的x和y值。


運行效果如下:




沒錯,小球從(0,0)座標運行到了(200,300)座標,並按我們預想的軌跡運行。


轉自:http://blog.csdn.net/qq_24530405/article/details/50630744


發佈了46 篇原創文章 · 獲贊 20 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章