天天看點

自定義控件三部曲之動畫篇(九)——聯合動畫的代碼實作

前言:為了夢想,行色匆匆,是否會錯過眼前的風景?有時也會懊悔,為何當時沒能好好享受時光,但如果當時真的跟他人一樣,是否現在也會跟他人一樣羨慕現在的自己?

相關部落格:

《Android自定義控件三部曲文章索引》

上幾篇給大家分别講了ValueAnimator和ObjectAnimator,相比而言ObjectAnimator更為友善而且由于set函數是在控件類内部實作,是以封裝性更好。而且在現實使用中一般而言都是使用ObjectAnimator的機率比較大。

但ValueAnimator和ObjectAnimator都隻能單單實作一個動畫,那如果我們想要使用一個組合動畫,比如邊放大,邊移動,邊改變alpha值,要怎麼辦。對于這種組合型的動畫,谷歌給我們提供了一個類AnimatorSet;這篇我們就着重來看看組合動畫的實作方法吧。

###一、AnimatorSet——playSequentially,playTogether>

首先,AnimatorSet針對ValueAnimator和ObjectAnimator都是适用的,但一般而言,我們不會用到ValueAnimator的組合動畫,是以我們這篇僅講解ObjectAnimator下的組合動畫實作。

在AnimatorSet中直接給為我們提供了兩個方法playSequentially和playTogether,playSequentially表示所有動畫依次播放,playTogether表示所有動畫一起開始。

####1、playSequentially

我們先來看看playSequentially的聲明:

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

這裡有兩種聲明,第一個是我們最常用的,它的參數是可變長參數,也就是說我們可以傳進去任意多個Animator對象。這些對象的動畫會逐個播放。第二個構造函數,是傳進去一個List< Animator>的清單。原理一樣,也是逐個去取List中的動畫對象,然後逐個播放。但使用起來稍微麻煩一些。

下面我們就舉例來看一下playSequentially的使用方法,先來看下效果:

自定義控件三部曲之動畫篇(九)——聯合動畫的代碼實作

從效果圖中可以看到,首先改變了textview1的顔色,結束後移動textview1,在移動結束後,開始移動黃色的textview;是以這就是playSequentially的效果,即逐個播放動畫,一個動畫結束後,播放下一個動畫

下面我們來看實作代碼:

#####(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"/>

</RelativeLayout>
           

這裡也沒有什麼需要注意的地方,下面我們就直接來看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的對象,然後當點選按鈕時執行doPlaySequentiallyAnimator();函數。下面我們來看看doPlaySequentiallyAnimator()的具體實作:

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

這裡首先構造了三個動畫,針對textview1的是前兩個tv1BgAnimator和tv1TranslateY:分别是改變目前動畫背景和改變控件Y坐标位置;針對textview2則隻是通過translationY來改變控件Y坐标位置。有關動畫的建立方式,我這裡就不再講了,不了解的同學請參考上篇《Animation動畫詳解(七)——ObjectAnimator基本使用》

然後是利用AnimatorSet的playSequentially函數将這三個動畫組裝起來,逐個播放。代碼比較簡單,就不再細講。這篇我們就會在這個demo的基礎上來講解本篇所有的知識點。

源碼在文章底部給出

####2、playTogether

playTogether表示将所有動畫一起播放

我們先來看看playTogether的聲明:

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

同樣這裡也是有兩個構造函數,他們兩個的意義是一樣的,隻是傳入的參數不一樣,第一個依然是傳可變長參數清單,第二個則是需要傳一個組裝好的

Collection<Animator>

對象。

下面我們在上面例子的基礎上,看看playTogether函數的用法;

先來看看效果圖:

自定義控件三部曲之動畫篇(九)——聯合動畫的代碼實作

從效果圖中可以看到,所有動畫是一起開始播放的,下面來看看代碼:

當點選控鈕時,執行以下代碼:

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真正意義

想必大家都看到賽馬,在賽馬開始前,每個馬都會被放在起點的小門後面,到點了,門打開,馬開始一起往前跑。而假如我們把每匹馬看做是一個動畫,那我們的playTogether就相當于賽馬場裡每個賽道上門的意義(當比賽開始時,每個賽道上的門會打開,馬就可以開始比賽了);也就是說,playTogether隻是一個時間點上的一起開始,對于開始後,各個動畫怎麼操作就是他們自己的事了,至于各個動畫結不結束也是他們自已的事了。是以最恰當的描述就是門隻負責打開,打開之後馬咋跑,門也管不着,最後,馬回不回來跟門也沒啥關系。門的責任隻是到點就打開而已。放在動畫上,就是在激活動畫之後,動畫開始後的操作隻是動畫自己來負責。至于動畫結不結束,也隻有動畫自己知道。

而playSequentially的意義就是當一匹馬回來以後,再放另一匹。那如果上匹馬永遠沒回來,那下一匹馬也永遠不會被放出來。

放到動畫上,就是把激活一個動畫之後,動畫之後的操作就是動畫自己來負責了,這個動畫結束之後,再激活下一個動畫。如果上一個動畫沒有結束,那下一個動畫就永遠也不會被激活。

我們首先用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毫秒開始,并設為無限循環。tv2TranslateY設為開始延遲2000毫秒。而tv1BgAnimator則是沒有任何設定,是以是預設直接開始。我們來看效果圖:

自定義控件三部曲之動畫篇(九)——聯合動畫的代碼實作

在效果圖中可以看到,在點選按鈕以後,先進行的是tv1的顔色變化,在顔色變化完以後,tv2的延時也剛好結束,此時兩個textview開始位移變換。最後textview1的位移變換是無限循環的。

是以從這個例子中也可以看到,playTogether隻是負責在同一時間點把門拉開,拉開門以後,馬跑不跑,那是它自己的事了,回不回來,門也管不着。

playSequentially也是一樣,隻是一個回來結束以後,才打開另一個的門。如果上一個一直沒回來,那下一個也是永遠不會開始的。

通過這個例子,我想告訴大家:playTogether和playSequentially在開始動畫時,隻是把每個控件的動畫激活,至于每個控件自身的動畫是否具有延時、是否無限循環,隻與控件自身的動畫設定有關,與playTogether、playSequentially無關。playTogether和playSequentially隻負責到點激活動畫。

我們再來看一個例子:

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設定了延時開始,tv1TranslateY設定為無限循環;使用playSequentially來逐個播放這三個動畫,首先是tv1BgAnimator:在開始之後,這個動畫會延時2000毫秒再開始。結束之後,激活tv1TranslateY,這個動畫會無限循環。無限循環也就是說它永遠也不會結束。那麼第三個動畫tv2TranslateY也永遠不會開始。下面來看看效果圖:

自定義控件三部曲之動畫篇(九)——聯合動畫的代碼實作

在效果圖中也可以看出,textview1先是等了一段時間然後開始背景色變化,然後開始無限循環的上下運動。另一個textview永遠也不會開始動畫了。

源碼在文章底部給出

通過上面兩個例子,總結的時候到了:
  • 第一:playTogether和playSequentially在激活動畫後,控件的動畫情況與它們無關,他們隻負責定時激活控件動畫。
  • 第二:playSequentially隻有上一個控件做完動畫以後,才會激活下一個控件的動畫,如果上一控件的動畫是無限循環,那下一個控件就别再指望能做動畫了。

    ####4、如何實作無限循環動畫

    很多同學會一直糾結如何實作無限循環的組合動畫,因為AnimatorSet中沒有設定循環次數的函數!通過上面的講解,我們也能知道是否無限循環主要是看動畫本身,與門(playTogether)無關!

    下面我們就實作三個動畫同時開始并無限循環的動畫:

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

上面的代碼很容易了解,我們為每個動畫設定了無限循環,是以在playTogether指定開始動畫之後,每個動畫都是無限循環的。

效果圖如下:

自定義控件三部曲之動畫篇(九)——聯合動畫的代碼實作

總之:playTogether和playSequentially隻是負責指定什麼時候開始動畫,不幹涉動畫自己的運作過程。換言之:playTogether和playSequentially隻是賽馬場上的每個賽道的門,門打開以後,賽道上的那匹馬怎麼跑跟它沒什麼關系。

源碼在文章底部給出

###二、自由設定動畫順序——AnimatorSet.Builder

####1、概述

上面我們講了playTogether和playSequentially,分别能實作一起開始動畫和逐個開始動畫。但并不是非常自由的組合動畫,比如我們有三個動畫A,B,C我們想先播放C然後同時播放A和B。利用playTogether和playSequentially是沒辦法實作的,是以為了更友善的組合動畫,谷歌的開發人員另外給我們提供一個類AnimatorSet.Builder;

我們這裡使用AnimatorSet.Builder實作下面這個效果:

即兩個控件一同開始動畫

自定義控件三部曲之動畫篇(九)——聯合動畫的代碼實作
我們直接來看實作的代碼:
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();
           

關鍵部分在最後幾句:

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

首先是構造一個AnimatorSet對象。然後調用animatorSet.play(tv1BgAnimator)方法生成一個AnimatorSet.Builder對象,直接調用builder.with()就能實作兩個控件同時做動畫了,多麼神奇,下面我們來看看這個AnimatorSet.Builder的定義!

源碼在文章底部給出

####2、AnimatorSet.Builder函數

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

//調用AnimatorSet中的play方法是擷取AnimatorSet.Builder對象的唯一途徑
//表示要播放哪個動畫
public Builder play(Animator anim)
           

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

//和前面動畫一起執行
public Builder with(Animator anim)
//執行前面的動畫後才執行該動畫
public Builder before(Animator anim)
//執行先執行這個動畫再執行前面動畫
public Builder after(Animator anim)
//延遲n毫秒之後執行動畫
public Builder after(long delay)
           

play(Animator anim)表示目前在播放哪個動畫,另外的with(Animator anim)、before(Animator anim)、after(Animator anim)都是以play中的目前所播放的動畫為基準的。

比如,當play(playAnim)與before(beforeAnim)共用,則表示在播放beforeAnim之前,先播放playAnim動畫;同樣,當play(playAnim)與after(afterAnim)共用時,則表示在在播放afterAnim動畫之後,再播放playAnim動畫。

上面每個函數的意義很好了解,這裡要格外注意一點,他們每個函數的傳回值都是Builder對象,也就是說我們有兩種方式使用他們:

#####方式一:使用builder對象逐個添加動畫

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

#####方式二:串行方式

由于每個函數的傳回值都是Builder對象,是以我們是依然可以直接調用Builder的所有函數的,是以就可以用串行的方式把他們一行串起來,是以上面的代碼我們也可以寫成下面的簡化方式:

下面我們就舉個例子來看一下他們的用法,這裡實作的效果是:在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監聽器

在AnimatorSet中也可以添加監聽器,對應的監聽器為:

public static interface AnimatorListener {
    /**
     * 當AnimatorSet開始時調用
     */
    void onAnimationStart(Animator animation);

    /**
     * 當AnimatorSet結束時調用
     */
    void onAnimationEnd(Animator animation);

    /**
     * 當AnimatorSet被取消時調用
     */
    void onAnimationCancel(Animator animation);

    /**
     * 當AnimatorSet重複時調用,由于AnimatorSet沒有設定repeat的函數,是以這個方法永遠不會被調用
     */
    void onAnimationRepeat(Animator animation);
}
           

添加方法為:

好像這個listenner和ValueAnimator的一模一樣啊。不錯,确實是一模一樣,因為ValueAnimator和AnimatorSet都派生自Animator類,而AnimatorListener是Animator類中的函數。

監聽器的用法并不難,難點在于,我們AnimatorSet中的監聽器,監聽的AnimatorSet本身的動作,還是它内部的每個動畫的動作?在AnimatorSet代碼注釋中我們已經提到,它監聽的是AnimatorSet的過程,是以隻有當AnimatorSet的狀态發生變化時,才會被調用。

我們來看個例子:

額外添加一個Cancel按鈕,在點選start按鈕時,開始動畫,在點選取消按鈕時取消動畫

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

這段代碼很簡單,在點選開始時,執行 doListenerAnimation()函數, doListenerAnimation()會把構造的AnimatorSet對象傳回,在點選取消時,取消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;
}
           

這裡着重注意兩點:

第一:将動畫tv2TranslateY設定為無限循環

第二:在animatorSet添加的Animator.AnimatorListener()中每個部分添加上log

我們看一下對應的動畫及Log

自定義控件三部曲之動畫篇(九)——聯合動畫的代碼實作

對應的Log

自定義控件三部曲之動畫篇(九)——聯合動畫的代碼實作

從效果圖和對應中的Log中也可以看到,雖然我們的tv2TranslateY動畫在無限循環,但Log中沒有列印出對應的repeat的日志,從日志中也可以看出,AnimatorSet的監聽函數也隻是用來監聽AnimatorSet的狀态的,與其中的動畫無關;

是以我們來總結一下AnimatorSet的監聽:

1、AnimatorSet的監聽函數也隻是用來監聽AnimatorSet的狀态的,與其中的動畫無關;

2、AnimatorSet中沒有設定循環的函數,是以AnimatorSet監聽器中永遠無法運作到onAnimationRepeat()中!

有關如何實作無限循環的問題,我們上面已經講了,就不再贅述

源碼在文章底部給出

###四、通用函數逐個設定與AnimatorSet設定的差別

####1、概述及簡單示例

在AnimatorSet中還有幾個函數:

//設定單次動畫時長
public AnimatorSet setDuration(long duration);
//設定加速器
public void setInterpolator(TimeInterpolator interpolator)
//設定ObjectAnimator動畫目标控件
public void setTarget(Object target)
           

這幾個函數好像比較詭異,因為在ObjectAnimator中也都有這幾個函數。那在AnimatorSet中設定與在單個ObjectAnimator中設定有什麼差別呢?

差別就是:在AnimatorSet中設定以後,會覆寫單個ObjectAnimator中的設定;即如果AnimatorSet中沒有設定,那麼就以ObjectAnimator中的設定為準。如果AnimatorSet中設定以後,ObjectAnimator中的設定就會無效。

下面我們簡單舉個例子來看下

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);設定為所有動畫單詞運動時長為2000毫秒,雖然我們給tv1TranslateY設定了單次動畫時長為tv1TranslateY.setDuration(500000000);但由于AnimatorSet設定了setDuration(2000)這個參數以後,單個動畫的時長設定将無效。是以每個動畫的時長為2000毫秒。

但我們這裡還分别給tv1和tv2設定了加速器,但并沒有給AnimatorSet設定加速器,那麼tv1,tv2将按各自加速器的表現形式做動畫。

同樣,如果我們給AnimatorSet設定上了加速器,那麼單個動畫中所設定的加速器都将無效,以AnimatorSet中的加速器為準。

效果圖如下:

自定義控件三部曲之動畫篇(九)——聯合動畫的代碼實作

從動畫中也可以看到,這兩個控件同時開始,同時結束,這說明他們兩個的單次動畫的時長是一樣的。也就是以animatorSet.setDuration(2000)為準的2000毫秒。

其次,這兩個動畫在運動過程中的表現形式是完全不一樣的,這說明他們的加速器是不一樣的。也就是在AnimatorSet沒有統一設定的情況下,各自按各自的來。

####2、setTarget(Object target)示例

//設定ObjectAnimator動畫目标控件
public void setTarget(Object target)
           

這裡我們着重講一下AnimatorSet的setTartget函數,這個函數是用來設定目标控件的,也就是說,隻要通過AnimatorSet的setTartget函數設定了目标控件,那麼單個動畫中的目标控件都以AnimatorSet設定的為準

我們來看個例子:

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設定了改變背景色,給tv2設定了上下移動。但由于我們通過animatorSet.setTarget(mTv2);将各個動畫的目标控件設定為mTv2,是以tv1将不會有任何動畫,所有的動畫都會發生在tv2上。

效果圖如下:

自定義控件三部曲之動畫篇(九)——聯合動畫的代碼實作
是以AnimatorSet.setTarget()的作用就是将動畫的目标統一設定為目前控件,AnimatorSet中的所有動畫都将作用在所設定的target控件上

源碼在文章底部給出

###五、AnimatorSet之setStartDelay(long startDelay)

//設定延時開始動畫時長
public void setStartDelay(long startDelay)
           

上面我們講了,當AnimatorSet所擁有的函數與單個動畫所擁有的函數沖突時,就以AnimatorSet設定為準。但唯一的例外就是setStartDelay。

setStartDelay函數不會覆寫單個動畫的延時,而且僅針對性的延長AnimatorSet的激活時間,單個動畫的所設定的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設定了延時,是以AnimatorSet會在2000毫秒以後,才會執行start()函數。另外我們還給tv2設定了延時2000毫秒,是以在動畫開始後,tv1會直接運動,但tv2要等2000毫秒以後,才會開始運動。

這裡要特别提醒大家注意一行代碼:

在這行代碼中,我們play的是tv1!而且tv1是沒有設定延時的!這裡要非常注意一下,下面我們會深入的探讨這個問題。

我們來看看效果圖:

自定義控件三部曲之動畫篇(九)——聯合動畫的代碼實作

在這個效果圖中可以看到在點選了start anim按鈕以後,動畫并沒有立即開始,這是因為我們給AnimatorSet設定了延時;另外在AnimatorSet延時過了以後,可以看到tv1立刻開始動畫,但此時tv2并沒有任何動靜。這是因為我們單獨給tv2又設定了延時。

是以從這裡,我們可以得到一個結論:

AnimatorSet的延時是僅針對性的延長AnimatorSet激活時間的,對單個動畫的延時設定沒有影響。

####示例二:

上面我們提示大家注意動畫順序,上面的動畫順序是

我們這裡将動畫順序翻倒一下,看會是什麼結果呢?

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

我們先來看一下動畫效果:

自定義控件三部曲之動畫篇(九)——聯合動畫的代碼實作

這個動畫效果有沒有感覺非常奇怪,這裡的代碼僅僅調換了play的順序,卻與上面的效果完全不一樣!

按說這裡的效果應該與上個的效果是一樣的才對,即在AnimatorSet被激活以後,tv1應該立即運作,等2000毫秒後tv2才開始運作。

但這裡的效果卻是過了一段時間以後,tv1和tv2一起運作!

這是因為:

AnimatorSet真正激活延時 = AnimatorSet.startDelay+第一個動畫.startDelay
           

也就是說AnimatorSet被激活的真正延時等于它本身設定的setStartDelay(2000)延時再上第一個動畫的延時;

在真正的延時過了之後,動畫被激活,這時相當于賽馬場的每個跑道的門就打開了。每個動畫就按照自己的動畫處理來操作了,如果有延時就延時動畫。但由于第一個動畫的延時已經AnimatorSet被用掉了,是以第一個動畫就直接運作。

在這個例子中,由于隻有tv1有延時,而在AnimatorSet被激活後,tv1的延時被AnimatorSet用掉了,是以tv1直接運作;而在AnimatorSet激活後,由于tv2沒有設定延時,是以tv2直接運動。

下面我們再舉個例子,同樣是上面的代碼,我們如果給tv2加上延時會怎樣:

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添加了延時,而且給tv1添加了延時。

效果圖如下:

自定義控件三部曲之動畫篇(九)——聯合動畫的代碼實作

從效果圖中也可以看到,由于AnimatorSet激活延時 = AnimatorSet.startDelay+第一個動畫.startDelay;是以在4000毫秒後,動畫被激活,tv2由于已經被用掉了延時,是以在激活後直接開始。但tv1則按照自己的設定,在動畫激活後,延時2000毫秒後才開始動畫;

經過上面的例子,我們可以得出以下結論:
  • AnimatorSet的延時是僅針對性的延長AnimatorSet激活時間的,對單個動畫的延時設定沒有影響。
  • AnimatorSet真正激活延時 = AnimatorSet.startDelay+第一個動畫.startDelay
  • 在AnimatorSet激活之後,第一個動畫絕對是會開始運作的,後面的動畫則根據自己是否延時自行處理。

源碼在文章底部給出

好了,這篇文章把AnimatorSet相關的知識都講完了,看似簡單的知識,其實比較複雜。尤其是最後的延時部分,大家可以多看看源碼,多試試這些函數的用法,應該就能了解出來。下篇将帶大家來看動畫的XML實作方式。

如果本文有幫到你,記得加關注哦

源碼下載下傳位址:

csdn:http://download.csdn.net/detail/harvic880925/9446819

github:https://github.com/harvic/BlogResForGitHub

請大家尊重原創者版權,轉載請标明出處:http://blog.csdn.net/harvic880925/article/details/50759059 謝謝

如果你喜歡我的文章,你可能更喜歡我的公衆号

自定義控件三部曲之動畫篇(九)——聯合動畫的代碼實作