long long ago,我寫過幾篇有關 Animation 的文章,講解了傳統(tǒng)的 alpha、scale、translate、rotate 的用法及代碼生成方法。其實這三篇文章講的所有動畫效果叫做 Tween Animation(補間動畫) 在 Android 動畫中,總共有兩種類型的動畫 View Animation(視圖動畫)和 Property Animator(屬性動畫);
其中
首先,直觀上,他們有如下三點不同: 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 呢?
我提出一個假設:請問大家,如何利用補間動畫來將一個控件的背景色在一分鐘內(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)部的屬性值來達到動畫效果的
下面我們就利用 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();
}
});
}
}
這段代碼很容易理解,這里就不再細講了。 源碼在文章底部給出
我們前面講了 Property Animator 包括 ValueAnimator 和 ObjectAnimator;這篇文章就主要來看看 ValueAnimator 的使用方法吧。 我覺得谷歌那幫老頭是最會起名字的人,單從命名上,就能看出來這個東東的含義。ValueAnimator 從名字可以看出,這個 Animation 是針對值的!ValueAnimator 不會對控件做任何操作,我們可以給它設定從哪個值運動到哪個值,通過監(jiān)聽這些值的漸變過程來自己操作控件。以前我們曾講過 Scroller 類,Scroller 類也是不會對控件操作的,也是通過給他設定滾動值和時長,它會自己計算滾動過程,然后我們需要監(jiān)聽它的動畫過程來自己操作控件,ValueAnimator 的原理與 Scroller 類相似。有關 Scroller 的知識,大家可以參考:《 ListView 滑動刪除實現(xiàn)之四——Scroller 類與 listview 緩慢滑動》
要使用 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 做一個動畫: 我們先看看效果圖:
http://wiki.jikexueyuan.com/project/android-animation/images/54.gif" alt="" />
首先這個動畫的布局與上一個實例是一樣的。但實現(xiàn)的效果確不大相同:
下面我們就來看看這里如何利用 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 要怎么使用了,下面我們就來具體來看看它還有哪些常用的方法吧。
在上面的例子中,我們使用了 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 移動到指定位置即可。
源碼在文章底部給出
先做個匯總,這部分將講述的方法有:
/**
* 設置動畫時長,單位是毫秒
*/
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 返回。 源碼在文章底部給出
(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)聽器的部分,我們就到這里了
源碼在文章底部給出
上面我們講了 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