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

ObjectAnimator 基本使用

一、概述

1、引入

上幾篇給大家講了 ValueAnimator,但 ValueAnimator 有個缺點,就是只能對數值對動畫計算。我們要想對哪個控件操作,需要監(jiān)聽動畫過程,在監(jiān)聽中對控件操作。這樣使用起來相比補間動畫而言就相對比較麻煩。 為了能讓動畫直接與對應控件相關聯,以使我們從監(jiān)聽動畫過程中解放出來,谷歌的開發(fā)人員在 ValueAnimator 的基礎上,又派生了一個類 ObjectAnimator; 由于 ObjectAnimator 是派生自 ValueAnimator 的,所以 ValueAnimator 中所能使用的方法,在 ObjectAnimator 中都可以正常使用。 但 ObjectAnimator 也重寫了幾個方法,比如 ofInt(),ofFloat()等。我們先看看利用 ObjectAnimator 重寫的 ofFloat 方法如何實現一個動畫:(改變透明度)

ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"alpha",1,0,1);  
animator.setDuration(2000);  
animator.start(); 

效果圖如下:

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

我們這里還是直接使用上一篇的框架代碼;(當點擊 start anim 時執(zhí)行動畫)從上面的代碼中可以看到構造 ObjectAnimator 的方法非常簡單:

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) 
  • 第一個參數用于指定這個動畫要操作的是哪個控件
  • 第二個參數用于指定這個動畫要操作這個控件的哪個屬性
  • 第三個參數是可變長參數,這個就跟 ValueAnimator 中的可變長參數的意義一樣了,就是指這個屬性值是從哪變到哪。像我們上面的代碼中指定的就是將 textview 的 alpha 屬性從 0 變到 1 再變到 0; 下面我們再來看一下如何實現旋轉效果:
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotation",0,180,0);  
animator.setDuration(2000);  
animator.start();

效果圖如下:

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

從代碼中可以看到,我們只需要改變 ofFloat()的第二個參數的值就可以實現對應的動畫; 那么問題來了,我們怎么知道第二個參數的值是啥呢?

2、setter 函數

我們再回來看構造改變 rotation 值的 ObjectAnimator 的方法

ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotation",0,180,0);

TextView 控件有 rotation 這個屬性嗎?沒有,不光 TextView 沒有,連它的父類 View 中也沒有這個屬性。那它是怎么來改變這個值的呢?其實,ObjectAnimator 做動畫,并不是根據控件 xml 中的屬性來改變的,而是通過指定屬性所對應的 set 方法來改變的。比如,我們上面指定的改變 rotation 的屬性值,ObjectAnimator 在做動畫時就會到指定控件(TextView)中去找對應的 setRotation()方法來改變控件中對應的值。同樣的道理,當我們在最開始的示例代碼中,指定改變”alpha”屬性值的時候,ObjectAnimator 也會到 TextView 中去找對應的 setAlpha()方法。那 TextView 中都有這些方法嗎,有的,這些方法都是從 View 中繼承過來的,在 View 中有關動畫,總共有下面幾組 set 方法:

//1、透明度:alpha  
public void setAlpha(float alpha)  

//2、旋轉度數:rotation、rotationX、rotationY  
public void setRotation(float rotation)  
public void setRotationX(float rotationX)  
public void setRotationY(float rotationY)  

//3、平移:translationX、translationY  
public void setTranslationX(float translationX)   
public void setTranslationY(float translationY)  

//縮放:scaleX、scaleY  
public void setScaleX(float scaleX)  
public void setScaleY(float scaleY) 

可以看到在 View 中已經實現了有關 alpha,rotaion,translate,scale 相關的 set 方法。所以我們在構造 ObjectAnimator 時可以直接使用。 在開始逐個看這些函數的使用方法前,我們先做一個總結: 1、要使用 ObjectAnimator 來構造對畫,要操作的控件中,必須存在對應的屬性的 set 方法 2、setter 方法的命名必須以駱駝拼寫法命名,即 set 后每個單詞首字母大寫,其余字母小寫,即類似于 setPropertyName 所對應的屬性為 propertyName 下面我們就來看一下上面中各個方法的使用方法及作用。 有關 alpha 的用法,上面已經講過了,下面我們來看看其它的

