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

ValueAnimator 基本使用

一、概述

long long ago,我寫過幾篇有關 Animation 的文章,講解了傳統(tǒng)的 alpha、scale、translate、rotate 的用法及代碼生成方法。其實這三篇文章講的所有動畫效果叫做 Tween Animation(補間動畫) 在 Android 動畫中,總共有兩種類型的動畫 View Animation(視圖動畫)和 Property Animator(屬性動畫);

其中

  • View Animation 包括 Tween Animation(補間動畫)和 Frame Animation(逐幀動畫);
  • Property Animator 包括 ValueAnimator 和 ObjectAnimation;

首先,直觀上,他們有如下三點不同: 1、引入時間不同:View Animation 是 API Level 1 就引入的。Property Animation 是 API Level 11 引入的,即 Android 3.0 才開始有 Property Animation 相關的 API。 2、所在包名不同:View Animation 在包 android.view.animation 中。而 Property Animation API 在包 android.animation 中。 3、動畫類的命名不同:View Animation 中動畫類取名都叫 XXXXAnimation,而在 Property Animator 中動畫類的取名則叫 XXXXAnimator

大家都知道逐幀動畫主要是用來實現(xiàn)動畫的,而補間動畫才能實現(xiàn)控件的漸入漸出、移動、旋轉(zhuǎn)和縮放的;而 Property Animator 是在 Android 3.0 版本才引入的,之前是沒有的。大家可能會覺得補間動畫和逐幀動畫已經(jīng)很全了,為什么還要引入 Property Animator 呢?

1、為什么引入 Property Animator(屬性動畫)

我提出一個假設:請問大家,如何利用補間動畫來將一個控件的背景色在一分鐘內(nèi)從綠色變?yōu)榧t色?這個效果想必沒辦法僅僅通過改變控件的漸入漸出、移動、旋轉(zhuǎn)和縮放來實現(xiàn)吧,而這個效果是可以通過 Property Animator 完美實現(xiàn)的 這就是第一個原因:Property Animator 能實現(xiàn)補間動畫無法實現(xiàn)的功能 大家都知道,補間動畫和逐幀動畫統(tǒng)稱為 View Animation,也就是說這兩個動畫只能對派生自 View 的控件實例起作用;而 Property Animator 則不同,從名字中可以看出屬性動畫,應該是作用于控件屬性的!正因為屬性動畫能夠只針對控件的某一個屬性來做動畫,所以也就造就了他能單獨改變控件的某一個屬性的值!比如顏色!這就是 Property Animator 能實現(xiàn)補間動畫無法實現(xiàn)的功能的最重要原因。 我們得到了第二點不同:View Animation 僅能對指定的控件做動畫,而 Property Animator 是通過改變控件某一屬性值來做動畫的。 假設我們將一個按鈕從左上角利用補間動畫將其移動到右下角,在移動過程中和移動后,這個按鈕都是不會響應點擊事件的。這是為什么呢?因為補間動畫僅僅轉(zhuǎn)變的是控件的顯示位置而已,并沒有改變控件本身的值。View Animation 的動畫實現(xiàn)是通過其 Parent View 實現(xiàn)的,在 View 被 drawn 時 Parents View 改變它的繪制參數(shù),這樣雖然 View 的大小或旋轉(zhuǎn)角度等改變了,但 View 的實際屬性沒變,所以有效區(qū)域還是應用動畫之前的區(qū)域;我們看到的效果僅僅是系統(tǒng)作用在按鈕上的顯示效果,利用動畫把按鈕從原來的位置移到了右下角,但按鈕內(nèi)部的任何值是沒有變化的,所以按鈕所捕捉的點擊區(qū)域仍是原來的點擊區(qū)域。(下面會舉例來說明這個問題) 這就得到了第三點不同:補間動畫雖能對控件做動畫,但并沒有改變控件內(nèi)部的屬性值。而 Property Animator 則是恰恰相反,Property Animator 是通過改變控件內(nèi)部的屬性值來達到動畫效果的

2、舉例說明補間動畫的點擊區(qū)域問題

下面我們就利用 TranslateAnimation 來做一個移動動畫的例子,看它的點擊區(qū)域是否會變。 我們先來看看效果:

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

在效果圖中,首先,我給 textview 添加了點擊響應,當點擊 textview 時,會彈出 Toast。 然后,當我點擊按鈕的時候,textview 開始向右下角移動。 從結(jié)果中可以看出,在移動前,點擊 textview 是可以彈出 toast 的的,在移動后,點擊 textview 時則沒有響應,相反,點擊 textview 的原來所在區(qū)域則會彈出 toast. 這就論證了不同第三點:補間動畫雖能對控件做動畫,但并沒有改變控件內(nèi)部的屬性值 下面簡單看看這個動畫的實現(xiàn)代碼吧:

(1)、看布局(main.xml) 從效果圖中也可以看出,布局很簡單,一個 button,一個 textview,垂直排列,布局代碼如下:

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout 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:padding="10dp"  
            android:text="start anim"  
            />  
    <TextView  
            android:id="@+id/tv"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:padding="10dp"  
            android:background="#ffff00"  
            android:text="Hello qijian"/>  
</LinearLayout> 

(2)JAVA 代碼(MyActivity.java) 接下來是操作代碼,就是分別給 button 和 textview 添加上點擊響應,當點擊 textview 時彈出 toast,點擊 button 時,textview 使用移動。 代碼如下:

public class MyActivity extends Activity {  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  

        final TextView tv  = (TextView) findViewById(R.id.tv);  
        Button btn  = (Button)findViewById(R.id.btn);  

        btn.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                final TranslateAnimation animation = new TranslateAnimation(Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 400,  
                        Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 400);  
                animation.setFillAfter(true);  
                animation.setDuration(1000);  
                tv.startAnimation(animation);  
            }  
        });  

        tv.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                Toast.makeText(MyActivity.this,"clicked me",Toast.LENGTH_SHORT).show();  
            }  
        });  

    }  
}  

這段代碼很容易理解,這里就不再細講了。 源碼在文章底部給出

二、ValueAnimator 簡單使用

我們前面講了 Property Animator 包括 ValueAnimator 和 ObjectAnimator;這篇文章就主要來看看 ValueAnimator 的使用方法吧。 我覺得谷歌那幫老頭是最會起名字的人,單從命名上,就能看出來這個東東的含義。ValueAnimator 從名字可以看出,這個 Animation 是針對值的!ValueAnimator 不會對控件做任何操作,我們可以給它設定從哪個值運動到哪個值,通過監(jiān)聽這些值的漸變過程來自己操作控件。以前我們曾講過 Scroller 類,Scroller 類也是不會對控件操作的,也是通過給他設定滾動值和時長,它會自己計算滾動過程,然后我們需要監(jiān)聽它的動畫過程來自己操作控件,ValueAnimator 的原理與 Scroller 類相似。有關 Scroller 的知識,大家可以參考:《 ListView 滑動刪除實現(xiàn)之四——Scroller 類與 listview 緩慢滑動》

1、初步使用 ValueAnimator

要使用 ValueAnimaiton,總共有兩步: 第一步:創(chuàng)建 ValueAnimator 實例

ValueAnimator animator = ValueAnimator.ofInt(0,400);  
animator.setDuration(1000);  
animator.start(); 

在這里我們利用 ValueAnimator.ofInt 創(chuàng)建了一個值從 0 到 400 的動畫,動畫時長是 1s,然后讓動畫開始。從這段代碼中可以看出,ValueAnimator 沒有跟任何的控件相關聯(lián),那也正好說明 ValueAnimator 只是對值做動畫運算,而不是針對控件的,我們需要監(jiān)聽 ValueAnimator 的動畫過程來自己對控件做操作。

第二步:添加監(jiān)聽 上面的三行代碼,我們已經(jīng)實現(xiàn)了動畫,下面我們就添加監(jiān)聽:

ValueAnimator animator = ValueAnimator.ofInt(0,400);  
animator.setDuration(1000);  

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
    @Override  
    public void onAnimationUpdate(ValueAnimator animation) {  
        int curValue = (int)animation.getAnimatedValue();  
        Log.d("qijian","curValue:"+curValue);  
    }  
});  
animator.start(); 

在上面的代碼中,我們通過 addUpdateListener 添加了一個監(jiān)聽,在監(jiān)聽傳回的結(jié)果中,是表示當前狀態(tài)的 ValueAnimator 實例,我們通過 animation.getAnimatedValue()得到當前值。然后通過 Log 打印出來,結(jié)果如下:

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

這就是 ValueAnimator 的功能:ValueAnimator 對指定值區(qū)間做動畫運算,我們通過對運算過程做監(jiān)聽來自己操作控件。 總而言之就是兩點:

  • ValueAnimator 只負責對指定的數(shù)字區(qū)間進行動畫運算
  • 我們需要對運算過程進行監(jiān)聽,然后自己對控件做動畫操作

2、實例使用 ValueAnimator

這段,我們就使用上面我們講到的 ValueAnimator 做一個動畫: 我們先看看效果圖:

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

首先這個動畫的布局與上一個實例是一樣的。但實現(xiàn)的效果確不大相同:

  • 首先,點擊按鈕后,textview 從屏幕(0,0)點運動到(400,400)點
  • 運動前后,textview 都是可以響應點擊事件的

下面我們就來看看這里如何利用 ValueAnimator 來實現(xiàn)這個效果的。 1、布局(main.xml) 布局代碼與上個例子相同:垂直布局按鈕控件和 textview

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout 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:padding="10dp"  
            android:text="start anim"  
            />  
    <TextView  
            android:id="@+id/tv"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:padding="10dp"  
            android:background="#ffff00"  
            android:text="Hello qijian"/>  
</LinearLayout>  

2、JAVA 操作 首先,是對 textview 和 btn 添加點擊響應,當點擊 textview 時,彈出 toast;點擊 btn 時,textview 開始做動畫

public class MyActivity extends Activity {  
    private TextView tv;  
    private Button btn;  

    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        tv = (TextView) findViewById(R.id.tv);  

        btn = (Button) findViewById(R.id.btn);  
        btn.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                doAnimation();  
            }  
        });  

        tv.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                Toast.makeText(MyActivity.this, "clicked me", Toast.LENGTH_SHORT).show();  
            }  
        });  
    }  
    …………  
}  

這段代碼很簡單,在點擊 btn 的時候執(zhí)行 doAnimation()來執(zhí)行動畫操作,在點擊 tv 的時候,彈出 toast; 下面來看看 doAnimation()的具體實現(xiàn):

private void doAnimation(){  
    ValueAnimator animator = ValueAnimator.ofInt(0,400);  
    animator.setDuration(1000);  

    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
        @Override  
        public void onAnimationUpdate(ValueAnimator animation) {  
            int curValue = (int)animation.getAnimatedValue();  
            tv.layout(curValue,curValue,curValue+tv.getWidth(),curValue+tv.getHeight());  
        }  
    });  
    animator.start();  
}  

首先,我們構造一個 ValueAnimator 實例,讓其計算的值是從 0 到 400; 然后添加對計算過程進行監(jiān)聽:

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
    @Override  
    public void onAnimationUpdate(ValueAnimator animation) {  
        int curValue = (int)animation.getAnimatedValue();  
        tv.layout(curValue,curValue,curValue+tv.getWidth(),curValue+tv.getHeight());  
    }  
});  

