自定義View(四)-動畫- Interpolator與Evaluator

介紹

Interpolator插值器之前我們已經接觸過了堕花,而Evaluator好像我們還沒有將,這是屬性動畫中倆個比較中的兩個知識點,弄清楚它們有助于我們更好的使用與理解屬性動畫雕擂。


Interpolator插值器

  • <font color=#FF4500 size=3>分析</font>

之前我們已經明白了它的作用了,他就是一個控制動畫如何運動的一個工具贱勃。比如有勻速效果插值器井赌,回彈效果的插值器等等。現在我們就來從源碼的角度分析下看看他是如何實現的贵扰。我們以LinearInterpolator來分析插值器(在api22以后繼承BaseInterpolator仇穗,但是邏輯是一樣的)

public class LinearInterpolator extends Interpolator   {

    public LinearInterpolator() {
    }

    public LinearInterpolator(Context context, AttributeSet attrs) {
    }

    public float getInterpolation(float input) {
        return input;
    }
}

LinearInterpolator實現了Interpolator接口;而Interpolator接口則直接繼承自TimeInterpolator戚绕,而且并沒有添加任何其它的方法纹坐。
那我們來看看TimeInterpolator接口都有哪些函數吧:

·
    /** 
     * A time interpolator defines the rate of change of an animation. This allows animations 
     * to have non-linear motion, such as acceleration and deceleration. 
     */  
    public interface TimeInterpolator {  
      
        /** 
         * Maps a value representing the elapsed fraction of an animation to a value that represents 
         * the interpolated fraction. This interpolated value is then multiplied by the change in 
         * value of an animation to derive the animated value at the current elapsed animation time. 
         * 
         * @param input A value between 0 and 1.0 indicating our current point 
         *        in the animation where 0 represents the start and 1.0 represents 
         *        the end 
         * @return The interpolation value. This value can be more than 1.0 for 
         *         interpolators which overshoot their targets, or less than 0 for 
         *         interpolators that undershoot their targets. 
         */  
        float getInterpolation(float input);  
    }  

這里是TimeInterpolator的代碼,它里面只有一個函數float getInterpolation(float input);我們來講講這個函數是干什么的舞丛。
參數input: input參數是一個float類型恰画,它取值范圍是0到1,表示當前動畫的進度瓷马,取0時表示動畫剛開始拴还,取1時表示動畫結束,取0.5時表示動畫中間的位置欧聘,其它類推片林。
返回值: 表示當前實際想要顯示的進度。取值可以超過1也可以小于0怀骤,超過1表示已經超過目標值费封,小于0表示小于開始位置。正是因為每種插值器返回值不同才形成了不同運動效果的動畫
對于input參數蒋伦,它表示的是當前動畫的進度弓摘,勻速增加的。什么叫動畫的進度痕届,動畫的進度就是動畫在時間上的進度韧献,與我們的任何設置無關,隨著時間的增長研叫,動畫的進度自然的增加锤窑,從0到1;input參數相當于時間的概念嚷炉,我們通過setDuration()指定了動畫的時長渊啰,在這個時間范圍內,動畫進度肯定是一點點增加的;就相當于我們播放一首歌绘证,這首歌的進度是從0到1是一樣的隧膏。
而返回值則表示動畫的數值進度,它的對應的數值范圍是我們通過ofInt(),ofFloat()來指定的嚷那,這個返回值就表示當前時間所對應的數值的進度胞枕。
我們而我們上篇中在AnimatorUpdateListener()監(jiān)聽的值是如何等到的呢?這里我們得到的都是關于浮點型的小數车酣,但是我們在打印的時候得到的卻是具體變化的數值,那么又是如何得到具體的數值的呢索绪?
其實我們在AnimatorUpdateListener()監(jiān)聽中得到的數值值通過一個公式:

當前的值= 開始的值 + 當前的進度 * (結束的進度 - 開始的進度)

