鍍金池/ 教程/ Android/ 高級進(jìn)階(二)
聯(lián)合動畫的 XML 實現(xiàn)與使用示例
Interpolator 插值器
高級進(jìn)階(二)
ObjectAnimator 基本使用
ValueAnimator 基本使用
alpha、scale、translate、rotate、set 的 xml 屬性及用法
PropertyValuesHolder 與 Keyframe
layoutAnimation 與 gridLayoutAnimation
自定義控件三部曲之動畫篇(十三)——實現(xiàn)ListView Item進(jìn)入動畫
自定義控件三部曲之動畫篇(十二)——animateLayoutChanges與LayoutTransition
高級進(jìn)階(一)
代碼生成 alpha、scale、translate、rotate、set 及插值器動畫
聯(lián)合動畫的代碼實現(xiàn)

高級進(jìn)階(二)

通過上兩篇的講解,我們對 ValueAnimator 的動畫的整個過程應(yīng)該都已經(jīng)有較深入的理解,不過還有兩個概念我們還沒有講解關(guān)鍵幀和 ofObject(),關(guān)鍵幀的部分涉及問題比較多,我們將其放在系列的末尾再講,這篇著重講一下 ofObject 函數(shù)的使用

一、ofObject()概述

前面我們講了 ofInt()和 ofFloat()來定義動畫,但 ofInt()只能傳入 Integer 類型的值,而 ofFloat()則只能傳入 Float 類型的值。那如果我們需要操作其它類型的變量要怎么辦呢?其實 ValueAnimator 還有一個函數(shù) ofObject(),可以傳進(jìn)去任何類型的變量,定義如下:

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

它有兩個參數(shù),第一個是自定義的 Evaluator,第二個是可變長參數(shù),Object 類型的; 大家可能會疑問,為什么要強(qiáng)制傳進(jìn)去自定義的 Evaluator?首先,大家知道 Evaluator 的作用是根據(jù)當(dāng)前動畫的顯示進(jìn)度,計算出當(dāng)前進(jìn)度下把對應(yīng)的值。那既然 Object 對象是我們自定的,那必然從進(jìn)度到值的轉(zhuǎn)換過程也必須由我們來做,不然系統(tǒng)哪知道你要轉(zhuǎn)成個什么鬼。 好了,現(xiàn)在我們先簡單看一下 ofObject 這個怎么用。 我們先來看看我們要實現(xiàn)的效果:

http://wiki.jikexueyuan.com/project/android-animation/images/66.gif" alt="" />

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

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();  

這里注意三點(diǎn): 第一,構(gòu)造時:

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

我們自定義的一個 CharEvaluator,這個類實現(xiàn),后面會講;在初始化動畫時,傳進(jìn)去的是 Character 對象,一個是字母 A,一個是字母 Z; 我們這里要實現(xiàn)的效果是,對 Character 對象來做動畫,利用動畫自動從字母 A 變到字母 Z,具體怎么實現(xiàn)就是 CharEvaluator 的事了,這里我們只需要知道,在構(gòu)造時傳進(jìn)去的是兩個 Character 對象 第二:看監(jiān)聽:

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

通過 animation.getAnimatedValue()得到當(dāng)前動畫的字符,然后把字符設(shè)置給 textview;大家知道我們構(gòu)造時傳進(jìn)去的值類型是 Character 對象,所以在動畫過程中通過 Evaluator 返回的值類型必然跟構(gòu)造時的類型是一致的,也是 Character 第三:插值器

animator.setInterpolator(new AccelerateInterpolator());  

我們使用的是加速插值器,加速插值器的特點(diǎn)就是隨著動畫的進(jìn)行,速度會越來越快,這點(diǎn)跟我們上面的效果圖是一致的。 下面最關(guān)鍵的就是看 CharEvaluator 是怎么實現(xiàn)的了,先拋開的代碼,我們先講一個點(diǎn),ASCII 碼中數(shù)值與字符的轉(zhuǎn)換方法。 我們知道在 ASCII 碼表中,每個字符都是有數(shù)字跟他一一對應(yīng)的,字母 A 到字母 Z 之間的所有字母對應(yīng)的數(shù)字區(qū)間為 65 到 90; 而且在程序中,我們能通過數(shù)字強(qiáng)轉(zhuǎn)成對應(yīng)的字符。 比如: 數(shù)字轉(zhuǎn)字符:

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