在監(jiān)聽過程中,通過 layout 函數(shù)來改變 textview 的位置。這里注意了,我們是通過 layout 函數(shù)來改變位置的,我們知道 layout 函數(shù)在改變控件位置時是永久性的,即通過更改控件 left,top,right,bottom 這四個點的坐標來改更改坐標位置的,而不僅僅是從視覺上畫在哪個位置,所以通過 layout 函數(shù)更改位置后,控件在新位置是可以響應點擊事件的。 大家可能注意到了,layout()函數(shù)中上下左右點的坐標是以屏幕坐標來標準的。所以在效果圖中可以看到,textview 的運動軌跡是從屏幕的左上角(0,0)點運行到(400,400)點。 源碼在文章底部給出

三、常用方法

經(jīng)過上面的例子,我們就大概知道 ValueAnimator 要怎么使用了,下面我們就來具體來看看它還有哪些常用的方法吧。

1、ofInt 與 ofFloat

在上面的例子中,我們使用了 ofInt 函數(shù),與它同樣功能的還有一個函數(shù)叫 ofFloat,下面我們先看看他們的具體聲明:

public static ValueAnimator ofInt(int... values)  
public static ValueAnimator ofFloat(float... values) 

他們的參數(shù)類型都是可變參數(shù)長參數(shù),所以我們可以傳入任何數(shù)量的值;傳進去的值列表,就表示動畫時的變化范圍;比如 ofInt(2,90,45)就表示從數(shù)值 2 變化到數(shù)字 90 再變化到數(shù)字 45;所以我們傳進去的數(shù)字越多,動畫變化就越復雜。從參數(shù)類型也可以看出 ofInt 與 ofFloat 的唯一區(qū)別就是傳入的數(shù)字類型不一樣,ofInt 需要傳入 Int 類型的參數(shù),而 ofFloat 則表示需要傳入 Float 類型的參數(shù)。 下面我們還在上面例子的基礎上,使用 ofFloat 函數(shù)來舉個例子:

ValueAnimator animator = ValueAnimator.ofFloat(0f,400f,50f,300f);  
animator.setDuration(3000);  

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
    @Override  
    public void onAnimationUpdate(ValueAnimator animation) {  
        Float curValueFloat = (Float)animation.getAnimatedValue();  
        int curValue = curValueFloat.intValue();  
        tv.layout(curValue,curValue,curValue+tv.getWidth(),curValue+tv.getHeight());  
    }  
});  
animator.start();

先看看效果:

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

在效果圖中,我們可以看到,在點擊按鈕之后,textview 先向右下運動然后再回來,然后再向右下運動過去 在這個例子中,我們使用 ValueAnimator.ofFloat(0f,400f,50f,300f)構造了一個比較復雜的動畫漸變,值是 0 變到 400 再回到 50 最后變成 300; 所以我們在監(jiān)聽時,首先得到當前動畫的值:

Float curValueFloat = (Float)animation.getAnimatedValue();  

通過 getAnimatedValue()來獲取當前運動點的值,大家可能會疑問為什么要轉(zhuǎn)成 Float 類型,我們先來看看 getAnimatedValue()的聲明:

Object getAnimatedValue(); 

它返回的類型是一個 Object 原始類型,那我們怎么知道我們要將它強轉(zhuǎn)成什么類型呢。注意,我們在設定動畫初始值時用的是 ofFloat()函數(shù),所以每個值的類型必定是 Float 類型,所以我們獲取出來的類型也必然是 Float 類型的。同樣,如果我們使用 ofInt 設定的初始值,那么通過 getAnimatedValue()獲取到的值就應該強轉(zhuǎn)為 Int 類型。 在得到當前運動的值以后,通過 layout 函數(shù)將 textview 移動到指定位置即可。

源碼在文章底部給出

2、常用函數(shù)

先做個匯總,這部分將講述的方法有:

/** 
 * 設置動畫時長,單位是毫秒 
 */  
ValueAnimator setDuration(long duration)  
/** 
 * 獲取 ValueAnimator 在運動時,當前運動點的值 
 */  
Object getAnimatedValue();  
/** 
 * 開始動畫 
 */  
void start()  
/** 
 * 設置循環(huán)次數(shù),設置為 INFINITE 表示無限循環(huán) 
 */  
void setRepeatCount(int value)  
/** 
 * 設置循環(huán)模式 
 * value 取值有 RESTART,REVERSE, 
 */  
void setRepeatMode(int value)  
/** 
 * 取消動畫 
 */  
void cancel() 

1、setDuration()、getAnimatedValue()、start() 這三個函數(shù)在上面的實例中已經(jīng)使用過,setDuration(long duration)是設置一次動畫的時長,單位是毫秒,start()是開始動畫,唯一有點難度的是 Object getAnimatedValue(),它的聲明為:

Object getAnimatedValue(); 

它的意義就是獲取動畫在當前運動點的值,所以這個對象只能用于在動畫運動中。返回的值是 Object,上面我們說過,通過 getAnimatedValue()得到的值的實際類型與初始設置的值相同,如果我們利用 ofInt()設置的動畫,那通過 getAnimatedValue()得到的值為類型就是 Int 類型。如果我們利用 ofFloat()設置的動畫,通過 getAnimatedValue()得到的值類型就是 Float 類型。 總而言之,通過 getAnimatedValue()值類型與初始設置動畫時的值類型相同 上面我們已經(jīng)用過這些函數(shù)了,這里就不再舉例了。

2、setRepeatCount()、setRepeatMode()、cancel() setRepeatCount(int value)用于設置動畫循環(huán)次數(shù),設置為 0 表示不循環(huán),設置為 ValueAnimation.INFINITE 表示無限循環(huán)。 cancel()用于取消動畫 我們著重說一下 setRepeatMode:

/** 
 * 設置循環(huán)模式 
 * value 取值有 RESTART,REVERSE 
 */  
void setRepeatMode(int value) 

setRepeatMode(int value)用于設置循環(huán)模式,取值為 ValueAnimation.RESTART 時,表示正序重新開始,當取值為 ValueAnimation.REVERSE 表示倒序重新開始。 下面我們使用這三個函數(shù)來舉個例子,先看下動畫效果:

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

在這里,有兩個按鈕,當點擊 start anim 時,textview 垂直向下運動,我定義的運動初始值為 ofInt(0,400);所以從效果圖中也可以看出我們定義它為無限循環(huán),而且每次循環(huán)時都是使用 ValueAnimation.REVERSE 讓其倒序重新開始循環(huán)。當我們點擊 cancel anim 時,取消動畫。 下面我們來看看代碼 首先是布局代碼,布局代碼時,采用 RelativeLayout 布局,將兩個按鈕放兩邊,textview 放中間,代碼如下:

<?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="wrap_content"  
            android:layout_height="wrap_content"  
            android:layout_centerHorizontal="true"  
            android:padding="10dp"  
            android:background="#ffff00"  
            android:text="Hello qijian"/>  
</RelativeLayout>  

這個布局代碼沒什么難度就不講了。 下面來看看兩個按鈕的操作代碼:

private Button btnStart,btnCancel;  
private ValueAnimator repeatAnimator;  

@Override  
public void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.main);  
    tv = (TextView) findViewById(R.id.tv);  

    btnStart = (Button) findViewById(R.id.btn);  
    btnCancel = (Button)findViewById(R.id.btn_cancel);  

    btnStart.setOnClickListener(new View.OnClickListener() {  
        @Override  
        public void onClick(View v) {  
            repeatAnimator = doRepeatAnim();  
        }  
    });  

    btnCancel.setOnClickListener(new View.OnClickListener() {  
        @Override  
        public void onClick(View v) {  

            repeatAnimator.cancel();  
        }  
    });  
}

