Android屬性動畫:插值器與估值器


聲明:本篇文章部分內容來自《Android開發藝術探索》。

 

我們都知道對於屬性動畫可以對某個屬性做動畫,而 插值器(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類,如下所示:

 1 /**
 2  * 保存小球坐標的類
 3  */
 4 public class Point {
 5     float x;
 6     float y;
 7     
 8     public Point(){}
 9     
10     public Point(float x,float y){
11         this.x = x;
12         this.y = y;
13     }
14 }

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)坐標,並按我們預想的軌跡運行。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM