在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

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

ValueAnimator 基本使用

一、概述

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

其中

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

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

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

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

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

2、舉例說明補(bǔ)間動(dòng)畫的點(diǎn)擊區(qū)域問題

下面我們就利用 TranslateAnimation 來(lái)做一個(gè)移動(dòng)動(dòng)畫的例子,看它的點(diǎn)擊區(qū)域是否會(huì)變。 我們先來(lái)看看效果:

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

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

(1)、看布局(main.xml) 從效果圖中也可以看出,布局很簡(jiǎn)單,一個(gè) button,一個(gè) 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) 接下來(lái)是操作代碼,就是分別給 button 和 textview 添加上點(diǎn)擊響應(yīng),當(dāng)點(diǎn)擊 textview 時(shí)彈出 toast,點(diǎn)擊 button 時(shí),textview 使用移動(dòng)。 代碼如下:

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

    }  
}  

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

二、ValueAnimator 簡(jiǎn)單使用

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

1、初步使用 ValueAnimator

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

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

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

第二步:添加監(jiān)聽 上面的三行代碼,我們已經(jīng)實(shí)現(xiàn)了動(dòng)畫,下面我們就添加監(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 添加了一個(gè)監(jiān)聽,在監(jiān)聽傳回的結(jié)果中,是表示當(dāng)前狀態(tài)的 ValueAnimator 實(shí)例,我們通過 animation.getAnimatedValue()得到當(dāng)前值。然后通過 Log 打印出來(lái),結(jié)果如下:

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

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

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

2、實(shí)例使用 ValueAnimator

這段,我們就使用上面我們講到的 ValueAnimator 做一個(gè)動(dòng)畫: 我們先看看效果圖:

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

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

  • 首先,點(diǎn)擊按鈕后,textview 從屏幕(0,0)點(diǎn)運(yùn)動(dòng)到(400,400)點(diǎn)
  • 運(yùn)動(dòng)前后,textview 都是可以響應(yīng)點(diǎn)擊事件的

下面我們就來(lái)看看這里如何利用 ValueAnimator 來(lái)實(shí)現(xiàn)這個(gè)效果的。 1、布局(main.xml) 布局代碼與上個(gè)例子相同:垂直布局按鈕控件和 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 操作 首先,是對(duì) textview 和 btn 添加點(diǎn)擊響應(yīng),當(dāng)點(diǎn)擊 textview 時(shí),彈出 toast;點(diǎn)擊 btn 時(shí),textview 開始做動(dòng)畫

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

這段代碼很簡(jiǎn)單,在點(diǎn)擊 btn 的時(shí)候執(zhí)行 doAnimation()來(lái)執(zhí)行動(dòng)畫操作,在點(diǎn)擊 tv 的時(shí)候,彈出 toast; 下面來(lái)看看 doAnimation()的具體實(shí)現(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();  
}  

首先,我們構(gòu)造一個(gè) ValueAnimator 實(shí)例,讓其計(jì)算的值是從 0 到 400; 然后添加對(duì)計(jì)算過程進(jìn)行監(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ù)來(lái)改變 textview 的位置。這里注意了,我們是通過 layout 函數(shù)來(lái)改變位置的,我們知道 layout 函數(shù)在改變控件位置時(shí)是永久性的,即通過更改控件 left,top,right,bottom 這四個(gè)點(diǎn)的坐標(biāo)來(lái)改更改坐標(biāo)位置的,而不僅僅是從視覺上畫在哪個(gè)位置,所以通過 layout 函數(shù)更改位置后,控件在新位置是可以響應(yīng)點(diǎn)擊事件的。 大家可能注意到了,layout()函數(shù)中上下左右點(diǎn)的坐標(biāo)是以屏幕坐標(biāo)來(lái)標(biāo)準(zhǔn)的。所以在效果圖中可以看到,textview 的運(yùn)動(dòng)軌跡是從屏幕的左上角(0,0)點(diǎn)運(yùn)行到(400,400)點(diǎn)。 源碼在文章底部給出

三、常用方法

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

1、ofInt 與 ofFloat

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

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

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

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="" />

在效果圖中,我們可以看到,在點(diǎn)擊按鈕之后,textview 先向右下運(yùn)動(dòng)然后再回來(lái),然后再向右下運(yùn)動(dòng)過去 在這個(gè)例子中,我們使用 ValueAnimator.ofFloat(0f,400f,50f,300f)構(gòu)造了一個(gè)比較復(fù)雜的動(dòng)畫漸變,值是 0 變到 400 再回到 50 最后變成 300; 所以我們?cè)诒O(jiān)聽時(shí),首先得到當(dāng)前動(dòng)畫的值:

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

通過 getAnimatedValue()來(lái)獲取當(dāng)前運(yùn)動(dòng)點(diǎn)的值,大家可能會(huì)疑問為什么要轉(zhuǎn)成 Float 類型,我們先來(lái)看看 getAnimatedValue()的聲明:

Object getAnimatedValue(); 

它返回的類型是一個(gè) Object 原始類型,那我們?cè)趺粗牢覀円獙⑺鼜?qiáng)轉(zhuǎn)成什么類型呢。注意,我們?cè)谠O(shè)定動(dòng)畫初始值時(shí)用的是 ofFloat()函數(shù),所以每個(gè)值的類型必定是 Float 類型,所以我們獲取出來(lái)的類型也必然是 Float 類型的。同樣,如果我們使用 ofInt 設(shè)定的初始值,那么通過 getAnimatedValue()獲取到的值就應(yīng)該強(qiáng)轉(zhuǎn)為 Int 類型。 在得到當(dāng)前運(yùn)動(dòng)的值以后,通過 layout 函數(shù)將 textview 移動(dòng)到指定位置即可。

源碼在文章底部給出

2、常用函數(shù)

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

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

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

Object getAnimatedValue(); 

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

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

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

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

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

在這里,有兩個(gè)按鈕,當(dāng)點(diǎn)擊 start anim 時(shí),textview 垂直向下運(yùn)動(dòng),我定義的運(yùn)動(dòng)初始值為 ofInt(0,400);所以從效果圖中也可以看出我們定義它為無(wú)限循環(huán),而且每次循環(huán)時(shí)都是使用 ValueAnimation.REVERSE 讓其倒序重新開始循環(huán)。當(dāng)我們點(diǎn)擊 cancel anim 時(shí),取消動(dòng)畫。 下面我們來(lái)看看代碼 首先是布局代碼,布局代碼時(shí),采用 RelativeLayout 布局,將兩個(gè)按鈕放兩邊,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>  

這個(gè)布局代碼沒什么難度就不講了。 下面來(lái)看看兩個(gè)按鈕的操作代碼:

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

這段代碼也沒什么難度,當(dāng)我們點(diǎn)擊 btnStart 的時(shí)候,執(zhí)行 doRepeatAnim()函數(shù),這個(gè)函數(shù)返回它構(gòu)造的 ValueAnimator 對(duì)象,將其賦值給 repeatAnimator 變量。當(dāng)點(diǎn)擊 btnCancel 時(shí),調(diào)用 repeatAnimator.cancel()取消當(dāng)前動(dòng)畫。 下面我們來(lái)看看 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;  
} 

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

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

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

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

關(guān)于監(jiān)聽器一:AnimatorUpdateListener 就是監(jiān)聽動(dòng)畫的實(shí)時(shí)變化狀態(tài),在 onAnimationUpdate(ValueAnimator animation)中的 animation 表示當(dāng)前狀態(tài)動(dòng)畫的實(shí)例。這里就不再細(xì)講這個(gè)監(jiān)聽器了,這里我們主要講講監(jiān)聽器 AnimatorListener; 在 AnimatorListener 中,主要是監(jiān)聽 Animation 的四個(gè)狀態(tài),start、end、cancel、repeat;當(dāng)動(dòng)畫開始時(shí),會(huì)調(diào)用 onAnimationStart(Animator animation)方法,當(dāng)動(dòng)畫結(jié)束時(shí)調(diào)用 onAnimationEnd(Animator animation),當(dāng)動(dòng)畫取消時(shí),調(diào)用 onAnimationCancel(Animator animation)函數(shù),當(dāng)動(dòng)畫重復(fù)時(shí),會(huì)調(diào)用 onAnimationRepeat(Animator animation)函數(shù)。 添加 AnimatorListener 的方法是 addListener(AnimatorListener listener) ; 下面我們就舉個(gè)例子來(lái)看一下 AnimatorListener 的使用方法。 我們?cè)谏厦?doRepeatAnim()函數(shù)的基礎(chǔ)上,添加上 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ù)的基礎(chǔ)上,又添加了 AnimatorListener()以監(jiān)聽它的狀態(tài),并把這些狀態(tài)打印出來(lái)。 我們來(lái)看看動(dòng)畫效果:

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

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

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

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

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

針對(duì) AnimatorUpdateListener 和 AnimatorListener,每個(gè)監(jiān)聽器都有兩個(gè)方法來(lái)移除;我們就以移除 AnimatorListener 來(lái)簡(jiǎn)單講一下,removeListener(AnimatorListener listener)用于在 animator 中移除指定的監(jiān)聽器,而 removeAllListeners()用于移除 animator 中所有的 AnimatorListener 監(jiān)聽器; 下面上在添加監(jiān)聽器的例子基礎(chǔ)上,不改變 doAnimatorListener()的代碼,仍然是 textview 做動(dòng)畫時(shí)添加 AnimatorListener 的狀態(tài)監(jiān)聽。然后點(diǎn)擊 cancelAnim 時(shí),移除 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 的代碼與上面的一樣,就不再重復(fù)貼了,當(dāng)點(diǎn)擊 btnCancel 時(shí)移除 animator 中所有的 AnimatorListener,但注意的是,我們?cè)谝瞥?AnimatorListener 后,并沒有 cancel 動(dòng)畫效果,所以動(dòng)畫會(huì)一直不停的運(yùn)動(dòng)下去。但移除 AnimatorListener 之后,Log 應(yīng)該就不會(huì)再打印了。 效果如下:

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

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

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

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

源碼在文章底部給出

4、其它函數(shù)

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

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

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

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

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();  
            //克隆一個(gè)新的 ValueAnimator,然后開始動(dòng)畫  
            ValueAnimator newAnimator = repeatAnimator.clone();  
            newAnimator.setStartDelay(1000);  
            newAnimator.start();  
        }  
    });  

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

            repeatAnimator.cancel();  
        }  
    });  
}  

在上面的代碼中,我們?cè)邳c(diǎn)擊 btnStart 時(shí):

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

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

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

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

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

從效果圖中也可以看出,點(diǎn)擊 btnCancel 按鈕是沒有做用的,并沒能取消動(dòng)畫。

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

1、《try_tween_anim_click》:對(duì)應(yīng)概述中:舉例說明補(bǔ)間動(dòng)畫的點(diǎn)擊區(qū)域問題 2、《BlogValueAnimator1》:對(duì)應(yīng):《實(shí)例使用 ValueAnimator》和《ofInt 與 ofFloat》兩部分源碼 3、《BlogValueAnimator2》:對(duì)應(yīng)以后所有的源碼

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

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

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