(1)、setRotationX、setRotationY 與 setRotation

  • setRotationX(float rotationX):表示圍繞 X 軸旋轉,rotationX 表示旋轉度數
  • setRotationY(rotationY):表示圍繞 Y 軸旋轉,rotationY 表示旋轉度數
  • setRotation(float rotation):表示圍繞 Z 旋轉,rotation 表示旋轉度數

先來看看 setRotationX 的效果:

ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotationX",0,270,0);  
animator.setDuration(2000);  
animator.start();

效果圖如下:

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

從效果圖中明顯看出,textview 的旋轉方法是圍繞 X 軸旋轉的,我們設定為從 0 度旋轉到 270 度再返回 0 度。 然后再來看看 setRotationY 的使用方法與效果:

ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotationY",0,180,0);  
animator.setDuration(2000);  
animator.start();

效果圖如下:

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

從效果圖中明顯可以看出圍繞 Y 軸旋轉的。 我們再來看看 setRotation 的用法與效果:

ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotation",0,270,0);  
animator.setDuration(2000);  
animator.start();  

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

我們上面說了,setRotation 是圍繞 Z 軸旋轉的,可能有些同學不理解什么是 Z 軸,我們來看一張圖:

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

從這張圖中,綠色框部分表示手機屏幕,很明顯可以看出 Z 軸就是從屏幕左上角原點向外伸出的一條軸。這樣,我們也就可以理解圍繞 Z 軸旋轉,為什么是這樣子轉了。

(2)、setTranslationX 與 setTranslationY

  • setTranslationX(float translationX) :表示在 X 軸上的平移距離,以當前控件為原點,向右為正方向,參數 translationX 表示移動的距離。
  • setTranslationY(float translationY) :表示在 Y 軸上的平移距離,以當前控件為原點,向下為正方向,參數 translationY 表示移動的距離。 我們先看看 setTranslationX 的用法:
ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "translationX", 0, 200, -200,0);  
animator.setDuration(2000);  
animator.start();  

效果圖如下:

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

所以,我們上面在構造動畫時,指定的移動距離是(0, 200, -200,0),所以控件會從自身所有位置向右移動 200 像素,然后再移動到距離原點-200 的位置,最后回到原點; 然后我們來看看 setTranslateY 的用法:

ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "translationY", 0, 200, -100,0);  
animator.setDuration(2000);  
animator.start(); 

效果圖如下:(為了方便看到效果,將 textview 垂直居中)

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

同樣,移動位置的坐標也都是以當前控件所在位置為中心點的。所以對應的移動位置從原點移動向下移動 200 像素,然后再移動到向下到距原點 200 像素的位置,最后再回到(0,0)從效果圖中很明顯可以看出來。 從上面可以看出:每次移動距離的計算都是以原點為中心的;比如初始動畫為 ObjectAnimator.ofFloat(tv, “translationY”, 0, 200, -100,0)表示首先從 0 移動到正方向 200 的位置,然后再移動到負方向 100 的位置,最后移動到原點。

(3)、setScaleX 與 setScaleY

  • setScaleX(float scaleX):在 X 軸上縮放,scaleX 表示縮放倍數
  • setScaleY(float scaleY):在 Y 軸上縮放,scaleY 表示縮放倍數 我們來看看 setScaleX 的用法:
ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "scaleX", 0, 3, 1);  
animator.setDuration(2000);  
animator.start();

效果圖如下:

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

在效果圖中,從 0 倍放大到 3 倍,然后再還原到 1 倍的原始狀態(tài)。 然后再來看看 setScaleY 的用法

ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "scaleY", 0, 3, 1);  
animator.setDuration(2000);  
animator.start(); 

為了更好的看到效果,我把 textview 垂直居中了,效果圖如下:

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

源碼在文章底部給出 好了,到這里有關 View 中自帶的 set 函數講完了,我們來看看 ObjectAnimator 是如何實現控件動畫效果的。

3、ObjectAnimator 動畫原理

我們先來看張圖:

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

