long long ago,我寫過幾篇有關Animation的文章,講解了傳統的alpha、scale、translate、rotate的用法及代碼生成方法。其實這三篇文章講的全部動畫效果叫作Tween Animation(補間動畫)
在Android動畫中,總共有兩種類型的動畫View Animation(視圖動畫)和Property Animator(屬性動畫);
其中 java
首先,直觀上,他們有以下三點不一樣:
一、引入時間不一樣:View Animation是API Level 1就引入的。Property Animation是API Level 11引入的,即Android 3.0纔開始有Property Animation相關的API。
二、所在包名不一樣:View Animation在包android.view.animation中。而Property Animation API在包 android.animation中。
三、動畫類的命名不一樣:View Animation中動畫類取名都叫XXXXAnimation,而在Property Animator中動畫類的取名則叫XXXXAnimator
你們都知道逐幀動畫主要是用來實現動畫的,而補間動畫才能實現控件的漸入漸出、移動、旋轉和縮放的;而Property Animator是在Android 3.0版本才引入的,以前是沒有的。你們可能會以爲補間動畫和逐幀動畫已經很全了,爲何還要引入Property Animator呢?android
我提出一個假設:請問你們,如何利用補間動畫來將一個控件的背景色在一分鐘內從綠色變爲紅色?這個效果想必沒辦法僅僅經過改變控件的漸入漸出、移動、旋轉和縮放來實現吧,而這個效果是能夠經過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是經過改變控件內部的屬性值來達到動畫效果的ide
下面咱們就利用TranslateAnimation來作一個移動動畫的例子,看它的點擊區域是否會變。
咱們先來看看效果: 函數
在效果圖中,首先,我給textview添加了點擊響應,當點擊textview時,會彈出Toast。
而後,當我點擊按鈕的時候,textview開始向右下角移動。
從結果中能夠看出,在移動前,點擊textview是能夠彈出toast的的,在移動後,點擊textview時則沒有響應,相反,點擊textview的原來所在區域則會彈出toast. 佈局
這就論證了不一樣第三點:補間動畫雖能對控件作動畫,但並無改變控件內部的屬性值
下面簡單看看這個動畫的實現代碼吧: 動畫
從效果圖中也能夠看出,佈局很簡單,一個button,一個textview,垂直排列,佈局代碼以下:this
<?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>
接下來是操做代碼,就是分別給button和textview添加上點擊響應,當點擊textview時彈出toast,點擊button時,textview使用移動。
代碼以下:spa
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不會對控件作任何操做,咱們能夠給它設定從哪一個值運動到哪一個值,經過監聽這些值的漸變過程來本身操做控件。之前咱們曾講過Scroller類,Scroller類也是不會對控件操做的,也是經過給他設定滾動值和時長,它會本身計算滾動過程,而後咱們須要監聽它的動畫過程來本身操做控件,ValueAnimator的原理與Scroller類類似。有關Scroller的知識,你們能夠參考:《 ListView滑動刪除實現之四——Scroller類與listview緩慢滑動》.net
要使用ValueAnimaiton,總共有兩步: 3d
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作一個動畫:
咱們先看看效果圖:
首先這個動畫的佈局與上一個實例是同樣的。但實現的效果確不大相同:
下面咱們就來看看這裏如何利用ValueAnimator來實現這個效果的。
佈局代碼與上個例子相同:垂直佈局按鈕控件和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>
首先,是對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要怎麼使用了,下面咱們就來具體來看看它還有哪些經常使用的方法吧。
在上面的例子中,咱們使用了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移動到指定位置便可。
先作個彙總,這部分將講述的方法有:
/** * 設置動畫時長,單位是毫秒 */ ValueAnimator setDuration(long duration) /** * 獲取ValueAnimator在運動時,當前運動點的值 */ Object getAnimatedValue(); /** * 開始動畫 */ void start() /** * 設置循環次數,設置爲INFINITE表示無限循環 */ void setRepeatCount(int value) /** * 設置循環模式 * value取值有RESTART,REVERSE, */ void setRepeatMode(int value) /** * 取消動畫 */ void cancel()
這三個函數在上面的實例中已經使用過,setDuration(long duration)是設置一次動畫的時長,單位是毫秒,start()是開始動畫,惟一有點難度的是Object getAnimatedValue(),它的聲明爲:
Object getAnimatedValue();
它的意義就是獲取動畫在當前運動點的值,因此這個對象只能用於在動畫運動中。返回的值是Object,上面咱們說過,經過getAnimatedValue()獲得的值的實際類型與初始設置的值相同,若是咱們利用ofInt()設置的動畫,那經過getAnimatedValue()獲得的值爲類型就是Int類型。若是咱們利用ofFloat()設置的動畫,經過getAnimatedValue()獲得的值類型就是Float類型。
總而言之,經過getAnimatedValue()值類型與初始設置動畫時的值類型相同
上面咱們已經用過這些函數了,這裏就再也不舉例了。
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返回。
前面,咱們講過一個添加監聽器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()以監聽它的狀態,並把這些狀態打印出來。
咱們來看看動畫效果:
打印出來結果以下:
上面咱們講了如何添加監聽函數,下面咱們來看看如何移除監聽器:
/** * 移除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了。
好了,有關監聽器的部分,咱們就到這裏了
上面咱們講了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按鈕是沒有作用的,並沒能取消動畫。