這段代碼也沒什么難度,當我們點擊 btnStart 的時候,執(zhí)行 doRepeatAnim()函數(shù),這個函數(shù)返回它構造的 ValueAnimator 對象,將其賦值給 repeatAnimator 變量。當點擊 btnCancel 時,調(diào)用 repeatAnimator.cancel()取消當前動畫。 下面我們來看看 doRepeatAnim()函數(shù)都做了哪些工作:

private ValueAnimator doRepeatAnim(){  
   ValueAnimator animator = ValueAnimator.ofInt(0,400);  
   animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
       @Override  
       public void onAnimationUpdate(ValueAnimator animation) {  
           int curValue = (int)animation.getAnimatedValue();  
           tv.layout(tv.getLeft(),curValue,tv.getRight(),curValue+tv.getHeight());  
       }  
   });  
   animator.setRepeatMode(ValueAnimator.REVERSE);  
   animator.setRepeatCount(ValueAnimator.INFINITE);  
   animator.setDuration(1000);  
   animator.start();  
   return animator;  
} 

在這里我們構造了一個 ValueAnimator,動畫范圍是 0-400,設置重復次數(shù)為無限循環(huán)。循環(huán)模式為倒序。在 animator.setDuration(1000)表示動畫一次的時長為 1000 毫秒。最后,由于我們在取消動畫時還需要我們構造的這個 ValueAnimator 實例,所以將 animator 返回。 源碼在文章底部給出

3、兩個監(jiān)聽器

(1)、添加監(jiān)聽器 前面,我們講過一個添加監(jiān)聽器 animator.addUpdateListener,以監(jiān)聽動畫過程中值的實時變化,其實在 ValueAnimator 中共有兩個監(jiān)聽器:

/** 
 * 監(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)  

關于監(jiān)聽器一:AnimatorUpdateListener 就是監(jiān)聽動畫的實時變化狀態(tài),在 onAnimationUpdate(ValueAnimator animation)中的 animation 表示當前狀態(tài)動畫的實例。這里就不再細講這個監(jiān)聽器了,這里我們主要講講監(jiān)聽器 AnimatorListener; 在 AnimatorListener 中,主要是監(jiān)聽 Animation 的四個狀態(tài),start、end、cancel、repeat;當動畫開始時,會調(diào)用 onAnimationStart(Animator animation)方法,當動畫結(jié)束時調(diào)用 onAnimationEnd(Animator animation),當動畫取消時,調(diào)用 onAnimationCancel(Animator animation)函數(shù),當動畫重復時,會調(diào)用 onAnimationRepeat(Animator animation)函數(shù)。 添加 AnimatorListener 的方法是 addListener(AnimatorListener listener) ; 下面我們就舉個例子來看一下 AnimatorListener 的使用方法。 我們在上面 doRepeatAnim()函數(shù)的基礎上,添加上 AnimatorListener,代碼如下: 代碼如下:

private ValueAnimator doAnimatorListener(){  
    ValueAnimator animator = ValueAnimator.ofInt(0,400);  

    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
        @Override  
        public void onAnimationUpdate(ValueAnimator animation) {  
            int curValue = (int)animation.getAnimatedValue();  
            tv.layout(tv.getLeft(),curValue,tv.getRight(),curValue+tv.getHeight());  
        }  
    });  
    animator.addListener(new Animator.AnimatorListener() {  
        @Override  
        public void onAnimationStart(Animator animation) {  
            Log.d("qijian","animation start");  
        }  

        @Override  
        public void onAnimationEnd(Animator animation) {  
            Log.d("qijian","animation end");  
        }  

        @Override  
        public void onAnimationCancel(Animator animation) {  
            Log.d("qijian","animation cancel");  
        }  

        @Override  
        public void onAnimationRepeat(Animator animation) {  
            Log.d("qijian","animation repeat");  
        }  
    });  
    animator.setRepeatMode(ValueAnimator.REVERSE);  
    animator.setRepeatCount(ValueAnimator.INFINITE);  
    animator.setDuration(1000);  
    animator.start();  
    return animator;  
} 

在上面的代碼中,我們是在 doRepeatAnim()函數(shù)的基礎上,又添加了 AnimatorListener()以監(jiān)聽它的狀態(tài),并把這些狀態(tài)打印出來。 我們來看看動畫效果:

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

打印出來結(jié)果如下:

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

(2)、取消監(jiān)聽 上面我們講了如何添加監(jiān)聽函數(shù),下面我們來看看如何移除監(jiān)聽器:

/** 
 * 移除 AnimatorUpdateListener 
 */  
void removeUpdateListener(AnimatorUpdateListener listener);  
void removeAllUpdateListeners();  
 /** 
  * 移除 AnimatorListener 
  */  
void removeListener(AnimatorListener listener);  
void removeAllListeners();

針對 AnimatorUpdateListener 和 AnimatorListener,每個監(jiān)聽器都有兩個方法來移除;我們就以移除 AnimatorListener 來簡單講一下,removeListener(AnimatorListener listener)用于在 animator 中移除指定的監(jiān)聽器,而 removeAllListeners()用于移除 animator 中所有的 AnimatorListener 監(jiān)聽器; 下面上在添加監(jiān)聽器的例子基礎上,不改變 doAnimatorListener()的代碼,仍然是 textview 做動畫時添加 AnimatorListener 的狀態(tài)監(jiān)聽。然后點擊 cancelAnim 時,移除 AnimatorListener,代碼如下: AnimatorListener 的代碼:

public void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.main);  

    …………  
    btnStart.setOnClickListener(new View.OnClickListener() {  
        @Override  
        public void onClick(View v) {  
            repeatAnimator = doAnimatorListener();  
        }  
    });  

    btnCancel.setOnClickListener(new View.OnClickListener() {  
        @Override  
        public void onClick(View v) {  
            repeatAnimator.removeAllListeners();  
        }  
    });  
} 

doAnimatorListener 的代碼與上面的一樣,就不再重復貼了,當點擊 btnCancel 時移除 animator 中所有的 AnimatorListener,但注意的是,我們在移除 AnimatorListener 后,并沒有 cancel 動畫效果,所以動畫會一直不停的運動下去。但移除 AnimatorListener 之后,Log 應該就不會再打印了。 效果如下:

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

在效果圖中,在動畫循環(huán)了三次之后,我們點擊 btnCancel 移除所有的 AnimatorListener;打印 tag 如下:

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

可見只打印了循環(huán)三次以前的 log,在移除我們添加的 AnimatorListener 之后,我們打印 log 的代碼就不會再執(zhí)行了,所以也就不會再有 log 了。 好了,有關監(jiān)聽器的部分,我們就到這里了

源碼在文章底部給出

4、其它函數(shù)

上面我們講了 ValueAnimator 中常用的一些函數(shù),但是還有一些函數(shù)雖然不常用,但我們還是簡單講一下,他們分別是:

/** 
 * 延時多久時間開始,單位是毫秒 
 */  
public void setStartDelay(long startDelay)  
/** 
 * 完全克隆一個 ValueAnimator 實例,包括它所有的設置以及所有對監(jiān)聽器代碼的處理 
 */  
public ValueAnimator clone() 

setStartDelay(long startDelay)非常容易理解,就是設置多久后動畫才開始。 但 clone()這個函數(shù)就有點難度了;首先是什么叫克隆。就是完全一樣!注意是完全一樣!就是復制出來一個完全一樣的新的 ValueAnimator 實例出來。對原來的那個 ValueAnimator 是怎么處理的,在這個新的實例中也是全部一樣的。 我們來看一個例子來看一下,什么叫全部一樣: 首先,我們定義一個函數(shù) doRepeatAnim():

