在上篇博客中主要和你們討論了屬性動畫的用法,相信經過上篇博客你們對屬性動畫的用法已經不陌生了,那麼今天就來你們一塊兒討論下屬性動畫的執行流程。算法
談到屬性動畫的執行流程,其實離不開插值器(Interpolator)和估值器(TypeEvaluator)的協同工做,本篇博客就來探究這二者是如何協同工做的。經過本篇博客你講學到如下知識點: ①理解Interpolator和TypeEvaluator各自的做用 ②理解Interpolator和TypeEvaluator是如何協同的工做的 ③如何自定義TypeEvaluator和Interpolator 好的廢話很少說進入正題,以前也談到過學習這些基礎的內容要參考官方文檔,那麼本篇也是同樣,官方文檔首先舉了一個以下的例子: 以下圖所示,描繪了一個假設的對象,它以x屬性爲動畫,表示其在屏幕上的水平位置。 動畫的持續時間設置爲40 ms,行駛距離爲40像素。 每10 ms,這是默認的幀刷新率,對象水平移動10像素。 在40ms結束時,動畫中止,物體在水平位置40處結束。這是具備線性插值的動畫的示例,意味着物體以恆定的速度移動。 bash
什麼意思呢?簡單來講就是上圖採用了一個勻速動畫,在40ms內,咱們假設的對象的位置從0勻速變爲40。 咱們能夠分析一下中間的某一個時刻,好比(x=20,t=20ms)當t=20ms的時候,時間流逝的百分比是0.5(20/40=0.5),可是這個百分比不是咱們所想要的,咱們關心的是x的變化,那麼怎麼知道x變化了多少呢?這時候就要用到插值器和估值器了,上述咱們說到是勻速運動,咱們就來看看線性插值器的源碼:/**
* An interpolator where the rate of change is constant
*/
@HasNativeInterpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public LinearInterpolator() {
}
public LinearInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return input;
}
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createLinearInterpolator();
}
複製代碼
從getInterpolator能夠看到線性插值器的返回值和輸入值是相同的。另外getInterpolator()方法接收一個參數input,這個參數的值會隨着動畫的運動而不斷變化,不過它的變化是有規律的,就是根據設定的動畫時長勻速增長,變化範圍是0到1。也就是說當動畫一開始的時候input的值是0,到動畫結束的時候input的值是1,而中間的值則是隨着動畫運行的時長在0到1之間變化的,它表示的是時間流逝的百分比,它的做用就是根據時間流逝的百分比計算出當前屬性值改變的百分比,在例中當t=20ms時,由於運行的總時長是40ms因此時間流逝的百分比爲20/40=0.5。咱們剛纔說過插值器的做用是拿到時間流逝的百分比,那麼咱們怎麼根據這個百分比來計算屬性值改變的百分比呢?這個時候就要用到估值器了,它的做用是根據當前屬性改變的百分比來計算改變後的屬性值。一樣咱們來看看系統提供的整形估算值算法的源碼:ide
public class IntEvaluator implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
複製代碼
能夠看到evaluate方法接收三個參數,第一個參數fraction這個參數很是重要它表示動畫的完成度,第二和第三個參數分表表示動畫的初始值和結束值。所以在上述代碼中用結束值減去初始值能夠得出它們之間的差,而後乘以fraction這個係數,再加上初始值,那麼獲得的就是動畫當前的值了。 到這裏你們應該能夠想到上面剛剛提到的input,那麼input和fraction有什麼關係或者區別呢?答案很簡單,input的值決定了fraction。而input的值是由系統通過計算後傳入到getInterpolation()方法中的,而後咱們能夠本身實現getInterpolator()方法中算法,根據input的值計算出一個返回值,而這個返回值就是fraction。理清楚這兩個值的關係特別重要。接着咱們整體來分析一下剛開始的那個實例,當t=20ms的時候,能夠計算出input的值是20/40=0.5,即動畫已經完成了50%了,getInterpolator()方法的返回值就是fraction,拿到fraction後,咱們就要根據它計算x屬性的變化了,IntEvaluator這個估值器中的evaluate的三個分別爲:0.5,0,40。代入後計算即:0+0.5*(40-0)=20。能夠看出與上圖是吻合的當t=20ms時,x=20。這就是屬性動畫執行的流程,固然咱們說的這種狀況是最簡單的一種狀況。學習
對於插值器和估值器來講,除了系統提供的外,咱們還能夠自定義。實現方式也很簡單,由於插值器和估值器都是一個接口,且內部都只有一個方法,咱們只要實現接口就能夠了,就能夠作出不少絢麗的動畫了。其中,自定義插值器須要實現Interpolator或者TimeInterpolator,自定義估值器須要實現TypeEvaluator。可是通常來講,插值器通常使用系統的就足夠了,估值器通常自定義會比較多,另外就是若是要對其餘類型(非Int丶float丶color)作動畫,必須自定義類型估值算法。接下來咱們就來自定義一個TypeEvaluator來講下自定義TypeEvaluator的流程,所實現的效果以下: 動畫
從上述效果能夠看到小球作的是拋物線運動,那麼如何能讓小球作拋物線運動呢?從屋裏學的角度來說若是知足x=400t,y=400t²,及x軸作勻速運動y軸作加速運動便可實現小球作拋物線運動。從表達式看只和時間有關係,可是根據時間的變化,橫向和縱向的移動速率是不一樣的,咱們應該如何實現呢?此時就要重寫TypeValue的時候了,由於咱們在時間變化的同時,須要返回給對象兩個值,x當前位置,y當前位置,而且須要知足x=400t,y=400t²。咱們能夠建立一個PointView類來保存x,y這兩個值。PointView的代碼以下public class PointView {
float x;
float y;
public PointView(){
}
public PointView(float x, float y) {
this.x=x;
this.y=y;
}
}
複製代碼
有了這兩個值以後咱們就能夠自定義一個TypeEvaluatorui
class PointViewEvaluator implements TypeEvaluator {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
PointView pointView=new PointView();
//fraction爲時間流逝的百分比
pointView.x=400*(fraction*2);
pointView.y=400*(fraction*2)*(fraction*2);
return pointView;
}
}
複製代碼
從自定義的PointViewEvaluator 中能夠看到PointView的x和y是知足x=400t,y=400t²這兩個表達式的,另外咱們將時間寫死了爲2s。自定義好以後咱們就能夠直接拿過來用了代碼以下this
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private ImageView ivBall;
private Button btnClick;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ivBall=findViewById(R.id.iv_ball);
btnClick=findViewById(R.id.btn_click);
btnClick.setOnClickListener(this);
}
@Override
public void onClick(View view) {
ValueAnimator valueAnimator=ValueAnimator.ofObject(new PointViewEvaluator(),new PointView(0,0));
valueAnimator.setDuration(2000);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
PointView pointView= (PointView) valueAnimator.getAnimatedValue();
ivBall.setX(pointView.x);
ivBall.setY(pointView.y);
}
});
valueAnimator.start();
}
}
複製代碼
從上述代碼中咱們能夠發現設置了LinearInterpolator線性的插值器,同時咱們給動畫添加了監聽,而後獲取當前PointView對象(至於爲何會返回PointView對象,能夠去看上一篇文章講解的很清晰Android屬性動畫詳解(一),屬性動畫基本用法)獲取的目的就是拿到PointView對象的x和y的值,而後將這兩個值設置給小球,就會表現爲拋物線運動。以上就是自定義TypeEvaluator的相關流程,理清楚關係邏輯其實很簡單,接下來我們一塊兒看看自定義Interpolator雖然用的比較少,可是若是用到也要知道怎麼去定義它。咱們定義一個動畫以最快的速度啓動,而後減速運動至一半,最後加速運動至結束效果圖以下 lua
對插值器的定義其實就是一個數學表達式 y=0.5 × ((2t − 1)3+1)與其對應的圖形以下 從圖形中能夠看到它是先加速而後加速再加速的一個過程,與其對應的代碼以下:public class PointViewInterpolator implements Interpolator {
@Override
public float getInterpolation(float t) {
float x=2.0f*t-1;
return 0.5f*(x*x*x + 1.0f);
}
}
複製代碼
不要忘記添加這句話:spa
valueAnimator.setInterpolator(new PointViewInterpolator());
複製代碼
而後再運行就會有上面的效果。 好了以上就是對Interpolator和TypeEvaluator的相關討論。若有謬誤歡迎批評指正。 經過本篇博客相信你們對於Interpolator和TypeEvaluator是如何協同工做的,以及對於這二者是如何自定義的相信你們都應該有個清晰的瞭解了。.net