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

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

聯(lián)合動(dòng)畫(huà)的代碼實(shí)現(xiàn)

上幾篇給大家分別講了 ValueAnimator 和 ObjectAnimator,相比而言 ObjectAnimator 更為方便而且由于 set 函數(shù)是在控件類(lèi)內(nèi)部實(shí)現(xiàn),所以封裝性更好。而且在現(xiàn)實(shí)使用中一般而言都是使用 ObjectAnimator 的機(jī)率比較大。 但 ValueAnimator 和 ObjectAnimator 都只能單單實(shí)現(xiàn)一個(gè)動(dòng)畫(huà),那如果我們想要使用一個(gè)組合動(dòng)畫(huà),比如邊放大,邊移動(dòng),邊改變 alpha 值,要怎么辦。對(duì)于這種組合型的動(dòng)畫(huà),谷歌給我們提供了一個(gè)類(lèi) AnimatorSet;這篇我們就著重來(lái)看看組合動(dòng)畫(huà)的實(shí)現(xiàn)方法吧。

一、AnimatorSet——playSequentially,playTogether>

首先,AnimatorSet 針對(duì) ValueAnimator 和 ObjectAnimator 都是適用的,但一般而言,我們不會(huì)用到 ValueAnimator 的組合動(dòng)畫(huà),所以我們這篇僅講解 ObjectAnimator 下的組合動(dòng)畫(huà)實(shí)現(xiàn)。 在 AnimatorSet 中直接給為我們提供了兩個(gè)方法 playSequentially 和 playTogether,playSequentially 表示所有動(dòng)畫(huà)依次播放,playTogether 表示所有動(dòng)畫(huà)一起開(kāi)始。

1、playSequentially

我們先來(lái)看看 playSequentially 的聲明:

public void playSequentially(Animator... items);
public void playSequentially(List<Animator> items);

這里有兩種聲明,第一個(gè)是我們最常用的,它的參數(shù)是可變長(zhǎng)參數(shù),也就是說(shuō)我們可以傳進(jìn)去任意多個(gè) Animator 對(duì)象。這些對(duì)象的動(dòng)畫(huà)會(huì)逐個(gè)播放。第二個(gè)構(gòu)造函數(shù),是傳進(jìn)去一個(gè) List< Animator>的列表。原理一樣,也是逐個(gè)去取 List 中的動(dòng)畫(huà)對(duì)象,然后逐個(gè)播放。但使用起來(lái)稍微麻煩一些。 下面我們就舉例來(lái)看一下 playSequentially 的使用方法,先來(lái)看下效果:

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

從效果圖中可以看到,首先改變了 textview1 的顏色,結(jié)束后移動(dòng) textview1,在移動(dòng)結(jié)束后,開(kāi)始移動(dòng)黃色的 textview;所以這就是 playSequentially 的效果,即逐個(gè)播放動(dòng)畫(huà),一個(gè)動(dòng)畫(huà)結(jié)束后,播放下一個(gè)動(dòng)畫(huà) 下面我們來(lái)看實(shí)現(xiàn)代碼:

(1)、main.xml 布局

從效果圖中也可以看出布局非常簡(jiǎn)單,就三個(gè)控件。代碼如下:

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


這里也沒(méi)有什么需要注意的地方,下面我們就直接來(lái)看 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();
        }
    });
}
…………

}


這段代碼也沒(méi)什么難度,首先是初始化 textview1,textview2 和 btn 的對(duì)象,然后當(dāng)點(diǎn)擊按鈕時(shí)執(zhí)行 doPlaySequentiallyAnimator();函數(shù)。下面我們來(lái)看看 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)造了三個(gè)動(dòng)畫(huà),針對(duì) textview1 的是前兩個(gè) tv1BgAnimator 和 tv1TranslateY:分別是改變當(dāng)前動(dòng)畫(huà)背景和改變控件 Y 坐標(biāo)位置;針對(duì) textview2 則只是通過(guò) translationY 來(lái)改變控件 Y 坐標(biāo)位置。有關(guān)動(dòng)畫(huà)的創(chuàng)建方式,我這里就不再講了,不理解的同學(xué)請(qǐng)參考上篇《Animation 動(dòng)畫(huà)詳解(七)——ObjectAnimator 基本使用》 
然后是利用 AnimatorSet 的 playSequentially 函數(shù)將這三個(gè)動(dòng)畫(huà)組裝起來(lái),逐個(gè)播放。代碼比較簡(jiǎn)單,就不再細(xì)講。這篇我們就會(huì)在這個(gè) demo 的基礎(chǔ)上來(lái)講解本篇所有的知識(shí)點(diǎn)。 
**源碼在文章底部給出**

## 2、playTogether

playTogether 表示將所有動(dòng)畫(huà)一起播放 
我們先來(lái)看看 playTogether 的聲明:

public void playTogether(Animator... items); public void playTogether(Collection items);


同樣這里也是有兩個(gè)構(gòu)造函數(shù),他們兩個(gè)的意義是一樣的,只是傳入的參數(shù)不一樣,第一個(gè)依然是傳可變長(zhǎng)參數(shù)列表,第二個(gè)則是需要傳一個(gè)組裝好的 Collection<Animator>對(duì)象。 
下面我們?cè)谏厦胬拥幕A(chǔ)上,看看 playTogether 函數(shù)的用法; 
先來(lái)看看效果圖: 

![](images/94.gif)

從效果圖中可以看到,所有動(dòng)畫(huà)是一起開(kāi)始播放的,下面來(lái)看看代碼: 
當(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();


同樣是上面的那三個(gè)動(dòng)畫(huà),只是將 playSequentially 改為了 playTogether; 
**源碼在文章底部給出**

## 3、playSequentially,playTogether 真正意義

想必大家都看到賽馬,在賽馬開(kāi)始前,每個(gè)馬都會(huì)被放在起點(diǎn)的小門(mén)后面,到點(diǎn)了,門(mén)打開(kāi),馬開(kāi)始一起往前跑。而假如我們把每匹馬看做是一個(gè)動(dòng)畫(huà),那我們的 playTogether 就相當(dāng)于賽馬場(chǎng)里每個(gè)賽道上門(mén)的意義(當(dāng)比賽開(kāi)始時(shí),每個(gè)賽道上的門(mén)會(huì)打開(kāi),馬就可以開(kāi)始比賽了);也就是說(shuō),playTogether 只是一個(gè)時(shí)間點(diǎn)上的一起開(kāi)始,對(duì)于開(kāi)始后,各個(gè)動(dòng)畫(huà)怎么操作就是他們自己的事了,至于各個(gè)動(dòng)畫(huà)結(jié)不結(jié)束也是他們自已的事了。所以最恰當(dāng)?shù)拿枋鼍褪情T(mén)只負(fù)責(zé)打開(kāi),打開(kāi)之后馬咋跑,門(mén)也管不著,最后,馬回不回來(lái)跟門(mén)也沒(méi)啥關(guān)系。門(mén)的責(zé)任只是到點(diǎn)就打開(kāi)而已。放在動(dòng)畫(huà)上,就是在激活動(dòng)畫(huà)之后,動(dòng)畫(huà)開(kāi)始后的操作只是動(dòng)畫(huà)自己來(lái)負(fù)責(zé)。至于動(dòng)畫(huà)結(jié)不結(jié)束,也只有動(dòng)畫(huà)自己知道。 
而 playSequentially 的意義就是當(dāng)一匹馬回來(lái)以后,再放另一匹。那如果上匹馬永遠(yuǎn)沒(méi)回來(lái),那下一匹馬也永遠(yuǎn)不會(huì)被放出來(lái)。 
放到動(dòng)畫(huà)上,就是把激活一個(gè)動(dòng)畫(huà)之后,動(dòng)畫(huà)之后的操作就是動(dòng)畫(huà)自己來(lái)負(fù)責(zé)了,這個(gè)動(dòng)畫(huà)結(jié)束之后,再激活下一個(gè)動(dòng)畫(huà)。如果上一個(gè)動(dòng)畫(huà)沒(méi)有結(jié)束,那下一個(gè)動(dòng)畫(huà)就永遠(yuǎn)也不會(huì)被激活。 
我們首先用 playTogether 來(lái)看個(gè)例子:

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


在這個(gè)例子中,我們將 tv1TranslateY 開(kāi)始延遲 2000 毫秒開(kāi)始,并設(shè)為無(wú)限循環(huán)。tv2TranslateY 設(shè)為開(kāi)始延遲 2000 毫秒。而 tv1BgAnimator 則是沒(méi)有任何設(shè)置,所以是默認(rèn)直接開(kāi)始。我們來(lái)看效果圖: 

![](images/95.gif)

在效果圖中可以看到,在點(diǎn)擊按鈕以后,先進(jìn)行的是 tv1 的顏色變化,在顏色變化完以后,tv2 的延時(shí)也剛好結(jié)束,此時(shí)兩個(gè) textview 開(kāi)始位移變換。最后 textview1 的位移變換是無(wú)限循環(huán)的。 
所以從這個(gè)例子中也可以看到,playTogether 只是負(fù)責(zé)在同一時(shí)間點(diǎn)把門(mén)拉開(kāi),拉開(kāi)門(mén)以后,馬跑不跑,那是它自己的事了,回不回來(lái),門(mén)也管不著。 
playSequentially 也是一樣,只是一個(gè)回來(lái)結(jié)束以后,才打開(kāi)另一個(gè)的門(mén)。如果上一個(gè)一直沒(méi)回來(lái),那下一個(gè)也是永遠(yuǎn)不會(huì)開(kāi)始的。

> 通過(guò)這個(gè)例子,我想告訴大家:playTogether 和 playSequentially 在開(kāi)始動(dòng)畫(huà)時(shí),只是把每個(gè)控件的動(dòng)畫(huà)激活,至于每個(gè)控件自身的動(dòng)畫(huà)是否具有延時(shí)、是否無(wú)限循環(huán),只與控件自身的動(dòng)畫(huà)設(shè)定有關(guān),與 playTogether、playSequentially 無(wú)關(guān)。playTogether 和 playSequentially 只負(fù)責(zé)到點(diǎn)激活動(dòng)畫(huà)。
我們?cè)賮?lái)看一個(gè)例子:

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


同樣是那三個(gè)動(dòng)畫(huà),首先 tv1BgAnimator 設(shè)置了延時(shí)開(kāi)始,tv1TranslateY 設(shè)置為無(wú)限循環(huán);使用 playSequentially 來(lái)逐個(gè)播放這三個(gè)動(dòng)畫(huà),首先是 tv1BgAnimator:在開(kāi)始之后,這個(gè)動(dòng)畫(huà)會(huì)延時(shí) 2000 毫秒再開(kāi)始。結(jié)束之后,激活 tv1TranslateY,這個(gè)動(dòng)畫(huà)會(huì)無(wú)限循環(huán)。無(wú)限循環(huán)也就是說(shuō)它永遠(yuǎn)也不會(huì)結(jié)束。那么第三個(gè)動(dòng)畫(huà) tv2TranslateY 也永遠(yuǎn)不會(huì)開(kāi)始。下面來(lái)看看效果圖: 

![](images/96.gif)

在效果圖中也可以看出,textview1 先是等了一段時(shí)間然后開(kāi)始背景色變化,然后開(kāi)始無(wú)限循環(huán)的上下運(yùn)動(dòng)。另一個(gè) textview 永遠(yuǎn)也不會(huì)開(kāi)始動(dòng)畫(huà)了。 
**源碼在文章底部給出**

> 通過(guò)上面兩個(gè)例子,總結(jié)的時(shí)候到了: 
> - 第一:playTogether 和 playSequentially 在激活動(dòng)畫(huà)后,控件的動(dòng)畫(huà)情況與它們無(wú)關(guān),他們只負(fù)責(zé)定時(shí)激活控件動(dòng)畫(huà)。 
> - 第二:playSequentially 只有上一個(gè)控件做完動(dòng)畫(huà)以后,才會(huì)激活下一個(gè)控件的動(dòng)畫(huà),如果上一控件的動(dòng)畫(huà)是無(wú)限循環(huán),那下一個(gè)控件就別再指望能做動(dòng)畫(huà)了。
**4、如何實(shí)現(xiàn)無(wú)限循環(huán)動(dòng)畫(huà)**

很多同學(xué)會(huì)一直糾結(jié)如何實(shí)現(xiàn)無(wú)限循環(huán)的組合動(dòng)畫(huà),因?yàn)?AnimatorSet 中沒(méi)有設(shè)置循環(huán)次數(shù)的函數(shù)!通過(guò)上面的講解,我們也能知道是否無(wú)限循環(huán)主要是看動(dòng)畫(huà)本身,與門(mén)(playTogether)無(wú)關(guān)! 
下面我們就實(shí)現(xiàn)三個(gè)動(dòng)畫(huà)同時(shí)開(kāi)始并無(wú)限循環(huán)的動(dòng)畫(huà):

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)槊總€(gè)動(dòng)畫(huà)設(shè)置了無(wú)限循環(huán),所以在 playTogether 指定開(kāi)始動(dòng)畫(huà)之后,每個(gè)動(dòng)畫(huà)都是無(wú)限循環(huán)的。 
效果圖如下: 

![](images/97.gif)

> 總之:playTogether 和 playSequentially 只是負(fù)責(zé)指定什么時(shí)候開(kāi)始動(dòng)畫(huà),不干涉動(dòng)畫(huà)自己的運(yùn)行過(guò)程。換言之:playTogether 和 playSequentially 只是賽馬場(chǎng)上的每個(gè)賽道的門(mén),門(mén)打開(kāi)以后,賽道上的那匹馬怎么跑跟它沒(méi)什么關(guān)系。 
**源碼在文章底部給出**

## 二、自由設(shè)置動(dòng)畫(huà)順序——AnimatorSet.Builder

### 1、概述

上面我們講了 playTogether 和 playSequentially,分別能實(shí)現(xiàn)一起開(kāi)始動(dòng)畫(huà)和逐個(gè)開(kāi)始動(dòng)畫(huà)。但并不是非常自由的組合動(dòng)畫(huà),比如我們有三個(gè)動(dòng)畫(huà) A,B,C 我們想先播放 C 然后同時(shí)播放 A 和 B。利用 playTogether 和 playSequentially 是沒(méi)辦法實(shí)現(xiàn)的,所以為了更方便的組合動(dòng)畫(huà),谷歌的開(kāi)發(fā)人員另外給我們提供一個(gè)類(lèi) AnimatorSet.Builder; 
我們這里使用 AnimatorSet.Builder 實(shí)現(xiàn)下面這個(gè)效果: 
即兩個(gè)控件一同開(kāi)始動(dòng)畫(huà) 

![](images/98.gif)

我們直接來(lái)看實(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)造一個(gè) AnimatorSet 對(duì)象。然后調(diào)用 animatorSet.play(tv1BgAnimator)方法生成一個(gè) AnimatorSet.Builder 對(duì)象,直接調(diào)用 builder.with()就能實(shí)現(xiàn)兩個(gè)控件同時(shí)做動(dòng)畫(huà)了,多么神奇,下面我們來(lái)看看這個(gè) AnimatorSet.Builder 的定義! 
**源碼在文章底部給出**

### 2、AnimatorSet.Builder 函數(shù)

從上面的代碼中,我們可以看到 AnimatorSet.Builder 是通過(guò) animatorSet.play(tv1BgAnimator)生成的,這是生成 AnimatorSet.Builder 對(duì)象的唯一途徑!

//調(diào)用 AnimatorSet 中的 play 方法是獲取 AnimatorSet.Builder 對(duì)象的唯一途徑 //表示要播放哪個(gè)動(dòng)畫(huà) public Builder play(Animator anim)


在上面的例子中,我們已經(jīng)接觸 AnimatorSet.Builder 的 with(Animator anim)函數(shù),其實(shí)除了 with 函數(shù)以外,AnimatorSet.Builder 還有一些函數(shù),聲明如下:

//和前面動(dòng)畫(huà)一起執(zhí)行 public Builder with(Animator anim) //執(zhí)行前面動(dòng)畫(huà)前執(zhí)行動(dòng)畫(huà) public Builder before(Animator anim) //執(zhí)行前面的動(dòng)畫(huà)后執(zhí)行該動(dòng)畫(huà) public Builder after(Animator anim) //延遲 n 毫秒之后執(zhí)行動(dòng)畫(huà) public Builder after(long delay)


上面每個(gè)函數(shù)的意義很好理解,這里要格外注意一點(diǎn),他們每個(gè)函數(shù)的返回值都是 Builder 對(duì)象,也就是說(shuō)我們有兩種方式使用他們:

**方式一:使用 builder 對(duì)象逐個(gè)添加動(dòng)畫(huà)**

AnimatorSet.Builder builder = animatorSet.play(tv1TranslateY); builder.with(tv2TranslateY); builder.after(tv1BgAnimator);


**方式二:串行方式**

由于每個(gè)函數(shù)的返回值都是 Builder 對(duì)象,所以我們是依然可以直接調(diào)用 Builder 的所有函數(shù)的,所以就可以用串行的方式把他們一行串起來(lái),所以上面的代碼我們也可以寫(xiě)成下面的簡(jiǎn)化方式:

animatorSet.play(tv1TranslateY).with(tv2TranslateY).after(tv1BgAnimator);


下面我們就舉個(gè)例子來(lái)看一下他們的用法,這里實(shí)現(xiàn)的效果是:在 tv1 顏色變化后,兩個(gè)控件一同開(kāi)始位移動(dòng)畫(huà):

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


上面的代碼比較簡(jiǎn)單,就不再講了,看下效果圖: 

![](images/99.gif)

**源碼在文章底部給出**

## 三、AnimatorSet 監(jiān)聽(tīng)器

在 AnimatorSet 中也可以添加監(jiān)聽(tīng)器,對(duì)應(yīng)的監(jiān)聽(tīng)器為:

public static interface AnimatorListener { /**

  • 當(dāng) AnimatorSet 開(kāi)始時(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);

    /**

  • 當(dāng) AnimatorSet 重復(fù)時(shí)調(diào)用,由于 AnimatorSet 沒(méi)有設(shè)置 repeat 的函數(shù),所以這個(gè)方法永遠(yuǎn)不會(huì)被調(diào)用 */ void onAnimationRepeat(Animator animation); }

添加方法為:

public void addListener(AnimatorListener listener);

好像這個(gè) listenner 和 ValueAnimator 的一模一樣啊。不錯(cuò),確實(shí)是一模一樣,因?yàn)?ValueAnimator 和 AnimatorSet 都派生自 Animator 類(lèi),而 AnimatorListener 是 Animator 類(lèi)中的函數(shù)。 監(jiān)聽(tīng)器的用法并不難,難點(diǎn)在于,我們 AnimatorSet 中的監(jiān)聽(tīng)器,監(jiān)聽(tīng)的 AnimatorSet 本身的動(dòng)作,還是它內(nèi)部的每個(gè)動(dòng)畫(huà)的動(dòng)作?在 AnimatorSet 代碼注釋中我們已經(jīng)提到,它監(jiān)聽(tīng)的是 AnimatorSet 的過(guò)程,所以只有當(dāng) AnimatorSet 的狀態(tài)發(fā)生變化時(shí),才會(huì)被調(diào)用。 我們來(lái)看個(gè)例子: 額外添加一個(gè) Cancel 按鈕,在點(diǎn)擊 start 按鈕時(shí),開(kāi)始動(dòng)畫(huà),在點(diǎn)擊取消按鈕時(shí)取消動(dòng)畫(huà)

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

這段代碼很簡(jiǎn)單,在點(diǎn)擊開(kāi)始時(shí),執(zhí)行 doListenerAnimation()函數(shù), doListenerAnimation()會(huì)把構(gòu)造的 AnimatorSet 對(duì)象返回,在點(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): 第一:將動(dòng)畫(huà) tv2TranslateY 設(shè)置為無(wú)限循環(huán) 第二:在 animatorSet 添加的 Animator.AnimatorListener()中每個(gè)部分添加上 log 我們看一下對(duì)應(yīng)的動(dòng)畫(huà)及 Log

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

對(duì)應(yīng)的 Log

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

從效果圖和對(duì)應(yīng)中的 Log 中也可以看到,雖然我們的 tv2TranslateY 動(dòng)畫(huà)在無(wú)限循環(huán),但 Log 中沒(méi)有打印出對(duì)應(yīng)的 repeat 的日志,從日志中也可以看出,AnimatorSet 的監(jiān)聽(tīng)函數(shù)也只是用來(lái)監(jiān)聽(tīng) AnimatorSet 的狀態(tài)的,與其中的動(dòng)畫(huà)無(wú)關(guān);

所以我們來(lái)總結(jié)一下 AnimatorSet 的監(jiān)聽(tīng): 1、AnimatorSet 的監(jiān)聽(tīng)函數(shù)也只是用來(lái)監(jiān)聽(tīng) AnimatorSet 的狀態(tài)的,與其中的動(dòng)畫(huà)無(wú)關(guān); 2、AnimatorSet 中沒(méi)有設(shè)置循環(huán)的函數(shù),所以 AnimatorSet 監(jiān)聽(tīng)器中永遠(yuǎn)無(wú)法運(yùn)行到 onAnimationRepeat()中! 有關(guān)如何實(shí)現(xiàn)無(wú)限循環(huán)的問(wèn)題,我們上面已經(jīng)講了,就不再贅述

源碼在文章底部給出

四、通用函數(shù)逐個(gè)設(shè)置與 AnimatorSet 設(shè)置的區(qū)別

1、概述及簡(jiǎn)單示例

在 AnimatorSet 中還有幾個(gè)函數(shù):

//設(shè)置單次動(dòng)畫(huà)時(shí)長(zhǎng)
public AnimatorSet setDuration(long duration);
//設(shè)置加速器
public void setInterpolator(TimeInterpolator interpolator)
//設(shè)置 ObjectAnimator 動(dòng)畫(huà)目標(biāo)控件
public void setTarget(Object target)

這幾個(gè)函數(shù)好像比較詭異,因?yàn)樵?ObjectAnimator 中也都有這幾個(gè)函數(shù)。那在 AnimatorSet 中設(shè)置與在單個(gè) ObjectAnimator 中設(shè)置有什么區(qū)別呢?

區(qū)別就是:在 AnimatorSet 中設(shè)置以后,會(huì)覆蓋單個(gè) ObjectAnimator 中的設(shè)置;即如果 AnimatorSet 中沒(méi)有設(shè)置,那么就以 ObjectAnimator 中的設(shè)置為準(zhǔn)。如果 AnimatorSet 中設(shè)置以后,ObjectAnimator 中的設(shè)置就會(huì)無(wú)效。

下面我們簡(jiǎn)單舉個(gè)例子來(lái)看下

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

在第這個(gè)例子中,我們通過(guò) animatorSet.setDuration(2000);設(shè)置為所有動(dòng)畫(huà)單詞運(yùn)動(dòng)時(shí)長(zhǎng)為 2000 毫秒,雖然我們給 tv1TranslateY 設(shè)置了單次動(dòng)畫(huà)時(shí)長(zhǎng)為 tv1TranslateY.setDuration(500000000);但由于 AnimatorSet 設(shè)置了 setDuration(2000)這個(gè)參數(shù)以后,單個(gè)動(dòng)畫(huà)的時(shí)長(zhǎng)設(shè)置將無(wú)效。所以每個(gè)動(dòng)畫(huà)的時(shí)長(zhǎng)為 2000 毫秒。 但我們這里還分別給 tv1 和 tv2 設(shè)置了加速器,但并沒(méi)有給 AnimatorSet 設(shè)置加速器,那么 tv1,tv2 將按各自加速器的表現(xiàn)形式做動(dòng)畫(huà)。 同樣,如果我們給 AnimatorSet 設(shè)置上了加速器,那么單個(gè)動(dòng)畫(huà)中所設(shè)置的加速器都將無(wú)效,以 AnimatorSet 中的加速器為準(zhǔn)。 效果圖如下:

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

從動(dòng)畫(huà)中也可以看到,這兩個(gè)控件同時(shí)開(kāi)始,同時(shí)結(jié)束,這說(shuō)明他們兩個(gè)的單次動(dòng)畫(huà)的時(shí)長(zhǎng)是一樣的。也就是以 animatorSet.setDuration(2000)為準(zhǔn)的 2000 毫秒。 其次,這兩個(gè)動(dòng)畫(huà)在運(yùn)動(dòng)過(guò)程中的表現(xiàn)形式是完全不一樣的,這說(shuō)明他們的加速器是不一樣的。也就是在 AnimatorSet 沒(méi)有統(tǒng)一設(shè)置的情況下,各自按各自的來(lái)。

2、setTarget(Object target)示例

//設(shè)置 ObjectAnimator 動(dòng)畫(huà)目標(biāo)控件
public void setTarget(Object target)

這里我們著重講一下 AnimatorSet 的 setTartget 函數(shù),這個(gè)函數(shù)是用來(lái)設(shè)置目標(biāo)控件的,也就是說(shuō),只要通過(guò) AnimatorSet 的 setTartget 函數(shù)設(shè)置了目標(biāo)控件,那么單個(gè)動(dòng)畫(huà)中的目標(biāo)控件都以 AnimatorSet 設(shè)置的為準(zhǔn) 我們來(lái)看個(gè)例子:

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è)置了上下移動(dòng)。但由于我們通過(guò) animatorSet.setTarget(mTv2);將各個(gè)動(dòng)畫(huà)的目標(biāo)控件設(shè)置為 mTv2,所以 tv1 將不會(huì)有任何動(dòng)畫(huà),所有的動(dòng)畫(huà)都會(huì)發(fā)生在 tv2 上。 效果圖如下:

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

所以 AnimatorSet.setTarget()的作用就是將動(dòng)畫(huà)的目標(biāo)統(tǒng)一設(shè)置為當(dāng)前控件,AnimatorSet 中的所有動(dòng)畫(huà)都將作用在所設(shè)置的 target 控件上

源碼在文章底部給出

五、AnimatorSet 之 setStartDelay(long startDelay)

//設(shè)置延時(shí)開(kāi)始動(dòng)畫(huà)時(shí)長(zhǎng)
public void setStartDelay(long startDelay)

上面我們講了,當(dāng) AnimatorSet 所擁有的函數(shù)與單個(gè)動(dòng)畫(huà)所擁有的函數(shù)沖突時(shí),就以 AnimatorSet 設(shè)置為準(zhǔn)。但唯一的例外就是 setStartDelay。 setStartDelay 函數(shù)不會(huì)覆蓋單個(gè)動(dòng)畫(huà)的延時(shí),而且僅針對(duì)性的延長(zhǎng) AnimatorSet 的激活時(shí)間,單個(gè)動(dòng)畫(huà)的所設(shè)置的 setStartDelay 仍對(duì)單個(gè)動(dòng)畫(huà)起作用。

示例一:

我們來(lái)看下面的一個(gè)例子:

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

在這個(gè)動(dòng)畫(huà)中,我們首先給 AnimatorSet 設(shè)置了延時(shí),所以 AnimatorSet 會(huì)在 2000 毫秒以后,才會(huì)執(zhí)行 start()函數(shù)。另外我們還給 tv2 設(shè)置了延時(shí) 2000 毫秒,所以在動(dòng)畫(huà)開(kāi)始后,tv1 會(huì)直接運(yùn)動(dòng),但 tv2 要等 2000 毫秒以后,才會(huì)開(kāi)始運(yùn)動(dòng)。 這里要特別提醒大家注意一行代碼:

animatorSet.play(tv1TranslateY).with(tv2TranslateY);

在這行代碼中,我們 play 的是 tv1!而且 tv1 是沒(méi)有設(shè)置延時(shí)的!這里要非常注意一下,下面我們會(huì)深入的探討這個(gè)問(wèn)題。 我們來(lái)看看效果圖:

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

在這個(gè)效果圖中可以看到在點(diǎn)擊了 start anim 按鈕以后,動(dòng)畫(huà)并沒(méi)有立即開(kāi)始,這是因?yàn)槲覀兘o AnimatorSet 設(shè)置了延時(shí);另外在 AnimatorSet 延時(shí)過(guò)了以后,可以看到 tv1 立刻開(kāi)始動(dòng)畫(huà),但此時(shí) tv2 并沒(méi)有任何動(dòng)靜。這是因?yàn)槲覀儐为?dú)給 tv2 又設(shè)置了延時(shí)。 所以從這里,我們可以得到一個(gè)結(jié)論:

AnimatorSet 的延時(shí)是僅針對(duì)性的延長(zhǎng) AnimatorSet 激活時(shí)間的,對(duì)單個(gè)動(dòng)畫(huà)的延時(shí)設(shè)置沒(méi)有影響。 示例二:

上面我們提示大家注意動(dòng)畫(huà)順序,上面的動(dòng)畫(huà)順序是

animatorSet.play(tv1TranslateY).with(tv2TranslateY);

我們這里將動(dòng)畫(huà)順序翻倒一下,看會(huì)是什么結(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();

我們先來(lái)看一下動(dòng)畫(huà)效果:

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

這個(gè)動(dòng)畫(huà)效果有沒(méi)有感覺(jué)非常奇怪,這里的代碼僅僅調(diào)換了 play 的順序,卻與上面的效果完全不一樣! 按說(shuō)這里的效果應(yīng)該與上個(gè)的效果是一樣的才對(duì),即在 AnimatorSet 被激活以后,tv1 應(yīng)該立即運(yùn)行,等 2000 毫秒后 tv2 才開(kāi)始運(yùn)行。 但這里的效果卻是過(guò)了一段時(shí)間以后,tv1 和 tv2 一起運(yùn)行! 這是因?yàn)椋?/p>

AnimatorSet 真正激活延時(shí) = AnimatorSet.startDelay+第一個(gè)動(dòng)畫(huà).startDelay

也就是說(shuō) AnimatorSet 被激活的真正延時(shí)等于它本身設(shè)置的 setStartDelay(2000)延時(shí)再上第一個(gè)動(dòng)畫(huà)的延時(shí); 在真正的延時(shí)過(guò)了之后,動(dòng)畫(huà)被激活,這時(shí)相當(dāng)于賽馬場(chǎng)的每個(gè)跑道的門(mén)就打開(kāi)了。每個(gè)動(dòng)畫(huà)就按照自己的動(dòng)畫(huà)處理來(lái)操作了,如果有延時(shí)就延時(shí)動(dòng)畫(huà)。但由于第一個(gè)動(dòng)畫(huà)的延時(shí)已經(jīng) AnimatorSet 被用掉了,所以第一個(gè)動(dòng)畫(huà)就直接運(yùn)行。 在這個(gè)例子中,由于只有 tv1 有延時(shí),而在 AnimatorSet 被激活后,tv1 的延時(shí)被 AnimatorSet 用掉了,所以 tv1 直接運(yùn)行;而在 AnimatorSet 激活后,由于 tv2 沒(méi)有設(shè)置延時(shí),所以 tv2 直接運(yùn)動(dòng)。 下面我們?cè)倥e個(gè)例子,同樣是上面的代碼,我們?nèi)绻o tv2 加上延時(shí)會(huì)怎樣:

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+第一個(gè)動(dòng)畫(huà).startDelay;所以在 4000 毫秒后,動(dòng)畫(huà)被激活,tv2 由于已經(jīng)被用掉了延時(shí),所以在激活后直接開(kāi)始。但 tv1 則按照自己的設(shè)定,在動(dòng)畫(huà)激活后,延時(shí) 2000 毫秒后才開(kāi)始動(dòng)畫(huà);

經(jīng)過(guò)上面的例子,我們可以得出以下結(jié)論:

  • AnimatorSet 的延時(shí)是僅針對(duì)性的延長(zhǎng) AnimatorSet 激活時(shí)間的,對(duì)單個(gè)動(dòng)畫(huà)的延時(shí)設(shè)置沒(méi)有影響。
  • AnimatorSet 真正激活延時(shí) = AnimatorSet.startDelay+第一個(gè)動(dòng)畫(huà).startDelay
  • 在 AnimatorSet 激活之后,第一個(gè)動(dòng)畫(huà)絕對(duì)是會(huì)開(kāi)始運(yùn)行的,后面的動(dòng)畫(huà)則根據(jù)自己是否延時(shí)自行處理。 源碼在文章底部給出 好了,這篇文章把 AnimatorSet 相關(guān)的知識(shí)都講完了,看似簡(jiǎn)單的知識(shí),其實(shí)比較復(fù)雜。尤其是最后的延時(shí)部分,大家可以多看看源碼,多試試這些函數(shù)的用法,應(yīng)該就能理解出來(lái)。下篇將帶大家來(lái)看動(dòng)畫(huà)的 XML 實(shí)現(xiàn)方式。

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

源碼下載地址: csdn:http://download.csdn.net/detail/harvic880925/9446819 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/50759059 謝謝