private ValueAnimator doRepeatAnim(){  
    ValueAnimator animator = ValueAnimator.ofInt(0,400);  

    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
        @Override  
        public void onAnimationUpdate(ValueAnimator animation) {  
            int curValue = (int)animation.getAnimatedValue();  
            tv.layout(tv.getLeft(),curValue,tv.getRight(),curValue+tv.getHeight());  
        }  
    });  
    animator.setDuration(1000);  
    animator.setRepeatMode(ValueAnimator.REVERSE);  
    animator.setRepeatCount(ValueAnimator.INFINITE);  
    return animator;  
}  

這個函數(shù)其實與上面在講循環(huán)函數(shù)時的 doRepeatAnim()函數(shù)是一樣的;在這個函數(shù)中,我們定義一個 ValueAnimator,設置為無限循環(huán),然后添加 AnimatorUpdateListener 監(jiān)聽;在動畫在運動時,向下移動 textview.這里要非常注意的一點是我們只是定義了一個 ValueAnimator 對象,并沒有調(diào)用 start()讓動畫開始!?。?! 然后我們再看看點擊 btnStart 和 btnCancel 時的代碼處理:

public void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.main);  

    …………  
    btnStart.setOnClickListener(new View.OnClickListener() {  
        @Override  
        public void onClick(View v) {  
            repeatAnimator = doRepeatAnim();  
            //克隆一個新的 ValueAnimator,然后開始動畫  
            ValueAnimator newAnimator = repeatAnimator.clone();  
            newAnimator.setStartDelay(1000);  
            newAnimator.start();  
        }  
    });  

    btnCancel.setOnClickListener(new View.OnClickListener() {  
        @Override  
        public void onClick(View v) {  
            repeatAnimator.removeAllUpdateListeners();  

            repeatAnimator.cancel();  
        }  
    });  
}  

在上面的代碼中,我們在點擊 btnStart 時:

repeatAnimator = doRepeatAnim();  
//克隆一個新的 ValueAnimator,然后開始動畫  
ValueAnimator newAnimator = repeatAnimator.clone();  
newAnimator.setStartDelay(1000);  
newAnimator.start();

我們利用 clone()克隆了一個 doRepeatAnim()生成的對象。然后調(diào)用 setStartDelay(1000);將動畫開始時間設為 1000 毫秒后開始動畫。最后調(diào)用 start()函數(shù)開始動畫。 這里有一點非常注意是:我們除了對 newAnimator 設置了動畫開始延時 1000 毫秒以后,沒有對它進行任何設置,更沒有在在它的監(jiān)聽器中對 textview 的處理?。。。∧?textview 會動嗎?答案是會動的,我們講了,克隆就是完全一樣,在原來的 ValueAnimator 中是如何處理的,克隆過來的 ValueAnimator 也是完全一樣的處理方式! 在點擊 btnCancel 時:

repeatAnimator.removeAllUpdateListeners();  
repeatAnimator.cancel(); 

我們既移除了 repeatAnimator 的監(jiān)聽器又取消了動畫。但有用嗎?必須當然是沒用的,因為我們 start 的動畫對象是從 repeatAnimator 克隆來的 newAnimator。這好比是克隆羊,原來的羊和克隆羊什么都是一樣的,但你把原來的羊殺了,克隆的羊會死嗎?用大腳指頭想都知道不會!所以如果要取消當前的動畫必須通過 newAnimator.cancel()來取消 效果圖如下:

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

從效果圖中也可以看出,點擊 btnCancel 按鈕是沒有做用的,并沒能取消動畫。

源碼在文章底部給出 好了,到這里有關 ValueAnimator 的常用函數(shù)基本就講完了,下篇將更深入的講解 ValueAnimator 的高級用法。 源碼內(nèi)容:

1、《try_tween_anim_click》:對應概述中:舉例說明補間動畫的點擊區(qū)域問題 2、《BlogValueAnimator1》:對應:《實例使用 ValueAnimator》和《ofInt 與 ofFloat》兩部分源碼 3、《BlogValueAnimator2》:對應以后所有的源碼

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

csdn:http://download.csdn.net/detail/harvic880925/9405988 github:https://github.com/harvic/BlogResForGitHub

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