在這張圖中,將 ValueAnimator 的動畫流程與 ObjectAnimator 的動畫流程做了個對比。 可以看到 ObjectAnimator 的動畫流程中,也是首先通過加速器產生當前進度的百分比,然后再經過 Evaluator 生成對應百分比所對應的數字值。這兩步與 ValueAnimator 是完全一樣的,唯一不同的是最后一步,在 ValueAnimator 中,我們要通過添加監(jiān)聽器來監(jiān)聽當前數字值。而在 ObjectAnimator 中,則是先根據屬性值拼裝成對應的 set 函數的名字,比如這里的 scaleY 的拼裝方法就是將屬性的第一個字母強制大寫后,與 set 拼接,所以就是 setScaleY。然后通過反射找到對應控件的 setScaleY(float scaleY)函數,將當前數字值做為 setScaleY(float scale)的參數將其傳入。 這里在找到控件的 set 函數以后,是通過反射來調用這個函數的,有關反射的使用大家可以參考《夯實 JAVA 基本之二 —— 反射(1):基本類周邊信息獲取》 這就是 ObjectAnimator 的流程,最后一步總結起來就是調用對應屬性的 set 方法,將動畫當前數字值做為參數傳進去。

根據上面的流程,這里有幾個注意事項: (1)、拼接 set 函數的方法:上面我們也說了是首先是強制將屬性的第一個字母大寫,然后與 set 拼接,就是對應的 set 函數的名字。注意,只是強制將屬性的第一個字母大寫,后面的部分是保持不變的。反過來,如果我們的函數名命名為 setScalePointX(float ),那我們在寫屬性時可以寫成”scalePointX”或者寫成“ScalePointX”都是可以的,即第一個字母大小寫可以隨意,但后面的部分必須與 set 方法后的大小寫保持一致。 (2)、如何確定函數的參數類型:上面我們知道了如何找到對應的函數名,那對應的參數方法的參數類型如何確定呢?我們在講 ValueAnimator 的時候說過,動畫過程中產生的數字值與構造時傳入的值類型是一樣的。由于 ObjectAnimator 與 ValueAnimator 在插值器和 Evaluator 這兩步是完全一樣的,而當前動畫數值的產生是在 Evaluator 這一步產生的,所以 ObjectAnimator 的動畫中產生的數值類型也是與構造時的類型一樣的。那么問題來了,像我們的構造方法。

ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "scaleY", 0, 3, 1);

由于構造時使用的是 ofFloat 函數,所以中間值的類型應該是 Float 類型的,所以在最后一步拼裝出來的 set 函數應該是 setScaleY(float xxx)的樣式;這時,系統就會利用反射來找到 setScaleY(float xxx)函數,并把當前的動畫數值做為參數傳進去。 那問題來了,如果沒有類似 setScaleY(float xxx)的函數,我們只實現了一個 setScaleY(int xxx)的函數怎么辦?這里雖然函數名一樣,但參數類型是不一樣的,那么系統就會報一個錯誤:

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

意思就是對應函數的指定參數類型沒有找到。 (3)、調用 set 函數以后怎么辦?從 ObjectAnimator 的流程可以看到,ObjectAnimator 只負責把動畫過程中的數值傳到對應屬性的 set 函數中就結束了,注意傳給 set 函數以后就結束了!set 函數就相當我們在 ValueAnimator 中添加的監(jiān)聽的作用,set 函數中的對控件的操作還是需要我們自己來寫的。

那我們來看看 View 中的 setScaleY 是怎么實現的吧:

/** 
 * Sets the amount that the view is scaled in Y around the pivot point, as a proportion of 
 * the view's unscaled width. A value of 1 means that no scaling is applied. 
 * 
 * @param scaleY The scaling factor. 
 * @see #getPivotX() 
 * @see #getPivotY() 
 * 
 * @attr ref android.R.styleable#View_scaleY 
 */  
public void setScaleY(float scaleY) {  
    ensureTransformationInfo();  
    final TransformationInfo info = mTransformationInfo;  
    if (info.mScaleY != scaleY) {  
        invalidateParentCaches();  
        // Double-invalidation is necessary to capture view's old and new areas  
        invalidate(false);  
        info.mScaleY = scaleY;  
        info.mMatrixDirty = true;  
        mPrivateFlags |= DRAWN; // force another invalidation with the new orientation  
        invalidate(false);  
    }  
} 

大家不必理解這一坨代碼的意義,因為這些代碼是需要讀懂 View 的整體流程以后才能看得懂的,只需要跟著我的步驟來理解就行。這段代碼總共分為兩部分:第一步重新設置當前控件的參數,第二步調用 Invalidate()強制重繪; 所以在重繪時,控件就會根據最新的控件參數來繪制了,所以我們就看到當前控件被縮放了。 (4)、set 函數調用頻率是多少:由于我們知道動畫在進行時,每隔十幾毫秒會刷新一次,所以我們的 set 函數也會每隔十幾毫秒會被調用一次。 講了這么多,就是為了強調一點:ObjectAnimator 只負責把當前運動動畫的數值傳給 set 函數。至于 set 函數里面怎么來做,是我們自己的事了。 好了,在知道了 ObjectAnimator 的原理以后,下面就來看看如何自定義一個 ObjectAnimator 的屬性吧。

二、自定義 ObjectAnimator 屬性

上面我們已經看了使用 View 自帶的 set 函數所對應屬性的方法,而且理解了 ObjectAnimator 的動畫實現原理,下面我們來自定義一個屬性來看看實現效果吧。 我們在開始之前再來捋一下 ObjectAnimator 的動畫設置流程:ObjectAnimator 需要指定操作的控件對象,在開始動畫時,到控件類中去尋找設置屬性所對應的 set 函數,然后把動畫中間值做為參數傳給這個 set 函數并執(zhí)行它。 所以,我們說了,控件類中必須所要設置屬性所要對應的 set 函數。所以為了自由控制控件的實現,我們這里自定義一個控件。大家知道在這個自定義控件中,肯定存在一個 set 函數與我們自定義的屬性相對應。 我們先來看看這段要實現的效果:

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

這個效果圖與我們上篇自定義控件實現的效果差不多,這個控件中存在一個圓形,也是在動畫時先將這個圓形放大,然后再將圓形還原。

1、保存圓形信息類——Point

為了,保存圓形的信息,我們先定義一個類:(Point.java)

public class Point {  
    private int mRadius;  

    public Point(int radius){  
        mRadius = radius;  
    }  

    public int getRadius() {  
        return mRadius;  
    }  

    public void setRadius(int radius) {  
        mRadius = radius;  
    }  
}  

這個類很好理解,只有一個成員變量 mRadius,表示圓的半徑。

2、自定義控件——MyPointView

然后我們自定義一個控件 MyPointView,完整代碼如下:

public class MyPointView extends View {  
    private Point mPoint = new Point(100);  

    public MyPointView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  

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

    void setPointRadius(int radius){  
        mPoint.setRadius(radius);  
        invalidate();  
    }  

}  

在這段代碼中,首先來看我們前面講到的 set 函數:

void setPointRadius(int radius){  
    mPoint.setRadius(radius);  
    invalidate();  
}  

第一點,這個 set 函數所對應的屬性應該是 pointRadius 或者 PointRadius。前面我們已經講了第一個字母大小寫無所謂,后面的字母必須保持與 set 函數完全一致。 第二點,在 setPointRadius 中,先將當前動畫傳過來的值保存到 mPoint 中,做為當前圓形的半徑。然后強制界面刷新 在界面刷新后,就開始執(zhí)行 onDraw()函數:

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

在 onDraw 函數中,就是根據當前 mPoint 的半徑值在(300,300)點外畫一個圓;有關畫圓的知識,大家可以參考《android Graphics(一):概述及基本幾何圖形繪制》

3、使用 MyPointView

首先,在 MyActivity 的布局中添加 MyPointView 的使用(main.xml):

<?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.example.BlogObjectAnimator1.MyPointView  
            android:id="@+id/pointview"  
            android:layout_width="match_parent"  
            android:layout_height="match_parent"  
            android:layout_below="@id/tv"/>  

</RelativeLayout> 

布局代碼很好理解,根據效果圖中的布局效果來理解,非常容易,就不再多講 然后看看在 MyActivity 中,點擊 start anim 后的處理方法:

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) {  
                doPointViewAnimation();  
            }  
        });  
    }  
  …………  
} 

在點擊 start anim 按鈕后,開始執(zhí)行 doPointViewAnimation()函數,doPointViewAnimation()函數代碼如下:

private void doPointViewAnimation(){  
     ObjectAnimator animator = ObjectAnimator.ofInt(mPointView, "pointRadius", 0, 300, 100);  
      animator.setDuration(2000);  
      animator.start();  
}  

