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

    ValueAnimator 基本使用

    一、概述

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

    其中

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

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

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

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

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

    2、舉例說(shuō)明補間動(dòng)畫(huà)的點(diǎn)擊區域問(wèn)題

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

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

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

    (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)擊響應,當點(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();  
                }  
            });  
    
        }  
    }  

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

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

    我們前面講了 Property Animator 包括 ValueAnimator 和 ObjectAnimator;這篇文章就主要來(lái)看看 ValueAnimator 的使用方法吧。 我覺(jué)得谷歌那幫老頭是最會(huì )起名字的人,單從命名上,就能看出來(lái)這個(gè)東東的含義。ValueAnimator 從名字可以看出,這個(gè) Animation 是針對值的!ValueAnimator 不會(huì )對控件做任何操作,我們可以給它設定從哪個(gè)值運動(dòng)到哪個(gè)值,通過(guò)監聽(tīng)這些值的漸變過(guò)程來(lái)自己操作控件。以前我們曾講過(guò) Scroller 類(lèi),Scroller 類(lèi)也是不會(huì )對控件操作的,也是通過(guò)給他設定滾動(dòng)值和時(shí)長(cháng),它會(huì )自己計算滾動(dòng)過(guò)程,然后我們需要監聽(tīng)它的動(dòng)畫(huà)過(guò)程來(lái)自己操作控件,ValueAnimator 的原理與 Scroller 類(lèi)相似。有關(guān) Scroller 的知識,大家可以參考:《 ListView 滑動(dòng)刪除實(shí)現之四——Scroller 類(lèi)與 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)畫(huà),動(dòng)畫(huà)時(shí)長(cháng)是 1s,然后讓動(dòng)畫(huà)開(kāi)始。從這段代碼中可以看出,ValueAnimator 沒(méi)有跟任何的控件相關(guān)聯(lián),那也正好說(shuō)明 ValueAnimator 只是對值做動(dòng)畫(huà)運算,而不是針對控件的,我們需要監聽(tīng) ValueAnimator 的動(dòng)畫(huà)過(guò)程來(lái)自己對控件做操作。

    第二步:添加監聽(tīng) 上面的三行代碼,我們已經(jīng)實(shí)現了動(dòng)畫(huà),下面我們就添加監聽(tīng):

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

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

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

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

    • ValueAnimator 只負責對指定的數字區間進(jìn)行動(dòng)畫(huà)運算
    • 我們需要對運算過(guò)程進(jìn)行監聽(tīng),然后自己對控件做動(dòng)畫(huà)操作

    2、實(shí)例使用 ValueAnimator

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

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

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

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

    下面我們就來(lái)看看這里如何利用 ValueAnimator 來(lái)實(shí)現這個(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 操作 首先,是對 textview 和 btn 添加點(diǎn)擊響應,當點(diǎn)擊 textview 時(shí),彈出 toast;點(diǎn)擊 btn 時(shí),textview 開(kāi)始做動(dòng)畫(huà)

    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í)候執行 doAnimation()來(lái)執行動(dòng)畫(huà)操作,在點(diǎn)擊 tv 的時(shí)候,彈出 toast; 下面來(lái)看看 doAnimation()的具體實(shí)現:

    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è) ValueAnimator 實(shí)例,讓其計算的值是從 0 到 400; 然后添加對計算過(guò)程進(jìn)行監聽(tīng):

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

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

    三、常用方法

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

    1、ofInt 與 ofFloat

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

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

    他們的參數類(lèi)型都是可變參數長(cháng)參數,所以我們可以傳入任何數量的值;傳進(jìn)去的值列表,就表示動(dòng)畫(huà)時(shí)的變化范圍;比如 ofInt(2,90,45)就表示從數值 2 變化到數字 90 再變化到數字 45;所以我們傳進(jìn)去的數字越多,動(dòng)畫(huà)變化就越復雜。從參數類(lèi)型也可以看出 ofInt 與 ofFloat 的唯一區別就是傳入的數字類(lèi)型不一樣,ofInt 需要傳入 Int 類(lèi)型的參數,而 ofFloat 則表示需要傳入 Float 類(lèi)型的參數。 下面我們還在上面例子的基礎上,使用 ofFloat 函數來(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 先向右下運動(dòng)然后再回來(lái),然后再向右下運動(dòng)過(guò)去 在這個(gè)例子中,我們使用 ValueAnimator.ofFloat(0f,400f,50f,300f)構造了一個(gè)比較復雜的動(dòng)畫(huà)漸變,值是 0 變到 400 再回到 50 最后變成 300; 所以我們在監聽(tīng)時(shí),首先得到當前動(dòng)畫(huà)的值:

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

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

    Object getAnimatedValue(); 

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

    源碼在文章底部給出

    2、常用函數

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

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

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

    Object getAnimatedValue(); 

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

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

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

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

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

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

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

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

    3、兩個(gè)監聽(tīng)器

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

    /** 
     * 監聽(tīng)器一:監聽(tīng)動(dòng)畫(huà)變化時(shí)的實(shí)時(shí)值 
     */  
    public static interface AnimatorUpdateListener {  
        void onAnimationUpdate(ValueAnimator animation);  
    }  
    //添加方法為:public void addUpdateListener(AnimatorUpdateListener listener)  
    /** 
     * 監聽(tīng)器二:監聽(tīng)動(dòng)畫(huà)變化時(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)于監聽(tīng)器一:AnimatorUpdateListener 就是監聽(tīng)動(dòng)畫(huà)的實(shí)時(shí)變化狀態(tài),在 onAnimationUpdate(ValueAnimator animation)中的 animation 表示當前狀態(tài)動(dòng)畫(huà)的實(shí)例。這里就不再細講這個(gè)監聽(tīng)器了,這里我們主要講講監聽(tīng)器 AnimatorListener; 在 AnimatorListener 中,主要是監聽(tīng) Animation 的四個(gè)狀態(tài),start、end、cancel、repeat;當動(dòng)畫(huà)開(kāi)始時(shí),會(huì )調用 onAnimationStart(Animator animation)方法,當動(dòng)畫(huà)結束時(shí)調用 onAnimationEnd(Animator animation),當動(dòng)畫(huà)取消時(shí),調用 onAnimationCancel(Animator animation)函數,當動(dòng)畫(huà)重復時(shí),會(huì )調用 onAnimationRepeat(Animator animation)函數。 添加 AnimatorListener 的方法是 addListener(AnimatorListener listener) ; 下面我們就舉個(gè)例子來(lái)看一下 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()以監聽(tīng)它的狀態(tài),并把這些狀態(tài)打印出來(lái)。 我們來(lái)看看動(dòng)畫(huà)效果:

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

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

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

    (2)、取消監聽(tīng) 上面我們講了如何添加監聽(tīng)函數,下面我們來(lái)看看如何移除監聽(tīng)器:

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

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

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

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

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

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

    源碼在文章底部給出

    4、其它函數

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

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

    setStartDelay(long startDelay)非常容易理解,就是設置多久后動(dòng)畫(huà)才開(kāi)始。 但 clone()這個(gè)函數就有點(diǎn)難度了;首先是什么叫克隆。就是完全一樣!注意是完全一樣!就是復制出來(lái)一個(gè)完全一樣的新的 ValueAnimator 實(shí)例出來(lái)。對原來(lái)的那個(gè) ValueAnimator 是怎么處理的,在這個(gè)新的實(shí)例中也是全部一樣的。 我們來(lái)看一個(gè)例子來(lái)看一下,什么叫全部一樣: 首先,我們定義一個(gè)函數 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í)與上面在講循環(huán)函數時(shí)的 doRepeatAnim()函數是一樣的;在這個(gè)函數中,我們定義一個(gè) ValueAnimator,設置為無(wú)限循環(huán),然后添加 AnimatorUpdateListener 監聽(tīng);在動(dòng)畫(huà)在運動(dòng)時(shí),向下移動(dòng) textview.這里要非常注意的一點(diǎn)是我們只是定義了一個(gè) ValueAnimator 對象,并沒(méi)有調用 start()讓動(dòng)畫(huà)開(kāi)始?。。?! 然后我們再看看點(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,然后開(kāi)始動(dòng)畫(huà)  
                ValueAnimator newAnimator = repeatAnimator.clone();  
                newAnimator.setStartDelay(1000);  
                newAnimator.start();  
            }  
        });  
    
        btnCancel.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                repeatAnimator.removeAllUpdateListeners();  
    
                repeatAnimator.cancel();  
            }  
        });  
    }  

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

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

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

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

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

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

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

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

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

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

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

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

    草莓视频在线观看视频6_免费草莓视频_草莓视频在线下载免费官网_草莓视频黄色在线观看