上幾篇給大家分別講了 ValueAnimator 和 ObjectAnimator,相比而言 ObjectAnimator 更為方便而且由于 set 函數(shù)是在控件類內(nèi)部實(shí)現(xiàn),所以封裝性更好。而且在現(xiàn)實(shí)使用中一般而言都是使用 ObjectAnimator 的機(jī)率比較大。 但 ValueAnimator 和 ObjectAnimator 都只能單單實(shí)現(xiàn)一個動畫,那如果我們想要使用一個組合動畫,比如邊放大,邊移動,邊改變 alpha 值,要怎么辦。對于這種組合型的動畫,谷歌給我們提供了一個類 AnimatorSet;這篇我們就著重來看看組合動畫的實(shí)現(xiàn)方法吧。
首先,AnimatorSet 針對 ValueAnimator 和 ObjectAnimator 都是適用的,但一般而言,我們不會用到 ValueAnimator 的組合動畫,所以我們這篇僅講解 ObjectAnimator 下的組合動畫實(shí)現(xiàn)。 在 AnimatorSet 中直接給為我們提供了兩個方法 playSequentially 和 playTogether,playSequentially 表示所有動畫依次播放,playTogether 表示所有動畫一起開始。
我們先來看看 playSequentially 的聲明:
public void playSequentially(Animator... items);
public void playSequentially(List<Animator> items);
這里有兩種聲明,第一個是我們最常用的,它的參數(shù)是可變長參數(shù),也就是說我們可以傳進(jìn)去任意多個 Animator 對象。這些對象的動畫會逐個播放。第二個構(gòu)造函數(shù),是傳進(jìn)去一個 List< Animator>的列表。原理一樣,也是逐個去取 List 中的動畫對象,然后逐個播放。但使用起來稍微麻煩一些。 下面我們就舉例來看一下 playSequentially 的使用方法,先來看下效果:
http://wiki.jikexueyuan.com/project/android-animation/images/93.gif" alt="" />
從效果圖中可以看到,首先改變了 textview1 的顏色,結(jié)束后移動 textview1,在移動結(jié)束后,開始移動黃色的 textview;所以這就是 playSequentially 的效果,即逐個播放動畫,一個動畫結(jié)束后,播放下一個動畫 下面我們來看實(shí)現(xiàn)代碼:
(1)、main.xml 布局
從效果圖中也可以看出布局非常簡單,就三個控件。代碼如下:
<?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"
/>
<TextView
android:id="@+id/tv_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="30dp"
android:layout_centerVertical="true"
android:background="#ff00ff"
android:padding="10dp"
android:text="textview1"
/>
<TextView
android:id="@+id/tv_2"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:gravity="center"
android:padding="10dp"
android:background="#ffff00"
android:text="Hello qijian"/>
這里也沒有什么需要注意的地方,下面我們就直接來看 MyActivity 的代碼:
**(2)、MyActivity.java**
public class MyActivity extends Activity { private Button mButton; private TextView mTv1, mTv2;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mButton = (Button) findViewById(R.id.btn);
mTv1 = (TextView) findViewById(R.id.tv_1);
mTv2 = (TextView) findViewById(R.id.tv_2);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doPlaySequentiallyAnimator();
}
});
}
…………
}
這段代碼也沒什么難度,首先是初始化 textview1,textview2 和 btn 的對象,然后當(dāng)點(diǎn)擊按鈕時(shí)執(zhí)行 doPlaySequentiallyAnimator();函數(shù)。下面我們來看看 doPlaySequentiallyAnimator()的具體實(shí)現(xiàn):
private void doPlaySequentiallyAnimator(){ ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff); ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 300, 0); ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(tv1BgAnimator,tv1TranslateY,tv2TranslateY);
animatorSet.setDuration(1000);
animatorSet.start();
}
這里首先構(gòu)造了三個動畫,針對 textview1 的是前兩個 tv1BgAnimator 和 tv1TranslateY:分別是改變當(dāng)前動畫背景和改變控件 Y 坐標(biāo)位置;針對 textview2 則只是通過 translationY 來改變控件 Y 坐標(biāo)位置。有關(guān)動畫的創(chuàng)建方式,我這里就不再講了,不理解的同學(xué)請參考上篇《Animation 動畫詳解(七)——ObjectAnimator 基本使用》
然后是利用 AnimatorSet 的 playSequentially 函數(shù)將這三個動畫組裝起來,逐個播放。代碼比較簡單,就不再細(xì)講。這篇我們就會在這個 demo 的基礎(chǔ)上來講解本篇所有的知識點(diǎn)。
**源碼在文章底部給出**
## 2、playTogether
playTogether 表示將所有動畫一起播放
我們先來看看 playTogether 的聲明:
public void playTogether(Animator... items);
public void playTogether(Collection
同樣這里也是有兩個構(gòu)造函數(shù),他們兩個的意義是一樣的,只是傳入的參數(shù)不一樣,第一個依然是傳可變長參數(shù)列表,第二個則是需要傳一個組裝好的 Collection<Animator>對象。
下面我們在上面例子的基礎(chǔ)上,看看 playTogether 函數(shù)的用法;
先來看看效果圖:

從效果圖中可以看到,所有動畫是一起開始播放的,下面來看看代碼:
當(dāng)點(diǎn)擊控鈕時(shí),執(zhí)行以下代碼:
ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff); ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0); ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);
AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(tv1BgAnimator,tv1TranslateY,tv2TranslateY); animatorSet.setDuration(1000); animatorSet.start();
同樣是上面的那三個動畫,只是將 playSequentially 改為了 playTogether;
**源碼在文章底部給出**
## 3、playSequentially,playTogether 真正意義
想必大家都看到賽馬,在賽馬開始前,每個馬都會被放在起點(diǎn)的小門后面,到點(diǎn)了,門打開,馬開始一起往前跑。而假如我們把每匹馬看做是一個動畫,那我們的 playTogether 就相當(dāng)于賽馬場里每個賽道上門的意義(當(dāng)比賽開始時(shí),每個賽道上的門會打開,馬就可以開始比賽了);也就是說,playTogether 只是一個時(shí)間點(diǎn)上的一起開始,對于開始后,各個動畫怎么操作就是他們自己的事了,至于各個動畫結(jié)不結(jié)束也是他們自已的事了。所以最恰當(dāng)?shù)拿枋鼍褪情T只負(fù)責(zé)打開,打開之后馬咋跑,門也管不著,最后,馬回不回來跟門也沒啥關(guān)系。門的責(zé)任只是到點(diǎn)就打開而已。放在動畫上,就是在激活動畫之后,動畫開始后的操作只是動畫自己來負(fù)責(zé)。至于動畫結(jié)不結(jié)束,也只有動畫自己知道。
而 playSequentially 的意義就是當(dāng)一匹馬回來以后,再放另一匹。那如果上匹馬永遠(yuǎn)沒回來,那下一匹馬也永遠(yuǎn)不會被放出來。
放到動畫上,就是把激活一個動畫之后,動畫之后的操作就是動畫自己來負(fù)責(zé)了,這個動畫結(jié)束之后,再激活下一個動畫。如果上一個動畫沒有結(jié)束,那下一個動畫就永遠(yuǎn)也不會被激活。
我們首先用 playTogether 來看個例子:
ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);
ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0); tv1TranslateY.setStartDelay(2000); tv1TranslateY.setRepeatCount(ValueAnimator.INFINITE);
ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0); tv2TranslateY.setStartDelay(2000);
AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(tv1BgAnimator,tv1TranslateY,tv2TranslateY); animatorSet.setDuration(2000); animatorSet.start();
在這個例子中,我們將 tv1TranslateY 開始延遲 2000 毫秒開始,并設(shè)為無限循環(huán)。tv2TranslateY 設(shè)為開始延遲 2000 毫秒。而 tv1BgAnimator 則是沒有任何設(shè)置,所以是默認(rèn)直接開始。我們來看效果圖:

在效果圖中可以看到,在點(diǎn)擊按鈕以后,先進(jìn)行的是 tv1 的顏色變化,在顏色變化完以后,tv2 的延時(shí)也剛好結(jié)束,此時(shí)兩個 textview 開始位移變換。最后 textview1 的位移變換是無限循環(huán)的。
所以從這個例子中也可以看到,playTogether 只是負(fù)責(zé)在同一時(shí)間點(diǎn)把門拉開,拉開門以后,馬跑不跑,那是它自己的事了,回不回來,門也管不著。
playSequentially 也是一樣,只是一個回來結(jié)束以后,才打開另一個的門。如果上一個一直沒回來,那下一個也是永遠(yuǎn)不會開始的。
> 通過這個例子,我想告訴大家:playTogether 和 playSequentially 在開始動畫時(shí),只是把每個控件的動畫激活,至于每個控件自身的動畫是否具有延時(shí)、是否無限循環(huán),只與控件自身的動畫設(shè)定有關(guān),與 playTogether、playSequentially 無關(guān)。playTogether 和 playSequentially 只負(fù)責(zé)到點(diǎn)激活動畫。
我們再來看一個例子:
ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff); tv1BgAnimator.setStartDelay(2000);
ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 300, 0); tv1TranslateY.setRepeatCount(ValueAnimator.INFINITE);
ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);
AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playSequentially(tv1BgAnimator,tv1TranslateY,tv2TranslateY); animatorSet.setDuration(2000); animatorSet.start();
同樣是那三個動畫,首先 tv1BgAnimator 設(shè)置了延時(shí)開始,tv1TranslateY 設(shè)置為無限循環(huán);使用 playSequentially 來逐個播放這三個動畫,首先是 tv1BgAnimator:在開始之后,這個動畫會延時(shí) 2000 毫秒再開始。結(jié)束之后,激活 tv1TranslateY,這個動畫會無限循環(huán)。無限循環(huán)也就是說它永遠(yuǎn)也不會結(jié)束。那么第三個動畫 tv2TranslateY 也永遠(yuǎn)不會開始。下面來看看效果圖:

在效果圖中也可以看出,textview1 先是等了一段時(shí)間然后開始背景色變化,然后開始無限循環(huán)的上下運(yùn)動。另一個 textview 永遠(yuǎn)也不會開始動畫了。
**源碼在文章底部給出**
> 通過上面兩個例子,總結(jié)的時(shí)候到了:
> - 第一:playTogether 和 playSequentially 在激活動畫后,控件的動畫情況與它們無關(guān),他們只負(fù)責(zé)定時(shí)激活控件動畫。
> - 第二:playSequentially 只有上一個控件做完動畫以后,才會激活下一個控件的動畫,如果上一控件的動畫是無限循環(huán),那下一個控件就別再指望能做動畫了。
**4、如何實(shí)現(xiàn)無限循環(huán)動畫**
很多同學(xué)會一直糾結(jié)如何實(shí)現(xiàn)無限循環(huán)的組合動畫,因?yàn)?AnimatorSet 中沒有設(shè)置循環(huán)次數(shù)的函數(shù)!通過上面的講解,我們也能知道是否無限循環(huán)主要是看動畫本身,與門(playTogether)無關(guān)!
下面我們就實(shí)現(xiàn)三個動畫同時(shí)開始并無限循環(huán)的動畫:
ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff); tv1BgAnimator.setRepeatCount(ValueAnimator.INFINITE); ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0); tv1TranslateY.setRepeatCount(ValueAnimator.INFINITE); ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0); tv2TranslateY.setRepeatCount(ValueAnimator.INFINITE);
AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(tv1BgAnimator,tv1TranslateY,tv2TranslateY); animatorSet.setDuration(2000); animatorSet.start();
上面的代碼很容易理解,我們?yōu)槊總€動畫設(shè)置了無限循環(huán),所以在 playTogether 指定開始動畫之后,每個動畫都是無限循環(huán)的。
效果圖如下:

> 總之:playTogether 和 playSequentially 只是負(fù)責(zé)指定什么時(shí)候開始動畫,不干涉動畫自己的運(yùn)行過程。換言之:playTogether 和 playSequentially 只是賽馬場上的每個賽道的門,門打開以后,賽道上的那匹馬怎么跑跟它沒什么關(guān)系。
**源碼在文章底部給出**
## 二、自由設(shè)置動畫順序——AnimatorSet.Builder
### 1、概述
上面我們講了 playTogether 和 playSequentially,分別能實(shí)現(xiàn)一起開始動畫和逐個開始動畫。但并不是非常自由的組合動畫,比如我們有三個動畫 A,B,C 我們想先播放 C 然后同時(shí)播放 A 和 B。利用 playTogether 和 playSequentially 是沒辦法實(shí)現(xiàn)的,所以為了更方便的組合動畫,谷歌的開發(fā)人員另外給我們提供一個類 AnimatorSet.Builder;
我們這里使用 AnimatorSet.Builder 實(shí)現(xiàn)下面這個效果:
即兩個控件一同開始動畫

我們直接來看實(shí)現(xiàn)的代碼:
ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff); ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0);
AnimatorSet animatorSet = new AnimatorSet(); AnimatorSet.Builder builder = animatorSet.play(tv1BgAnimator); builder.with(tv1TranslateY); animatorSet.start();
關(guān)鍵部分在最后幾句:
AnimatorSet animatorSet = new AnimatorSet(); AnimatorSet.Builder builder = animatorSet.play(tv1BgAnimator); builder.with(tv1TranslateY);
首先是構(gòu)造一個 AnimatorSet 對象。然后調(diào)用 animatorSet.play(tv1BgAnimator)方法生成一個 AnimatorSet.Builder 對象,直接調(diào)用 builder.with()就能實(shí)現(xiàn)兩個控件同時(shí)做動畫了,多么神奇,下面我們來看看這個 AnimatorSet.Builder 的定義!
**源碼在文章底部給出**
### 2、AnimatorSet.Builder 函數(shù)
從上面的代碼中,我們可以看到 AnimatorSet.Builder 是通過 animatorSet.play(tv1BgAnimator)生成的,這是生成 AnimatorSet.Builder 對象的唯一途徑!
//調(diào)用 AnimatorSet 中的 play 方法是獲取 AnimatorSet.Builder 對象的唯一途徑 //表示要播放哪個動畫 public Builder play(Animator anim)
在上面的例子中,我們已經(jīng)接觸 AnimatorSet.Builder 的 with(Animator anim)函數(shù),其實(shí)除了 with 函數(shù)以外,AnimatorSet.Builder 還有一些函數(shù),聲明如下:
//和前面動畫一起執(zhí)行 public Builder with(Animator anim) //執(zhí)行前面動畫前執(zhí)行動畫 public Builder before(Animator anim) //執(zhí)行前面的動畫后執(zhí)行該動畫 public Builder after(Animator anim) //延遲 n 毫秒之后執(zhí)行動畫 public Builder after(long delay)
上面每個函數(shù)的意義很好理解,這里要格外注意一點(diǎn),他們每個函數(shù)的返回值都是 Builder 對象,也就是說我們有兩種方式使用他們:
**方式一:使用 builder 對象逐個添加動畫**
AnimatorSet.Builder builder = animatorSet.play(tv1TranslateY); builder.with(tv2TranslateY); builder.after(tv1BgAnimator);
**方式二:串行方式**
由于每個函數(shù)的返回值都是 Builder 對象,所以我們是依然可以直接調(diào)用 Builder 的所有函數(shù)的,所以就可以用串行的方式把他們一行串起來,所以上面的代碼我們也可以寫成下面的簡化方式:
animatorSet.play(tv1TranslateY).with(tv2TranslateY).after(tv1BgAnimator);
下面我們就舉個例子來看一下他們的用法,這里實(shí)現(xiàn)的效果是:在 tv1 顏色變化后,兩個控件一同開始位移動畫:
ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff); ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0); ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);
AnimatorSet animatorSet = new AnimatorSet(); animatorSet.play(tv1TranslateY).with(tv2TranslateY).after(tv1BgAnimator); animatorSet.setDuration(2000); animatorSet.start();
上面的代碼比較簡單,就不再講了,看下效果圖:

**源碼在文章底部給出**
## 三、AnimatorSet 監(jiān)聽器
在 AnimatorSet 中也可以添加監(jiān)聽器,對應(yīng)的監(jiān)聽器為:
public static interface AnimatorListener { /**
當(dāng) AnimatorSet 開始時(shí)調(diào)用 */ void onAnimationStart(Animator animation);
/**
當(dāng) AnimatorSet 結(jié)束時(shí)調(diào)用 */ void onAnimationEnd(Animator animation);
/**
當(dāng) AnimatorSet 被取消時(shí)調(diào)用 */ void onAnimationCancel(Animator animation);
/**
添加方法為:
public void addListener(AnimatorListener listener);
好像這個 listenner 和 ValueAnimator 的一模一樣啊。不錯,確實(shí)是一模一樣,因?yàn)?ValueAnimator 和 AnimatorSet 都派生自 Animator 類,而 AnimatorListener 是 Animator 類中的函數(shù)。 監(jiān)聽器的用法并不難,難點(diǎn)在于,我們 AnimatorSet 中的監(jiān)聽器,監(jiān)聽的 AnimatorSet 本身的動作,還是它內(nèi)部的每個動畫的動作?在 AnimatorSet 代碼注釋中我們已經(jīng)提到,它監(jiān)聽的是 AnimatorSet 的過程,所以只有當(dāng) AnimatorSet 的狀態(tài)發(fā)生變化時(shí),才會被調(diào)用。 我們來看個例子: 額外添加一個 Cancel 按鈕,在點(diǎn)擊 start 按鈕時(shí),開始動畫,在點(diǎn)擊取消按鈕時(shí)取消動畫
private AnimatorSet mAnimatorSet;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mButton = (Button) findViewById(R.id.btn);
mBtnCancel = (Button) findViewById(R.id.btn_cancel);
mTv1 = (TextView) findViewById(R.id.tv_1);
mTv2 = (TextView) findViewById(R.id.tv_2);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mAnimatorSet = doListenerAnimation();
}
});
mBtnCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (null != mAnimatorSet) {
mAnimatorSet.cancel();
}
}
});
}
這段代碼很簡單,在點(diǎn)擊開始時(shí),執(zhí)行 doListenerAnimation()函數(shù), doListenerAnimation()會把構(gòu)造的 AnimatorSet 對象返回,在點(diǎn)擊取消時(shí),取消 AnimatorSet; 然后看一下 doListenerAnimation()的代碼:
private AnimatorSet doListenerAnimation() {
ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);
ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0);
ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);
tv2TranslateY.setRepeatCount(ValueAnimator.INFINITE);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(tv1TranslateY).with(tv2TranslateY).after(tv1BgAnimator);
//添加 listener
animatorSet.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
Log.d(tag, "animator start");
}
@Override
public void onAnimationEnd(Animator animation) {
Log.d(tag, "animator end");
}
@Override
public void onAnimationCancel(Animator animation) {
Log.d(tag, "animator cancel");
}
@Override
public void onAnimationRepeat(Animator animation) {
Log.d(tag, "animator repeat");
}
});
animatorSet.setDuration(2000);
animatorSet.start();
return animatorSet;
}
這里著重注意兩點(diǎn): 第一:將動畫 tv2TranslateY 設(shè)置為無限循環(huán) 第二:在 animatorSet 添加的 Animator.AnimatorListener()中每個部分添加上 log 我們看一下對應(yīng)的動畫及 Log
http://wiki.jikexueyuan.com/project/android-animation/images/100.gif" alt="" />
對應(yīng)的 Log
http://wiki.jikexueyuan.com/project/android-animation/images/18.png" alt="" />
從效果圖和對應(yīng)中的 Log 中也可以看到,雖然我們的 tv2TranslateY 動畫在無限循環(huán),但 Log 中沒有打印出對應(yīng)的 repeat 的日志,從日志中也可以看出,AnimatorSet 的監(jiān)聽函數(shù)也只是用來監(jiān)聽 AnimatorSet 的狀態(tài)的,與其中的動畫無關(guān);
所以我們來總結(jié)一下 AnimatorSet 的監(jiān)聽: 1、AnimatorSet 的監(jiān)聽函數(shù)也只是用來監(jiān)聽 AnimatorSet 的狀態(tài)的,與其中的動畫無關(guān); 2、AnimatorSet 中沒有設(shè)置循環(huán)的函數(shù),所以 AnimatorSet 監(jiān)聽器中永遠(yuǎn)無法運(yùn)行到 onAnimationRepeat()中! 有關(guān)如何實(shí)現(xiàn)無限循環(huán)的問題,我們上面已經(jīng)講了,就不再贅述
源碼在文章底部給出
在 AnimatorSet 中還有幾個函數(shù):
//設(shè)置單次動畫時(shí)長
public AnimatorSet setDuration(long duration);
//設(shè)置加速器
public void setInterpolator(TimeInterpolator interpolator)
//設(shè)置 ObjectAnimator 動畫目標(biāo)控件
public void setTarget(Object target)
這幾個函數(shù)好像比較詭異,因?yàn)樵?ObjectAnimator 中也都有這幾個函數(shù)。那在 AnimatorSet 中設(shè)置與在單個 ObjectAnimator 中設(shè)置有什么區(qū)別呢?
區(qū)別就是:在 AnimatorSet 中設(shè)置以后,會覆蓋單個 ObjectAnimator 中的設(shè)置;即如果 AnimatorSet 中沒有設(shè)置,那么就以 ObjectAnimator 中的設(shè)置為準(zhǔn)。如果 AnimatorSet 中設(shè)置以后,ObjectAnimator 中的設(shè)置就會無效。
下面我們簡單舉個例子來看下
ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0);
tv1TranslateY.setDuration(500000000);
tv1TranslateY.setInterpolator(new BounceInterpolator());
ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);
tv2TranslateY.setInterpolator(new AccelerateDecelerateInterpolator());
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(tv2TranslateY).with(tv1TranslateY);
animatorSet.setDuration(2000);
animatorSet.start();
在第這個例子中,我們通過 animatorSet.setDuration(2000);設(shè)置為所有動畫單詞運(yùn)動時(shí)長為 2000 毫秒,雖然我們給 tv1TranslateY 設(shè)置了單次動畫時(shí)長為 tv1TranslateY.setDuration(500000000);但由于 AnimatorSet 設(shè)置了 setDuration(2000)這個參數(shù)以后,單個動畫的時(shí)長設(shè)置將無效。所以每個動畫的時(shí)長為 2000 毫秒。 但我們這里還分別給 tv1 和 tv2 設(shè)置了加速器,但并沒有給 AnimatorSet 設(shè)置加速器,那么 tv1,tv2 將按各自加速器的表現(xiàn)形式做動畫。 同樣,如果我們給 AnimatorSet 設(shè)置上了加速器,那么單個動畫中所設(shè)置的加速器都將無效,以 AnimatorSet 中的加速器為準(zhǔn)。 效果圖如下:
http://wiki.jikexueyuan.com/project/android-animation/images/101.gif" alt="" />
從動畫中也可以看到,這兩個控件同時(shí)開始,同時(shí)結(jié)束,這說明他們兩個的單次動畫的時(shí)長是一樣的。也就是以 animatorSet.setDuration(2000)為準(zhǔn)的 2000 毫秒。 其次,這兩個動畫在運(yùn)動過程中的表現(xiàn)形式是完全不一樣的,這說明他們的加速器是不一樣的。也就是在 AnimatorSet 沒有統(tǒng)一設(shè)置的情況下,各自按各自的來。
//設(shè)置 ObjectAnimator 動畫目標(biāo)控件
public void setTarget(Object target)
這里我們著重講一下 AnimatorSet 的 setTartget 函數(shù),這個函數(shù)是用來設(shè)置目標(biāo)控件的,也就是說,只要通過 AnimatorSet 的 setTartget 函數(shù)設(shè)置了目標(biāo)控件,那么單個動畫中的目標(biāo)控件都以 AnimatorSet 設(shè)置的為準(zhǔn) 我們來看個例子:
ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);
ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(tv1BgAnimator,tv2TranslateY);
animatorSet.setDuration(2000);
animatorSet.setTarget(mTv2);
animatorSet.start();
在這段代碼中,我們給 tv1 設(shè)置了改變背景色,給 tv2 設(shè)置了上下移動。但由于我們通過 animatorSet.setTarget(mTv2);將各個動畫的目標(biāo)控件設(shè)置為 mTv2,所以 tv1 將不會有任何動畫,所有的動畫都會發(fā)生在 tv2 上。 效果圖如下:
http://wiki.jikexueyuan.com/project/android-animation/images/102.gif" alt="" />
所以 AnimatorSet.setTarget()的作用就是將動畫的目標(biāo)統(tǒng)一設(shè)置為當(dāng)前控件,AnimatorSet 中的所有動畫都將作用在所設(shè)置的 target 控件上
源碼在文章底部給出
//設(shè)置延時(shí)開始動畫時(shí)長
public void setStartDelay(long startDelay)
上面我們講了,當(dāng) AnimatorSet 所擁有的函數(shù)與單個動畫所擁有的函數(shù)沖突時(shí),就以 AnimatorSet 設(shè)置為準(zhǔn)。但唯一的例外就是 setStartDelay。 setStartDelay 函數(shù)不會覆蓋單個動畫的延時(shí),而且僅針對性的延長 AnimatorSet 的激活時(shí)間,單個動畫的所設(shè)置的 setStartDelay 仍對單個動畫起作用。
示例一:
我們來看下面的一個例子:
ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0);
ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);
tv2TranslateY.setStartDelay(2000);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(tv1TranslateY).with(tv2TranslateY);
animatorSet.setStartDelay(2000);
animatorSet.setDuration(2000);
animatorSet.start();
在這個動畫中,我們首先給 AnimatorSet 設(shè)置了延時(shí),所以 AnimatorSet 會在 2000 毫秒以后,才會執(zhí)行 start()函數(shù)。另外我們還給 tv2 設(shè)置了延時(shí) 2000 毫秒,所以在動畫開始后,tv1 會直接運(yùn)動,但 tv2 要等 2000 毫秒以后,才會開始運(yùn)動。 這里要特別提醒大家注意一行代碼:
animatorSet.play(tv1TranslateY).with(tv2TranslateY);
在這行代碼中,我們 play 的是 tv1!而且 tv1 是沒有設(shè)置延時(shí)的!這里要非常注意一下,下面我們會深入的探討這個問題。 我們來看看效果圖:
http://wiki.jikexueyuan.com/project/android-animation/images/103.gif" alt="" />
在這個效果圖中可以看到在點(diǎn)擊了 start anim 按鈕以后,動畫并沒有立即開始,這是因?yàn)槲覀兘o AnimatorSet 設(shè)置了延時(shí);另外在 AnimatorSet 延時(shí)過了以后,可以看到 tv1 立刻開始動畫,但此時(shí) tv2 并沒有任何動靜。這是因?yàn)槲覀儐为?dú)給 tv2 又設(shè)置了延時(shí)。 所以從這里,我們可以得到一個結(jié)論:
AnimatorSet 的延時(shí)是僅針對性的延長 AnimatorSet 激活時(shí)間的,對單個動畫的延時(shí)設(shè)置沒有影響。 示例二:
上面我們提示大家注意動畫順序,上面的動畫順序是
animatorSet.play(tv1TranslateY).with(tv2TranslateY);
我們這里將動畫順序翻倒一下,看會是什么結(jié)果呢?
ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0);
ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);
tv2TranslateY.setStartDelay(2000);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(tv2TranslateY).with(tv1TranslateY);
animatorSet.setStartDelay(2000);
animatorSet.setDuration(2000);
animatorSet.start();
我們先來看一下動畫效果:
http://wiki.jikexueyuan.com/project/android-animation/images/104.gif" alt="" />
這個動畫效果有沒有感覺非常奇怪,這里的代碼僅僅調(diào)換了 play 的順序,卻與上面的效果完全不一樣! 按說這里的效果應(yīng)該與上個的效果是一樣的才對,即在 AnimatorSet 被激活以后,tv1 應(yīng)該立即運(yùn)行,等 2000 毫秒后 tv2 才開始運(yùn)行。 但這里的效果卻是過了一段時(shí)間以后,tv1 和 tv2 一起運(yùn)行! 這是因?yàn)椋?/p>
AnimatorSet 真正激活延時(shí) = AnimatorSet.startDelay+第一個動畫.startDelay
也就是說 AnimatorSet 被激活的真正延時(shí)等于它本身設(shè)置的 setStartDelay(2000)延時(shí)再上第一個動畫的延時(shí); 在真正的延時(shí)過了之后,動畫被激活,這時(shí)相當(dāng)于賽馬場的每個跑道的門就打開了。每個動畫就按照自己的動畫處理來操作了,如果有延時(shí)就延時(shí)動畫。但由于第一個動畫的延時(shí)已經(jīng) AnimatorSet 被用掉了,所以第一個動畫就直接運(yùn)行。 在這個例子中,由于只有 tv1 有延時(shí),而在 AnimatorSet 被激活后,tv1 的延時(shí)被 AnimatorSet 用掉了,所以 tv1 直接運(yùn)行;而在 AnimatorSet 激活后,由于 tv2 沒有設(shè)置延時(shí),所以 tv2 直接運(yùn)動。 下面我們再舉個例子,同樣是上面的代碼,我們?nèi)绻o tv2 加上延時(shí)會怎樣:
ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0);
tv1TranslateY.setStartDelay(2000);
ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);
tv2TranslateY.setStartDelay(2000);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(tv2TranslateY).with(tv1TranslateY);
animatorSet.setStartDelay(2000);
animatorSet.setDuration(2000);
animatorSet.start();
代碼與上面的一樣,只是不僅給 tv2 添加了延時(shí),而且給 tv1 添加了延時(shí)。 效果圖如下:
http://wiki.jikexueyuan.com/project/android-animation/images/105.gif" alt="" />
從效果圖中也可以看到,由于 AnimatorSet 激活延時(shí) = AnimatorSet.startDelay+第一個動畫.startDelay;所以在 4000 毫秒后,動畫被激活,tv2 由于已經(jīng)被用掉了延時(shí),所以在激活后直接開始。但 tv1 則按照自己的設(shè)定,在動畫激活后,延時(shí) 2000 毫秒后才開始動畫;
經(jīng)過上面的例子,我們可以得出以下結(jié)論:
- AnimatorSet 的延時(shí)是僅針對性的延長 AnimatorSet 激活時(shí)間的,對單個動畫的延時(shí)設(shè)置沒有影響。
- AnimatorSet 真正激活延時(shí) = AnimatorSet.startDelay+第一個動畫.startDelay
- 在 AnimatorSet 激活之后,第一個動畫絕對是會開始運(yùn)行的,后面的動畫則根據(jù)自己是否延時(shí)自行處理。 源碼在文章底部給出 好了,這篇文章把 AnimatorSet 相關(guān)的知識都講完了,看似簡單的知識,其實(shí)比較復(fù)雜。尤其是最后的延時(shí)部分,大家可以多看看源碼,多試試這些函數(shù)的用法,應(yīng)該就能理解出來。下篇將帶大家來看動畫的 XML 實(shí)現(xiàn)方式。
如果本文有幫到你,記得加關(guān)注哦
源碼下載地址: csdn:http://download.csdn.net/detail/harvic880925/9446819 github:https://github.com/harvic/BlogResForGitHub 請大家尊重原創(chuàng)者版權(quán),轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/harvic880925/article/details/50759059 謝謝