android.animation(3) - ValueAnimator-ofObject(TypeEvaluator evaluator, Object... values)

 

1、ofObject()概述

前面咱們講了ofInt()和ofFloat()來定義動畫,但ofInt()只能傳入Integer類型的值,而ofFloat()則只能傳入Float類型的值。那若是咱們須要操做其它類型的變量要怎麼辦呢?其實ValueAnimator還有一個函數ofObject(),能夠傳進去任何類型的變量,定義以下:java

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

它有兩個參數,第一個是自定義的Evaluator,第二個是可變長參數,Object類型的; 
你們可能會疑問,爲何要強制傳進去自定義的Evaluator?首先,你們知道Evaluator的做用是根據當前動畫的顯示進度,計算出當前進度下把對應的值。那既然Object對象是咱們自定的,那必然從進度到值的轉換過程也必須由咱們來作,否則系統哪知道你要轉成個什麼鬼。 
好了,如今咱們先簡單看一下ofObject這個怎麼用。 
咱們先來看看咱們要實現的效果: android

從效果圖中能夠看到,按鈕上的字母從A變化到Z,剛開始變的慢,後來逐漸加速;canvas

ValueAnimator animator = ValueAnimator.ofObject(new CharEvaluator(),new Character('A'),new Character('Z'));  
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
    @Override  
    public void onAnimationUpdate(ValueAnimator animation) {  
        char text = (char)animation.getAnimatedValue();  
        tv.setText(String.valueOf(text));  
    }  
});  
animator.setDuration(10000);  
animator.setInterpolator(new AccelerateInterpolator());  
animator.start();  

這裏注意三點: 
第一,構造時:ide

ValueAnimator animator = ValueAnimator.ofObject(new CharEvaluator(),new Character('A'),new Character('Z'));  

咱們自定義的一個CharEvaluator,這個類實現,後面會講;在初始化動畫時,傳進去的是Character對象,一個是字母A,一個是字母Z; 
咱們這裏要實現的效果是,對Character對象來作動畫,利用動畫自動從字母A變到字母Z,具體怎麼實現就是CharEvaluator的事了,這裏咱們只須要知道,在構造時傳進去的是兩個Character對象 函數

第二:看監聽:佈局

char text = (char)animation.getAnimatedValue();  
tv.setText(String.valueOf(text)); 

經過animation.getAnimatedValue()獲得當前動畫的字符,而後把字符設置給textview;你們知道咱們構造時傳進去的值類型是Character對象,因此在動畫過程當中經過Evaluator返回的值類型必然跟構造時的類型是一致的,也是Character 動畫

第三:插值器this

animator.setInterpolator(new AccelerateInterpolator()); 

咱們使用的是加速插值器,加速插值器的特色就是隨着動畫的進行,速度會愈來愈快,這點跟咱們上面的效果圖是一致的。 
下面最關鍵的就是看CharEvaluator是怎麼實現的了,先拋開的代碼,咱們先講一個點,ASCII碼中數值與字符的轉換方法。 
咱們知道在ASCII碼錶中,每一個字符都是有數字跟他一一對應的,字母A到字母Z之間的全部字母對應的數字區間爲65到90; 
並且在程序中,咱們能經過數字強轉成對應的字符。 
好比: lua

數字轉字符:spa

char  temp = (char)65;//獲得的temp的值就是大寫字母A  

字符轉數字:

char temp = 'A';  
int num = (int)temp;  

在這裏獲得的num值就是對應的ASCII碼值65; 
好了,在咱們理解了ASCII碼數值與對應字符的轉換原理以後,再來看看CharEvaluator的實現:

public class CharEvaluator 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;  
    }  
}  

在這裏,咱們就利用A-Z字符在ASCII碼錶中對應數字是連續且遞增的原理,先求出來對應字符的數字值,而後再轉換成對應的字符。代碼難度不大,就再也不細講了。 
好了,到這裏,有關ofObject()的使用你們應該就會了,上面咱們說過,ofObject()可以初始化任何對象,下面咱們就稍微加深些難度, 咱們自定義一個類對象,而後利用ofObject()來構造這個對象的動畫。

2、ofObject之自定義對象示例

咱們先看看這部分,咱們將實現的效果: 

在這裏,咱們自定義了一個View,在這個view上畫一個圓,但這個圓是有動畫效果的。從效果中能夠看出使用的插值器應該是回彈插值器(BounceInterpolator) 
下面就來看看這個動畫是怎麼作出來的

一、首先,咱們自定義一個類Point:

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;  
    }  
} 

point類內容很簡單,只有一個成員變量:radius表示當前point的半徑。

二、而後咱們自定義一個View:MyPointView

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 paint = new Paint();  
            paint.setAntiAlias(true);  
            paint.setColor(Color.RED);  
            paint.setStyle(Paint.Style.FILL);  
            canvas.drawCircle(300,300,mCurPoint.getRadius(),paint);  
        }  
    }  
  
    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();  
            }  
        });  
        animator.setDuration(1000);  
        animator.setInterpolator(new BounceInterpolator());  
        animator.start();  
    }  
}  

(1)、doPointAnim()函數

在這段代碼中,首先來看看供外部調用開始動畫的doPointAnim()函數

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();  
        }  
    });  
    animator.setDuration(1000);  
    animator.setInterpolator(new BounceInterpolator());  
    animator.start();  
}  

一樣,先來看ofObject的構造動畫的方法:

ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(),new Point(20),new Point(200));  

在構造動畫時,動畫所對應的值的類型是Point對象,那說明咱們自定義的PointEvaluator中的返回值也必然是Point了。有關PointEvaluator的實現後面再講 
而後再來看看動畫過程監聽:

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
    @Override  
    public void onAnimationUpdate(ValueAnimator animation) {  
        mCurPoint = (Point)animation.getAnimatedValue();  
        invalidate();  
    }  
}); 

在監聽過程當中,先根據animation.getAnimatedValue()獲得當前動畫進度所對應的Point實例,保存在mCurPoint中,而後強制刷新 

(2)、OnDraw()函數

在強制刷新以後,就會走到OnDraw()函數下面:

protected void onDraw(Canvas canvas) {  
    super.onDraw(canvas);  
    if (mCurPoint != null){  
        Paint paint = new Paint();  
        paint.setAntiAlias(true);  
        paint.setColor(Color.RED);  
        paint.setStyle(Paint.Style.FILL);  
        canvas.drawCircle(300,300,mCurPoint.getRadius(),paint);  
    }  
} 

onDraw函數沒什麼難度,就是根據mCurPoint的半徑在(300,300)的位置畫出來圓形,有關繪圖的知識你們能夠參考另外一個系列《android Graphics(一):概述及基本幾何圖形繪製》 

(3)、PointEvaluator

在構造ofObject中,咱們也能夠知道,初始值和動畫中間值的類型都是Point類型,因此PointEvaluator輸入的返回類型都應該是Point類型的,先看看PointEvaluator的完整代碼:

public class PointEvaluator implements TypeEvaluator<Point> {  
    @Override  
    public Point evaluate(float fraction, Point startValue, Point endValue) {  
        int start = startValue.getRadius();  
        int end  = endValue.getRadius();  
        int curValue = (int)(start + fraction * (end - start));  
        return new Point(curValue);  
    }  
} 

這段代碼其實比較容易理解,就是根據初始半徑和最終半徑求出當前動畫進程所對應的半徑值,而後新建一個Point對象返回。

三、使用MyPointView

首先在main.xml中添加對應的控件佈局:從效果圖中也能夠看到,咱們將MyPointView是佈局在最下方的,佈局代碼以下:

<?xml version="1.0" encoding="utf-8"?>  
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
                android:orientation="vertical"  
                android:layout_width="fill_parent"  
                android:layout_height="fill_parent">  
  
    <Button  
            android:id="@+id/btn"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:layout_alignParentLeft="true"  
            android:padding="10dp"  
            android:text="start anim"  
            />  
  
    <Button  
            android:id="@+id/btn_cancel"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:layout_alignParentRight="true"  
            android:padding="10dp"  
            android:text="cancel anim"  
            />  
    <TextView  
            android:id="@+id/tv"  
            android:layout_width="100dp"  
            android:layout_height="wrap_content"  
            android:layout_centerHorizontal="true"  
            android:gravity="center"  
            android:padding="10dp"  
            android:background="#ffff00"  
            android:text="Hello qijian"/>  
  
    <com.harvic.BlogValueAnimator4.MyPointView  
            android:id="@+id/pointview"  
            android:layout_below="@id/tv"  
            android:layout_width="match_parent"  
            android:layout_height="match_parent"/>  
</RelativeLayout>  

其實也沒什麼難度,就是在原來的佈局代碼下面加一個MyPointView控件,難度不大,再也不細講了 
而後咱們來看看在MyActivity.java中是怎麼來用的吧

public class MyActivity extends Activity {  
    private Button btnStart;  
    private MyPointView mPointView;  
  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
  
        btnStart = (Button) findViewById(R.id.btn);  
        mPointView = (MyPointView)findViewById(R.id.pointview);  
  
        btnStart.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                mPointView.doPointAnim();  
            }  
        });  
    }  
}  

這段代碼沒什麼難度,就是在點擊start anim按鈕的時候,調用mPointView.doPointAnim()方法開始動畫。 

本文轉自:自定義控件三部曲之動畫篇(六)——ValueAnimator高級進階(二)

相關文章
相關標籤/搜索