在這段代碼中,著重看 ObjectAnimator 的構造方法,首先要操作的控件對象是 mPointView,然后對應的屬性是 pointRadius,然后值是從 0 到 300 再到 100; 所以在動畫開始以后,ObjectAnimator 就會實時地把動畫中產生的值做為參數傳給 MyPointView 類中的 setPointRadius(int radius)函數,然后調用 setPointRadius(int radius)。由于我們在 setPointRadius(int radius)中實時地設置圓形的半徑值然后強制重繪當前界面,所以可以看到圓形的半徑會隨著動畫的進行而改變。 源碼在文章底部給出

四、注意——何時需要實現對應屬性的 get 函數

我們再來看一下 ObjectAinimator 的下面三個構造方法:

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)  
public static ObjectAnimator ofInt(Object target, String propertyName, int... values)  
public static ObjectAnimator ofObject(Object target, String propertyName,TypeEvaluator evaluator, Object... values) 

前面我們已經分別講過三個函數的使用方法,在上面的三個構造方法中最后一個參數都是可變長參數。我們也講了,他們的意義就是從哪個值變到哪個值的。 那么問題來了:前面我們都是定義多個值,即至少兩個值之間的變化,那如果我們只定義一個值呢,如下面的方式:(同樣以 MyPointView 為例)

ObjectAnimator animator = ObjectAnimator.ofInt(mPointView, "pointRadius",100);

我們在這里只傳遞了一個變化值 100;那它從哪里開始變化呢?我們來看一下效果: 代碼如下:

ObjectAnimator animator = ObjectAnimator.ofInt(mPointView, "pointRadius",100);  
animator.setDuration(2000);  
animator.start();  

效果圖如下:

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

從效果圖中看起來是從 0 開始的,但是看 log 可以看出來已經在出警告了:

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

我們點了三次 start anim 按鈕,所以這里也報了三次,意思就是沒找到 pointRadius 屬性所對應的 getPointRadius()函數; 僅且僅當我們只給動畫設置一個值時,程序才會調用屬性對應的 get 函數來得到動畫初始值。如果動畫沒有初始值,那么就會使用系統默認值。比如 ofInt()中使用的參數類型是 int 類型的,而系統的 Int 值的默認值是 0,所以動畫就會從 0 運動到 100;也就是系統雖然在找到不到屬性對應的 get 函數時,會給出警告,但同時會用系統默認值做為動畫初始值。 如果通過給自定義控件 MyPointView 設置了 get 函數,那么將會以 get 函數的返回值做為初始值:

public class MyPointView extends View {  
    private Point mPoint = new Point(100);  

    public MyPointView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  

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

    public int getPointRadius(){  
        return 50;  
    }  

    public void setPointRadius(int radius){  
        mPoint.setRadius(radius);  
        invalidate();  
    }  

} 

我們在這里添加了 getPointRadius 函數,返回值是 Int.有些同學可能會疑惑:我怎么知道這里要返回 int 值呢? 我們前面說過當且僅當我們在創(chuàng)建 ObjectAnimator 時,只給他傳遞了一個過渡值的時候,系統才會調用屬性對應的 get 函數來得到動畫的初始值!所以做為動畫的初始值,那么在創(chuàng)建動畫時過渡值傳的什么類型,這里的 get 函數就要返回類型

public static ObjectAnimator ofObject(Object target, String propertyName,TypeEvaluator evaluator, Object... values)

比如上面的 ofObject,get 函數所返回的類型就是與最后一個參數 Object... values,相同類型的。 在我們在 MyPointView 添加上 PointRadius 所對應的 get 函數以后重新執(zhí)行動畫:

ObjectAnimator animator = ObjectAnimator.ofInt(mPointView, "pointRadius",100);  
animator.setDuration(2000);  
animator.start();  

此時的效果圖如下:

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

從動畫中可以看出,半徑已經不是從 0 開始的了,而是從 50 開始的。 最后我們總結一下:當且僅當動畫的只有一個過渡值時,系統才會調用對應屬性的 get 函數來得到動畫的初始值。

源碼在文章底部給出

三、常用函數

有關常用函數這一節(jié)其實沒有太多講的必要。因為 ObjectAnimator 的函數都是從 ValueAnimator 中繼承而來的,所以用法和效果與 ValueAnimator 是完全一樣的。我們這里只講解一下 Evaluator 的用法,其它的也就不再講了。