例如我們設置ValueAnimator.ofInt(100,400);動畫時間為2s湖员。當使用LinearInterpolator加速器(或是默認情況下)時間在1s時,顯示的進度為0.5(這里的顯示進度就是<font color=#006400>public float getInterpolation(float input)</font>方法返回的參數)瑞驱。那么計算的公式就為:

當前的值 = 100 + (400 - 100)* 0.5  

公式相當于我們做一個應用題:
小明從100的位置開始出發(fā)向400的位置開始跑去娘摔,在走到全程距離20%位置時,請問小明在哪個數字點上唤反?

  • <font color=#FF4500 size=3>自定義Interpolator插值器</font>
    其實Android安卓提供的插值器已經非常全了凳寺。我個人感覺沒有必要再去自定義插值器。當然既然我們都清楚原理了彤侍,那么就自定義一個自己的插值器試一下肠缨。 如下:
    public class MyInterploator implements TimeInterpolator {  
        @Override  
        public float getInterpolation(float input) {  
            return 1-input;  
        }  
    }  

自定義插值器注意2點:1.繼承TimeInterpolator 2.返回當前的顯示進度。這里我們將進度反轉過來盏阶,當傳0的時候晒奕,我們讓它數值進度在完成的位置,當完成的時候名斟,我們讓它在開始的位置脑慧。跟家復雜的可以參考自帶的其他插值器。
效果:


Evaluator估值器

TypeEvaluator估值器砰盐,他的作用是根據當期屬性的百分比來計算改變后的屬性值闷袒。Evaluator其實就是一個轉換器,他能把小數進度轉換成對應的數值位置岩梳。
先來看張圖(此圖來自Android自定義控件三部曲文章 )

image

這幅圖講述了從定義動畫的數字區(qū)間到通過AnimatorUpdateListener中得到當前動畫所對應數值的整個過程囊骤。下面我們對這四個步驟具體講解一下:
(1)、ofInt(0,400)表示指定動畫的數字區(qū)間冀值,是從0運動到400淘捡;
(2)、加速器:上面我們講了池摧,在動畫開始后焦除,通過加速器會返回當前動畫進度所對應的數字進度,但這個數字進度是百分制的作彤,以小數表示膘魄,如0.2
(3)乌逐、Evaluator:我們知道我們通過監(jiān)聽器拿到的是當前動畫所對應的具體數值,而不是百分制的進度创葡。那么就必須有一個地方會根據當前的數字進度浙踢,將其轉化為對應的數值,這個地方就是Evaluator灿渴;Evaluator就是將從加速器返回的數字進度轉成對應的數字值洛波。所以上部分中,我們講到的公式:

    當前的值 = 100 + (400 - 100)* 顯示進度  

(4)骚露、監(jiān)聽器:我們通過在AnimatorUpdateListener監(jiān)聽器使用animation.getAnimatedValue()函數拿到Evaluator中返回的數字值蹬挤。

在計算后就是得到ValueAnimator.addAnimatorUpdateListener()中變化的值。Android中已經為我們提供了IntEvaluator(針對Int類型估值器)棘幸,floatEvaluator(針對floatt類型估值器)和ArgbEvaluator(針對Color估值器)先不管ArgbEvaluator焰扳,IntEvaluator與floatEvaluator正好對應ValueAnimator.ofInt()與ValueAnimator.offloat(),如果你用.ofInt()那么在計算時就用IntEvaluator計算误续。.offloat()就用floatEvaluator吨悍,之前我們沒有設置適因為ofInt和ofFloat都是系統直接提供的函數,所以在使用時都會有默認的加速器和Evaluator來使用的蹋嵌,不指定則使用默認的育瓜。

以IntEvaluator我們分析下它的源碼,看看他是如何實現的:

·
    /** 
     * This evaluator can be used to perform type interpolation between <code>int</code> values. 
     */  
    public class IntEvaluator implements TypeEvaluator<Integer> {  
      
        /** 
         * This function returns the result of linearly interpolating the start and end values, with 
         * <code>fraction</code> representing the proportion between the start and end values. The 
         * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>, 
         * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>, 
         * and <code>t</code> is <code>fraction</code>. 
         * 
         * @param fraction   The fraction from the starting to the ending values 
         * @param startValue The start value; should be of type <code>int</code> or 
         *                   <code>Integer</code> 
         * @param endValue   The end value; should be of type <code>int</code> or <code>Integer</code> 
         * @return A linear interpolation between the start and end values, given the 
         *         <code>fraction</code> parameter. 
         */  
        public Integer evaluate(float fraction, Integer startValue, Integer endValue) {  
            int startInt = startValue;  
            return (int)(startInt + fraction * (endValue - startInt));  
        }  
    }  

首先IntEvaluator繼承了我們的TypeEvaluator估值器栽烂。因為是IntEvaluator所以指定的泛型類型為Integer爆雹,同時里面實現了一個方法,這個方法的參數的含義

  • <font color=#006400>fraction</font> 估值小數(就是加速器中的返回值愕鼓,表示當前動畫的數值進度钙态,百分制的小數表示。)
  • <font color=#006400>startValue</font> 動畫開始的值
  • <font color=#006400>startValue</font> 動畫結束的值
  • 返回值: 就是我們在AnimatorUpdateListener監(jiān)聽中得到具體運動的值菇晃。
    我們可以看到返回值的運算公式正是我們在插值器中所提到的公式
當前的值= 開始的值 + 當前的進度 * (結束的進度 - 開始的進度)

我們用圖片中的例子如果加載器為勻速加速器册倒,動畫時長為2s,在1s時(也就是動畫運動到一半的時候)磺送,這三個參數依次為0.5,100,400,此時在監(jiān)聽中得到的結果為:

    當前的值 = 100 + (400 - 100)* 0.5  

結果為250驻子。

  • <font color=#FF4500 size=3>自定義Evaluator估值器</font>
    理解了估值器以后我們就可以試著自定義自己的估值器。只需要實現TypeEvaluator接口估灿,并實現里面唯一的一個方法即可崇呵。
    下面提供2種簡單的自定義估值器:
  1. 簡單實現MyEvalutor
    2gif.gif
    3gif.gif
·
       public class MyEvaluator implements TypeEvaluator<Integer> {  
        @Override  
        public Integer evaluate(float fraction, Integer startValue, Integer endValue) {  
            int startInt = startValue;  
            return (int)(200+startInt + fraction * (endValue - startInt));  
        }  
    }  

這個插值器在原來的運動的點的值基礎上加200。效果對比如下:

  1. 實現倒序輸出實例
    1gif.gif
·
    public class ReverseEvaluator implements TypeEvaluator<Integer> {  
    @Override  
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {  
        int startInt = startValue;  
        return (int) (endValue - fraction * (endValue - startInt));  
    }  
}

其中 fraction * (endValue - startInt)表示動畫實際運動的距離馅袁,我們用endValue減去實際運動的距離就表示隨著運動距離的增加域慷,離終點越來越遠,這也就實現了從終點出發(fā),最終運動到起點的效果了犹褒。
總結: 在加速器中抵窒,我們可以通過自定義加速器的返回的數值進度來改變返回數值的位置。比如上面我們實現的倒序動畫
在Evaluator中叠骑,我們又可以通過改變進度值所對應的具體數字來改變數值的位置李皇。 所以<font color=#D02090 size=3>可以通過重寫加速器改變數值進度來改變數值位置,也可以通過改變Evaluator中進度所對應的數值來改變數值位置</font>宙枷。雖然2者都可以改變位置掉房。但是插值器(加速器)是根據運動是時間來決定當前進度的百分比。估值器是根據加速器的百分比來計算具體的數值慰丛。作用相同卓囚,職責不同。
我個人理解加速器側重運動的狀態(tài)(彈動璧帝,忽快忽慢等)估值器就是計算的作用(可以改變運動開始結束的位置)捍岳。


ArgbEvalutor顏色估值器

從一開始我們就說過屬性動畫可以改變動畫的顏色富寿。其實他就是通過ArgbEvalutor來做到的睬隶。為什么單獨把ArgbEvalutor拿出來呢?因為與前兩者的使用稍微有些不同页徐。使用如下:

`
                valueAnimator = ValueAnimator.ofInt(0xffff0000, 0xff0000ff);
                valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        int curValue = (int) animation.getAnimatedValue();
                        mTextView.setBackgroundColor(curValue);
                    }
                });
                 valueAnimator.setDuration(2000);
                 valueAnimator.setEvaluator(new ArgbEvaluator());
                 valueAnimator.start();

這里我們使用8位16進制數值表示A,R,G,B的顏色值苏潜。使用ofInt()來創(chuàng)建對象。顏色變化為由紅色(RED)變成藍色(BULE)效果圖如下:

4gif.gif

插值器基本原理变勇,與自定義插值器差不多也就這樣了恤左。 下面我們繼續(xù)往下看。


ValueAnimator.ofObject

之前在講到ValueAnimator我們提到了創(chuàng)建對象有.0fInt(),offloat()搀绣。但是他們只能傳入int與float類型的值飞袋。其實ValueAnimator還有一個方法就是ofObject()他可以傳入任意類型或是對象。他有什么用呢链患?我們測試一下巧鸭,看小面這個例子。

GIF.gif

在這里我們動態(tài)的將字母A-Z麻捻。實現了動態(tài)變換的效果纲仍。那么他是如何實現的呢?那我就先來了解下ofObject().

public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values);  

第一個參數是我們上面講到的估值器。第二個參數是我們要變換的值贸毕,和.0fInt(),offloat()一樣傳入的值越多郑叠,動畫效果變換越復雜。這里第二個參數好理解明棍。但是為什么要傳入估值器呢乡革?其實這里的估值器不是系統為我們預設的之前將的三種估值器的任意一種,而是自定義估值器。因為估值器的作用就是根據加速器返回的當前的進度來計算具體的值的署拟。如果我們不自己定義婉宰,那系統是沒有辦法計算具體的值的,比如上面的例子計算A-Z變化某一進度具體的值推穷,顯然我們講到的三種估值器都無法做到心包。所以我們需要自己定義自己的估值器。在明白了這一點使用起來就簡單多了馒铃。我們來看下代碼:

·  
        btn_object.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ValueAnimator valueAnimator = ValueAnimator.ofObject(new MyEvaluator(), 'A', 'Z');
                valueAnimator.setDuration(3000);
                valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        char text = (char) animation.getAnimatedValue();
                        mTextView.setText(String.valueOf(text));
                    }
                });
                valueAnimator.start();
            }
        });

    /**
     * 計算A-Z 具體的值的自定義估值器 
     */
    public class MyEvaluator implements TypeEvaluator<Character> {
        @Override
        public Character evaluate(float fraction, Character startValue, Character endValue) {
            int startInt = (int) startValue;
            int endInt = (int) endValue;
            int curInt = (int) (startInt + fraction * (endInt - startInt));
            char result = (char) curInt;
            return result;
        }
    }

java基礎好的同學都應該知道 A-Z對應ASCII碼為65-90 a-z為97-122蟹腾。所以我們在自定義估值器的時候就可以像上面那么些。核心公式也是我們上面提到的区宇,就不講解了娃殖。這段代碼也不難。這樣我們就可以實現文字變換的動畫议谷。其實ofObject方法非常強大炉爆,不僅可以針對控件的某一屬性(如文字)。甚至可以作用與對象本身卧晓。下面我們在舉一個例子:

1GIF.gif

具體實現如下:


·
//Activity調用
btn_point.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                point.doPointAnim();
            }
        });
        
//自定義View
public class MyPointView extends View {
    private Point mCurPoint;
    public MyPointView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mCurPoint!=null){
            Paint mPaint=new Paint();//畫筆 用來畫圓
            mPaint.setColor(Color.RED);//畫筆顏色
            mPaint.setStyle(Paint.Style.FILL);//畫筆的格式 這里用FILL畫出的圓就會填滿
            canvas.drawCircle(400f,600f,mCurPoint.getRadius(),mPaint);//400f,600f 表示基于屏幕圓心的X,Y軸坐標 mCurPoint.getRadius()表示圓的半徑
        }

    }
    public void doPointAnim(){
        ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(),new Point(20),new Point(200));
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mCurPoint = (Point)animation.getAnimatedValue();
                invalidate();//強制刷新 從而執(zhí)行onDraw()方法
            }
        });
        animator.setDuration(1000);
        animator.setInterpolator(new BounceInterpolator());//插值器 彈起效果
        animator.start();
    }  
    
//圓的實體類
public class Point {
    private int radius;//圓的半徑

    public Point(int radius){
        this.radius = radius;
    }

    public int getRadius() {
        return radius;
    }

    public void setRadius(int radius) {
        this.radius = radius;
    }
}

這樣我們就實現了傳入具體的對象芬首。根據對象的變化做動畫。雖然我們沒有講到自定義View用到的也不難逼裆,里面也沒有什么難度就不細講了郁稍。


結語

這里屬性動畫中的ValueAnimatot基本也就差不多了。其實學起來回頭看看也不是很難胜宇,在自定義View中重要是思路耀怜,平時還是已改多看看自定義View控件的文章,多學習多積累多練習才能熟練掌握桐愉,大家和我一起加油吧财破!

感謝

站在巨人的肩膀上可以讓我們看的更遠。
Android自定義控件三部曲文章

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末从诲,一起剝皮案震驚了整個濱河市左痢,隨后出現的幾起案子,更是在濱河造成了極大的恐慌盏求,老刑警劉巖抖锥,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異碎罚,居然都是意外死亡磅废,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門荆烈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拯勉,“玉大人竟趾,你說我怎么就攤上這事」停” “怎么了岔帽?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長导绷。 經常有香客問我犀勒,道長,這世上最難降的妖魔是什么妥曲? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任贾费,我火速辦了婚禮,結果婚禮上檐盟,老公的妹妹穿的比我還像新娘褂萧。我一直安慰自己,他們只是感情好葵萎,可當我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布导犹。 她就那樣靜靜地躺著,像睡著了一般羡忘。 火紅的嫁衣襯著肌膚如雪谎痢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天壳坪,我揣著相機與錄音舶得,去河邊找鬼掰烟。 笑死爽蝴,一個胖子當著我的面吹牛,可吹牛的內容都是我干的纫骑。 我是一名探鬼主播蝎亚,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼先馆!你這毒婦竟也來了发框?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤煤墙,失蹤者是張志新(化名)和其女友劉穎梅惯,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體仿野,經...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡铣减,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了脚作。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片葫哗。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡缔刹,死狀恐怖,靈堂內的尸體忽然破棺而出劣针,到底是詐尸還是另有隱情校镐,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布捺典,位于F島的核電站鸟廓,受9級特大地震影響,放射性物質發(fā)生泄漏襟己。R本人自食惡果不足惜肝箱,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望稀蟋。 院中可真熱鬧煌张,春花似錦、人聲如沸退客。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽萌狂。三九已至档玻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茫藏,已是汗流浹背误趴。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留务傲,地道東北人凉当。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像售葡,于是被迫代替她去往敵國和親看杭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內容