字符轉(zhuǎn)數(shù)字:

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

在這里得到的 num 值就是對應(yīng)的 ASCII 碼值 65; 好了,在我們理解了 ASCII 碼數(shù)值與對應(yīng)字符的轉(zhuǎn)換原理之后,再來看看 CharEvaluator 的實現(xiàn):

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 碼表中對應(yīng)數(shù)字是連續(xù)且遞增的原理,先求出來對應(yīng)字符的數(shù)字值,然后再轉(zhuǎn)換成對應(yīng)的字符。代碼難度不大,就不再細(xì)講了。 源碼在文章底部給出 好了,到這里,有關(guān) ofObject()的使用大家應(yīng)該就會了,上面我們說過,ofObject()能夠初始化任何對象,下面我們就稍微加深些難度, 我們自定義一個類對象,然后利用 ofObject()來構(gòu)造這個對象的動畫。

二、ofObject 之自定義對象示例

我們先看看這部分,我們將實現(xiàn)的效果:

http://wiki.jikexueyuan.com/project/android-animation/images/67.gif" alt="" />

在這里,我們自定義了一個 View,在這個 view 上畫一個圓,但這個圓是有動畫效果的。從效果中可以看出使用的插值器應(yīng)該是回彈插值器(BounceInterpolator) 下面就來看看這個動畫是怎么做出來的

1、首先,我們自定義一個類 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 類內(nèi)容很簡單,只有一個成員變量:radius 表示當(dāng)前 point 的半徑。

2、然后我們自定義一個 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()函數(shù) 在這段代碼中,首先來看看供外部調(diào)用開始動畫的 doPointAnim()函數(shù):

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 的構(gòu)造動畫的方法:

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

在構(gòu)造動畫時,動畫所對應(yīng)的值的類型是 Point 對象,那說明我們自定義的 PointEvaluator 中的返回值也必然是 Point 了。有關(guān) PointEvaluator 的實現(xiàn)后面再講 然后再來看看動畫過程監(jiān)聽:

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

在監(jiān)聽過程中,先根據(jù) animation.getAnimatedValue()得到當(dāng)前動畫進(jìn)度所對應(yīng)的 Point 實例,保存在 mCurPoint 中,然后強(qiáng)制刷新 (2)、OnDraw()函數(shù) 在強(qiáng)制刷新之后,就會走到 OnDraw()函數(shù)下面:

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 函數(shù)沒什么難度,就是根據(jù) mCurPoint 的半徑在(300,300)的位置畫出來圓形,有關(guān)繪圖的知識大家可以參考另一個系列《android Graphics(一):概述及基本幾何圖形繪制》 (3)、PointEvaluator 在構(gòu)造 ofObject 中,我們也可以知道,初始值和動畫中間值的類型都是 Point 類型,所以 PointEvaluator 輸入的返回類型都應(yīng)該是 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);  
    }  
} 

這段代碼其實比較容易理解,就是根據(jù)初始半徑和最終半徑求出當(dāng)前動畫進(jìn)程所對應(yīng)的半徑值,然后新建一個 Point 對象返回。

3、使用 MyPointView

首先在 main.xml 中添加對應(yīng)的控件布局:從效果圖中也可以看到,我們將 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 控件,難度不大,不再細(xì)講了 然后我們來看看在 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();  
            }  
        });  
    }  
} 

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

源碼在文章底部給出

好了,這篇到這里就結(jié)束了,其實沒想再開這一篇單獨(dú)來講 ofObject 的,但上篇實在是太長了,所以只能再開一篇,這篇講了兩個實例,確實有些嘮叨,畢竟開了一篇還是要多寫點(diǎn),不然也太辜負(fù)這一篇文章的地了。就此,有關(guān) VauleAnimator 的所有問題我們都講完了,下一篇講會講述有關(guān) ObjectAnimator 系列的知識。

如果本文有幫到你,記得加關(guān)注哦

源碼下載地址:

csdn:http://download.csdn.net/detail/u013210620/9420359

github:https://github.com/harvic/BlogResForGitHub

請大家尊重原創(chuàng)者版權(quán),轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/harvic880925/article/details/50549385 謝謝