1、使用 ArgbEvaluator

我們搜一下 TextView 所有的函數發(fā)現,TextView 有一個 set 函數能夠改變背景色:

public void setBackgroundColor(int color);  

大家可以回想到,我們在 ValueAnimator 中也曾改變過背景色,使用的是 ArgbEvaluator。在這里我們再回顧下 ArgbEvaluator,它的實現代碼如下:

public class ArgbEvaluator implements TypeEvaluator {  
    public Object evaluate(float fraction, Object startValue, Object endValue) {  
        int startInt = (Integer) startValue;  
        int startA = (startInt >> 24);  
        int startR = (startInt >> 16) & 0xff;  
        int startG = (startInt >> 8) & 0xff;  
        int startB = startInt & 0xff;  

        int endInt = (Integer) endValue;  
        int endA = (endInt >> 24);  
        int endR = (endInt >> 16) & 0xff;  
        int endG = (endInt >> 8) & 0xff;  
        int endB = endInt & 0xff;  

        return (int)((startA + (int)(fraction * (endA - startA))) << 24) |  
                (int)((startR + (int)(fraction * (endR - startR))) << 16) |  
                (int)((startG + (int)(fraction * (endG - startG))) << 8) |  
                (int)((startB + (int)(fraction * (endB - startB))));  
    }  
}  

有關它具體實現的原理,前面篇章中我們已經講過了,這里主要說一點,ArgbEvaluator 的返回值是 Integer 類型,所以我們要使用 ArgbEvaluator 的話,構造 ObjectAnimator 時必須使用 ofInt() 下面我們來看看使用 ArgbEvaluator 的代碼:

ObjectAnimator animator = ObjectAnimator.ofInt(tv, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);  
animator.setDuration(8000);  
animator.setEvaluator(new ArgbEvaluator());  
animator.start();

然后我們來看下代碼效果:

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

源碼在文章底部給出

2、其它函數

下面把其它所涉及到的函數的列表列在下面,大家可以參考 ValueAnimator 的使用方法來使用。有關自定義插值器和 Evaluator 的部分,可以參考《Animation 動畫詳解(五)——高級進階(一)》 (1)、常用函數

/** 
 * 設置動畫時長,單位是毫秒 
 */  
ValueAnimator setDuration(long duration)  
/** 
 * 獲取 ValueAnimator 在運動時,當前運動點的值 
 */  
Object getAnimatedValue();  
/** 
 * 開始動畫 
 */  
void start()  
/** 
 * 設置循環(huán)次數,設置為 INFINITE 表示無限循環(huán) 
 */  
void setRepeatCount(int value)  
/** 
 * 設置循環(huán)模式 
 * value 取值有 RESTART,REVERSE, 
 */  
void setRepeatMode(int value)  
/** 
 * 取消動畫 
 */  
void cancel()  
(2)、監(jiān)聽器相關
[java] view plain
/** 
 * 監(jiān)聽器一:監(jiān)聽動畫變化時的實時值 
 */  
public static interface AnimatorUpdateListener {  
    void onAnimationUpdate(ValueAnimator animation);  
}  
//添加方法為:public void addUpdateListener(AnimatorUpdateListener listener)  
/** 
 * 監(jiān)聽器二:監(jiān)聽動畫變化時四個狀態(tài) 
 */  
public static interface AnimatorListener {  
    void onAnimationStart(Animator animation);  
    void onAnimationEnd(Animator animation);  
    void onAnimationCancel(Animator animation);  
    void onAnimationRepeat(Animator animation);  
}  
//添加方法為:public void addListener(AnimatorListener listener)

(3)、插值器與 Evaluator

/** 
 * 設置插值器 
 */  
public void setInterpolator(TimeInterpolator value)  
/** 
 * 設置 Evaluator 
 */  
public void setEvaluator(TypeEvaluator value) 

到這里,有關 ObjectAnimator 的知識就講完了,下篇再講講聯合動畫和 xml 中實現動畫的方法。

如果本文有幫到你,記得加關注哦 源碼下載地址:

csdn:http://download.csdn.net/detail/harvic880925/9445785

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

請大家尊重原創(chuàng)者版權,轉載請標明出處,謝謝