android.animation(1) - ValueAnimator的ofInt(), ofFloat(), addUpdateListener(), addListener()(轉)


 

一、概述

long long ago,我寫過幾篇有關Animation的文章,講解了傳統的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

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

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

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

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

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

在效果圖中,首先,我給textview添加了點擊響應,當點擊textview時,會彈出Toast。 
然后,當我點擊按鈕的時候,textview開始向右下角移動。 
從結果中可以看出,在移動前,點擊textview是可以彈出toast的的,在移動后,點擊textview時則沒有響應,相反,點擊textview的原來所在區域則會彈出toast. 

這就論證了不同第三點:補間動畫雖能對控件做動畫,但並沒有改變控件內部的屬性值 
下面簡單看看這個動畫的實現代碼吧: 

(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不會對控件做任何操作,我們可以給它設定從哪個值運動到哪個值,通過監聽這些值的漸變過程來自己操作控件。以前我們曾講過Scroller類,Scroller類也是不會對控件操作的,也是通過給他設定滾動值和時長,它會自己計算滾動過程,然后我們需要監聽它的動畫過程來自己操作控件,ValueAnimator的原理與Scroller類相似。有關Scroller的知識,大家可以參考:《 ListView滑動刪除實現之四——Scroller類與listview緩慢滑動》

1、初步使用ValueAnimator

要使用ValueAnimaiton,總共有兩步: 

第一步:創建ValueAnimator實例

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

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

第二步:添加監聽

上面的三行代碼,我們已經實現了動畫,下面我們就添加監聽:

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添加了一個監聽,在監聽傳回的結果中,是表示當前狀態的ValueAnimator實例,我們通過animation.getAnimatedValue()得到當前值。然后通過Log打印出來,結果如下: 

這就是ValueAnimator的功能:ValueAnimator對指定值區間做動畫運算,我們通過對運算過程做監聽來自己操作控件。

總而言之就是兩點:

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

2、實例使用ValueAnimator

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

 

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

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

下面我們就來看看這里如何利用ValueAnimator來實現這個效果的。  

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的時候執行 doAnimation()來執行動畫操作,在點擊tv的時候,彈出toast; 
下面來看看 doAnimation()的具體實現:

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; 
然后添加對計算過程進行監聽:

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

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

三、常用方法

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

1、ofInt與ofFloat

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

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

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

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

先看看效果: 

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

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

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

Object getAnimatedValue();  

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

2、常用函數

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

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

1、setDuration()、getAnimatedValue()、start()

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

Object getAnimatedValue();  

它的意義就是獲取動畫在當前運動點的值,所以這個對象只能用於在動畫運動中。返回的值是Object,上面我們說過,通過getAnimatedValue()得到的值的實際類型與初始設置的值相同,如果我們利用ofInt()設置的動畫,那通過getAnimatedValue()得到的值為類型就是Int類型。如果我們利用ofFloat()設置的動畫,通過getAnimatedValue()得到的值類型就是Float類型。 

總而言之,通過getAnimatedValue()值類型與初始設置動畫時的值類型相同 

上面我們已經用過這些函數了,這里就不再舉例了。 

2、setRepeatCount()、setRepeatMode()、cancel()

setRepeatCount(int value)用於設置動畫循環次數,設置為0表示不循環,設置為ValueAnimation.INFINITE表示無限循環。 
cancel()用於取消動畫 
我們着重說一下setRepeatMode:

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

setRepeatMode(int value)用於設置循環模式,取值為ValueAnimation.RESTART時,表示正序重新開始,當取值為ValueAnimation.REVERSE表示倒序重新開始。 

下面我們使用這三個函數來舉個例子,先看下動畫效果: 

在這里,有兩個按鈕,當點擊start anim時,textview垂直向下運動,我定義的運動初始值為ofInt(0,400);所以從效果圖中也可以看出我們定義它為無限循環,而且每次循環時都是使用ValueAnimation.REVERSE讓其倒序重新開始循環。當我們點擊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的時候,執行doRepeatAnim()函數,這個函數返回它構造的ValueAnimator對象,將其賦值給repeatAnimator變量。當點擊btnCancel時,調用 repeatAnimator.cancel()取消當前動畫。 
下面我們來看看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.setRepeatMode(ValueAnimator.REVERSE);  
   animator.setRepeatCount(ValueAnimator.INFINITE);  
   animator.setDuration(1000);  
   animator.start();  
   return animator;  
} 

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

3、兩個監聽器

(1)、添加監聽器

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

/** 
 * 監聽器一:監聽動畫變化時的實時值 
 */  
public static interface AnimatorUpdateListener {  
    void onAnimationUpdate(ValueAnimator animation);  
}  
//添加方法為:public void addUpdateListener(AnimatorUpdateListener listener)  
/** 
 * 監聽器二:監聽動畫變化時四個狀態 
 */  
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)   

關於監聽器一:AnimatorUpdateListener就是監聽動畫的實時變化狀態,在onAnimationUpdate(ValueAnimator animation)中的animation表示當前狀態動畫的實例。這里就不再細講這個監聽器了,這里我們主要講講監聽器AnimatorListener; 
在AnimatorListener中,主要是監聽Animation的四個狀態,start、end、cancel、repeat;當動畫開始時,會調用onAnimationStart(Animator animation)方法,當動畫結束時調用onAnimationEnd(Animator animation),當動畫取消時,調用onAnimationCancel(Animator animation)函數,當動畫重復時,會調用onAnimationRepeat(Animator animation)函數。 
添加AnimatorListener的方法是addListener(AnimatorListener listener) ; 
下面我們就舉個例子來看一下AnimatorListener的使用方法。 
我們在上面doRepeatAnim()函數的基礎上,添加上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()函數的基礎上,又添加了AnimatorListener()以監聽它的狀態,並把這些狀態打印出來。 
我們來看看動畫效果: 

打印出來結果如下: 

(2)、取消監聽

上面我們講了如何添加監聽函數,下面我們來看看如何移除監聽器:

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

針對AnimatorUpdateListener和AnimatorListener,每個監聽器都有兩個方法來移除;我們就以移除AnimatorListener來簡單講一下,removeListener(AnimatorListener listener)用於在animator中移除指定的監聽器,而removeAllListeners()用於移除animator中所有的AnimatorListener監聽器; 
下面上在添加監聽器的例子基礎上,不改變doAnimatorListener()的代碼,仍然是textview做動畫時添加AnimatorListener的狀態監聽。然后點擊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應該就不會再打印了。 
效果如下: 

 

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

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

4、其它函數

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

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

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

這個函數其實與上面在講循環函數時的doRepeatAnim()函數是一樣的;在這個函數中,我們定義一個ValueAnimator,設置為無限循環,然后添加AnimatorUpdateListener監聽;在動畫在運動時,向下移動textview.這里要非常注意的一點是我們只是定義了一個ValueAnimator對象,並沒有調用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()生成的對象。然后調用setStartDelay(1000);將動畫開始時間設為1000毫秒后開始動畫。最后調用start()函數開始動畫。 
這里有一點非常注意是:我們除了對newAnimator設置了動畫開始延時1000毫秒以后,沒有對它進行任何設置,更沒有在在它的監聽器中對textview的處理!!!!那textview會動嗎?答案是會動的,我們講了,克隆就是完全一樣,在原來的ValueAnimator中是如何處理的,克隆過來的ValueAnimator也是完全一樣的處理方式! 
在點擊btnCancel時:

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

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

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

參考鏈接:自定義控件三部曲之動畫篇(四)——ValueAnimator基本使用

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM