鍍金池/ 教程/ Android/ 自定義控件三部曲之動(dòng)畫(huà)篇(十二)——animateLayoutChanges與LayoutTransition
聯(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)

自定義控件三部曲之動(dòng)畫(huà)篇(十二)——animateLayoutChanges與LayoutTransition

前篇給大家講了LayoutAnimation的知識(shí),LayoutAnimation雖能實(shí)現(xiàn)ViewGroup的進(jìn)入動(dòng)畫(huà),但只能在創(chuàng)建時(shí)有效。在創(chuàng)建后,再往里添加控件就不會(huì)再有動(dòng)畫(huà)。在API 11后,又添加了兩個(gè)能實(shí)現(xiàn)在創(chuàng)建后添加控件仍能應(yīng)用動(dòng)畫(huà)的方法,分別是android:animateLayoutChanges屬性和LayoutTransition類(lèi)。這篇文章就來(lái)簡(jiǎn)單說(shuō)一下他們的用法。由于他們的API 等級(jí)必須>=11,API等級(jí)稍高,且存在較多問(wèn)題,并不建議讀者使用,本篇只講解具體用法,不做深究.

android:animateLayoutChanges屬性

在API 11之后,Android為了支持ViewGroup類(lèi)控件,在添加和移除其中控件時(shí)自動(dòng)添加動(dòng)畫(huà),為我們提供了一個(gè)非常簡(jiǎn)單的屬性:android:animateLayoutChanges=[true/false],所有派生自ViewGroup的控件都具有此屬性,只要在XML中添加上這個(gè)屬性,就能實(shí)現(xiàn)添加/刪除其中控件時(shí),帶有默認(rèn)動(dòng)畫(huà)了。 我們來(lái)看下這次的效果圖:

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

然后來(lái)看看具體代碼是如何來(lái)做的:

main.xml布局代碼

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
              android:layout_width="match_parent"  
              android:layout_height="match_parent"  
              android:orientation="vertical">  

    <LinearLayout  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            android:orientation="horizontal">  

        <Button  
                android:id="@+id/add_btn"  
                android:layout_width="wrap_content"  
                android:layout_height="wrap_content"  
                android:text="添加控件"/>  

        <Button  
                android:id="@+id/remove_btn"  
                android:layout_width="wrap_content"  
                android:layout_height="wrap_content"  
                android:text="移除控件"/>  
    </LinearLayout>  

    <LinearLayout  
            android:id="@+id/layoutTransitionGroup"  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            android:animateLayoutChanges="true"  
            android:orientation="vertical"/>  

</LinearLayout>  

布局代碼很簡(jiǎn)單,兩個(gè)按鈕,最底部是一個(gè)LinearLayout做為動(dòng)態(tài)添加btn的container,注意,我們給它添加了android:animateLayoutChanges="true"也就是說(shuō),它內(nèi)部的控件在添加和刪除時(shí),是會(huì)帶有默認(rèn)動(dòng)畫(huà)。

MyActivity代碼

MyActivity的代碼也很簡(jiǎn)單,就是在點(diǎn)擊添加按鈕時(shí)向其中動(dòng)態(tài)添加一個(gè)btn,在點(diǎn)擊刪除按鈕時(shí),將其中第一個(gè)按鈕給刪除。

public class MyActivity extends Activity implements View.OnClickListener {  
    private LinearLayout layoutTransitionGroup;  

    private int i = 0;  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  

        layoutTransitionGroup = (LinearLayout) findViewById(R.id.layoutTransitionGroup);  
        findViewById(R.id.add_btn).setOnClickListener(this);  
        findViewById(R.id.remove_btn).setOnClickListener(this);  
    }  

    private void addButtonView() {  
        i++;  
        Button button = new Button(this);  
        button.setText("button" + i);  
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,  
                ViewGroup.LayoutParams.WRAP_CONTENT);  
        button.setLayoutParams(params);  
        layoutTransitionGroup.addView(button, 0);  
    }  

    private void removeButtonView() {  
        if (i > 0) {  
            layoutTransitionGroup.removeViewAt(0);  
        }  
        i--;  
    }  

    @Override  
    public void onClick(View v) {  
        if (v.getId() == R.id.add_btn) {  
            addButtonView();  
        }  
        if (v.getId() == R.id.remove_btn) {  
            removeButtonView();  
        }  

    }  
}  

代碼很簡(jiǎn)單就不再細(xì)講了。

由上面的效果圖可見(jiàn),我們只需要在viewGroup的XML中添加一行代碼android:animateLayoutChanges=[true]即可實(shí)現(xiàn)內(nèi)部控件添加刪除時(shí)都加上動(dòng)畫(huà)效果。

下面我們來(lái)做下對(duì)比,如果把上面LinearLayout中的android:animateLayoutChanges=[true]給去掉的效果是怎樣的?大家來(lái)看下原始添加控件是怎樣的,就知道默認(rèn)動(dòng)畫(huà)效果是什么了。 在沒(méi)加android:animateLayoutChanges=true時(shí):

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

可見(jiàn),在添加和刪除控件時(shí)是沒(méi)有任何動(dòng)畫(huà)的。經(jīng)過(guò)對(duì)比就可知道,默認(rèn)的進(jìn)入動(dòng)畫(huà)就是向下部控件下移,然后新添控件透明度從0到1顯示出來(lái)。默認(rèn)的退出動(dòng)畫(huà)是控件透明度從1變到0消失,下部控件上移。

源碼在文章底部給出

LayoutTransaction

概述

上面雖然在ViewGroup類(lèi)控件XML中僅添加一行android:animateLayoutChanges=[true]即可實(shí)現(xiàn)內(nèi)部控件添加刪除時(shí)都加上動(dòng)畫(huà)效果。但卻只能使用默認(rèn)動(dòng)畫(huà)效果,而無(wú)法自定義動(dòng)畫(huà)。

為了能讓我們自定義動(dòng)畫(huà),谷歌在API 11時(shí),同時(shí)為我們引入了一個(gè)類(lèi)LayoutTransaction。 要使用LayoutTransaction是非常容易的,只需要三步:

第一步:創(chuàng)建實(shí)例

[html] view plain copy 在CODE上查看代碼片派生到我的代碼片

LayoutTransaction transitioner = new LayoutTransition();

第二步:創(chuàng)建動(dòng)畫(huà)并設(shè)置

[java] view plain copy 在CODE上查看代碼片派生到我的代碼片

ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotation", 0f, 90f, 0f);  
transitioner.setAnimator(LayoutTransition.DISAPPEARING, animOut); 

第三步:將LayoutTransaction設(shè)置進(jìn)ViewGroup

[java] view plain copy 在CODE上查看代碼片派生到我的代碼片

linearLayout.setLayoutTransition(mTransitioner);

其中第三步中,在API 11之后,所有派生自ViewGroup類(lèi),比如LinearLayout,F(xiàn)rameLayout,RelativeLayout等,都具有一個(gè)專(zhuān)門(mén)用來(lái)設(shè)置LayoutTransition的方法:

[java] view plain copy 在CODE上查看代碼片派生到我的代碼片

public void setLayoutTransition(LayoutTransition transition)  

在第二步中,transitioner.setAnimator設(shè)置動(dòng)畫(huà)的函數(shù)聲明為:

[java] view plain copy 在CODE上查看代碼片派生到我的代碼片

public void setAnimator(int transitionType, Animator animator)  

其中

第一個(gè)參數(shù)int transitionType:表示當(dāng)前應(yīng)用動(dòng)畫(huà)的對(duì)象范圍,取值有:

  • APPEARING —— 元素在容器中出現(xiàn)時(shí)所定義的動(dòng)畫(huà)。
  • DISAPPEARING —— 元素在容器中消失時(shí)所定義的動(dòng)畫(huà)。
  • CHANGE_APPEARING —— 由于容器中要顯現(xiàn)一個(gè)新的元素,其它需要變化的元素所應(yīng)用的動(dòng)畫(huà)
  • CHANGE_DISAPPEARING —— 當(dāng)容器中某個(gè)元素消失,其它需要變化的元素所應(yīng)用的動(dòng)畫(huà)

這幾個(gè)具體的意義,我們后面會(huì)具體來(lái)講。

第二個(gè)參數(shù)Animator animator:表示當(dāng)前所選范圍的控件所使用的動(dòng)畫(huà)。

LayoutTransition.APPEARING與LayoutTransition.DISAPPEARING示例

我們先來(lái)看看LayoutTransition.APPEARING與LayoutTransition.DISAPPEARING分別代表什么意義: 我們先來(lái)看效果圖:

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

大家可以看到,在新增一個(gè)btn時(shí),這個(gè)新增的btn會(huì)有一個(gè)繞Y軸旋轉(zhuǎn)360度的動(dòng)畫(huà)。這個(gè)就是LayoutTransition.APPEARING所對(duì)應(yīng)的當(dāng)一個(gè)控件出現(xiàn)時(shí)所對(duì)應(yīng)的動(dòng)畫(huà)。

當(dāng)我們從容器中移除一個(gè)控件時(shí),這個(gè)被移除的控件會(huì)繞Z軸旋轉(zhuǎn)90度后,再消失。這個(gè)就是LayoutTransition.DISAPPEARING在一個(gè)控件被移除時(shí),此被移除的控件所對(duì)應(yīng)的動(dòng)畫(huà)。

這樣大家就理解了,LayoutTransition.APPEARING和LayoutTransition.DISAPPEARING的意義。下面我們就來(lái)看看代碼吧。

這個(gè)示例也是建立在上個(gè)android:animateLayoutChanges屬性所對(duì)應(yīng)示例的基礎(chǔ)上的,所以框架部分是一樣的,僅列出代碼,不再多講,只講關(guān)鍵部分。

首先是main.xml布局

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
              android:layout_width="match_parent"  
              android:layout_height="match_parent"  
              android:orientation="vertical">  

    <LinearLayout  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            android:orientation="horizontal">  

        <Button  
                android:id="@+id/add_btn"  
                android:layout_width="wrap_content"  
                android:layout_height="wrap_content"  
                android:text="添加控件"/>  

        <Button  
                android:id="@+id/remove_btn"  
                android:layout_width="wrap_content"  
                android:layout_height="wrap_content"  
                android:text="移除控件"/>  
    </LinearLayout>  

    <LinearLayout  
            android:id="@+id/layoutTransitionGroup"  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            android:orientation="vertical"/>  

</LinearLayout>  

布局代碼與上面一樣,但唯一不同的是在LinearLayout中沒(méi)有android:animateLayoutChanges="true"

然后是在MyActivity中的代碼處理

public class MyActivity extends Activity implements View.OnClickListener{  
    private LinearLayout layoutTransitionGroup;  
    private LayoutTransition mTransitioner;  
    private int i = 0;  

    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  

        layoutTransitionGroup = (LinearLayout) findViewById(R.id.layoutTransitionGroup);  
        findViewById(R.id.add_btn).setOnClickListener(this);  
        findViewById(R.id.remove_btn).setOnClickListener(this);  

        mTransitioner = new LayoutTransition();  
        //入場(chǎng)動(dòng)畫(huà):view在這個(gè)容器中消失時(shí)觸發(fā)的動(dòng)畫(huà)  
        ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 0f, 360f,0f);  
        mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);  

        //出場(chǎng)動(dòng)畫(huà):view顯示時(shí)的動(dòng)畫(huà)  
        ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotation", 0f, 90f, 0f);  
        mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animOut);  

        layoutTransitionGroup.setLayoutTransition(mTransitioner);  
    }  

    private void addButtonView() {  
        i++;  
        Button button = new Button(this);  
        button.setText("button" + i);  
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,  
                ViewGroup.LayoutParams.WRAP_CONTENT);  
        button.setLayoutParams(params);  
        layoutTransitionGroup.addView(button, 0);  
    }  

    private void removeButtonView() {  
        if (i > 0) {  
            layoutTransitionGroup.removeViewAt(0);  
        }  
        i--;  
    }  

    @Override  
    public void onClick(View v) {  
        if (v.getId() == R.id.add_btn) {  
            addButtonView();  
        }  
        if (v.getId() == R.id.remove_btn) {  
            removeButtonView();  
        }  

    }  
}  

同樣是在點(diǎn)擊“添加控件”按鈕時(shí),向LinearLayout中動(dòng)態(tài)添加一個(gè)控件,在點(diǎn)擊“移除控件”按鈕時(shí),將LinearLayout中第一個(gè)控件給移除。

但是非常注意的是我們的LayoutTransition是在OnCreate中設(shè)置的,也就是說(shuō)是在LinearLayout創(chuàng)建時(shí)就給它定義好控件的入場(chǎng)動(dòng)畫(huà)和出場(chǎng)動(dòng)畫(huà)的,定義代碼如下:

mTransitioner = new LayoutTransition();  
//入場(chǎng)動(dòng)畫(huà):view在這個(gè)容器中消失時(shí)觸發(fā)的動(dòng)畫(huà)  
ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 0f, 360f,0f);  
mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);  

//出場(chǎng)動(dòng)畫(huà):view顯示時(shí)的動(dòng)畫(huà)  
ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotation", 0f, 90f, 0f);  
mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animOut);  

layoutTransitionGroup.setLayoutTransition(mTransitioner);  

代碼難度不大,也就是我們這節(jié)開(kāi)始時(shí)所講的那三步:

第一步,定義LayoutTransition實(shí)例:

mTransitioner = new LayoutTransition();  

第二步:創(chuàng)建動(dòng)畫(huà)并設(shè)置

//入場(chǎng)動(dòng)畫(huà):view在這個(gè)容器中消失時(shí)觸發(fā)的動(dòng)畫(huà)  
ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 0f, 360f,0f);  
mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);  

//出場(chǎng)動(dòng)畫(huà):view顯示時(shí)的動(dòng)畫(huà)  
ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotation", 0f, 90f, 0f);  
mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animOut);  

分別定義了,當(dāng)一個(gè)控件被插入時(shí),這個(gè)被插入的控件所使用的動(dòng)畫(huà):即繞Y軸旋轉(zhuǎn)360度。

ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 0f, 360f,0f);  
mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);  

然后是當(dāng)一個(gè)控件被移除時(shí),這個(gè)被移除的控件所使用的動(dòng)畫(huà):即繞Z軸旋轉(zhuǎn)90度:

ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotation", 0f, 90f, 0f);  
mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animOut);  

第三步:將LayoutTransaction設(shè)置進(jìn)ViewGroup

layoutTransitionGroup.setLayoutTransition(mTransitioner);  

這段代碼很容易理解,沒(méi)什么難度,這里涉及到ObjectAnimator相關(guān)的動(dòng)畫(huà)知識(shí),如果有不理解的同學(xué)請(qǐng)參考

《Animation動(dòng)畫(huà)詳解(七)——ObjectAnimator基本使用》

LayoutTransition.CHANGE_APPEARING與LayoutTransition.CHANGE_DISAPPEARING

我們先來(lái)看下本例的效果圖,先理解LayoutTransition.CHANGE_APPEARING和LayoutTransition.CHANGE_DISAPPEARING分別是什么意義

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

在這個(gè)效果圖中,在添加控件時(shí),除了被添加控件本身的入場(chǎng)動(dòng)畫(huà)以外,其它需要移動(dòng)位置的控件,在移動(dòng)位置時(shí),也被添加上了動(dòng)畫(huà)(left點(diǎn)位移動(dòng)畫(huà)),這些除了被添加控件以外的其它需要移動(dòng)位置的控件組合,所對(duì)應(yīng)的動(dòng)畫(huà)就是LayoutTransition.CHANGE_APPEARING 同樣,在移除一個(gè)控件時(shí),因?yàn)橐瞥艘粋€(gè)控件,而其它所有需要改變位置的控件組合所對(duì)應(yīng)的動(dòng)畫(huà)就是LayoutTransition.CHANGE_DISAPPEARING,這里L(fēng)ayoutTransition.CHANGE_DISAPPEARING所對(duì)應(yīng)的動(dòng)畫(huà)是 《 Animation動(dòng)畫(huà)詳解(八)——PropertyValuesHolder與Keyframe》的響鈴效果。

LayoutTransition.CHANGE_APPEARING實(shí)現(xiàn)

我們這里先看看LayoutTransition.CHANGE_APPEARING所對(duì)應(yīng)的完整代碼

public void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.main);  

    layoutTransitionGroup = (LinearLayout) findViewById(R.id.layoutTransitionGroup);  
    findViewById(R.id.add_btn).setOnClickListener(this);  
    findViewById(R.id.remove_btn).setOnClickListener(this);  

    mTransitioner = new LayoutTransition();  
    //入場(chǎng)動(dòng)畫(huà):view在這個(gè)容器中消失時(shí)觸發(fā)的動(dòng)畫(huà)  
    ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 0f, 360f,0f);  
    mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);  

    //出場(chǎng)動(dòng)畫(huà):view顯示時(shí)的動(dòng)畫(huà)  
    ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotation", 0f, 90f, 0f);  
    mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animOut);  

    PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left",0,100,0);  
    PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top",1,1);  
    Animator changeAppearAnimator  
            = ObjectAnimator.ofPropertyValuesHolder(layoutTransitionGroup, pvhLeft,pvhBottom,pvhTop,pvhRight);  
    mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING,changeAppearAnimator);  

    layoutTransitionGroup.setLayoutTransition(mTransitioner);  
}  

入場(chǎng)動(dòng)畫(huà)((LayoutTransition.APPEARING)和出場(chǎng)動(dòng)畫(huà)(LayoutTransition.DISAPPEARING)我們已經(jīng)講過(guò)了,下面我們主要看看入場(chǎng)時(shí),其它控件位移動(dòng)畫(huà)的部分:

PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left",0,100,0);  
PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top",1,1);  
Animator changeAppearAnimator  
        = ObjectAnimator.ofPropertyValuesHolder(layoutTransitionGroup, pvhLeft,pvhBottom,pvhTop,pvhRight);  
mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING,changeAppearAnimator);  

這里有幾點(diǎn)注意事項(xiàng):

1、LayoutTransition.CHANGE_APPEARING和LayoutTransition.CHANGE_DISAPPEARING必須使用PropertyValuesHolder所構(gòu)造的動(dòng)畫(huà)才會(huì)有效果,不然無(wú)效!也就是說(shuō)使用ObjectAnimator構(gòu)造的動(dòng)畫(huà),在這里是不會(huì)有效果的!

2、在構(gòu)造PropertyValuesHolder動(dòng)畫(huà)時(shí),”left”、”top”屬性的變動(dòng)是必寫(xiě)的。如果不需要變動(dòng),則直接寫(xiě)為:

PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left",0,0);  
PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top",0,0); 

3、在構(gòu)造PropertyValuesHolder時(shí),所使用的ofInt,ofFloat中的參數(shù)值,第一個(gè)值和最后一個(gè)值必須相同,不然此屬性所對(duì)應(yīng)的的動(dòng)畫(huà)將被放棄,在此屬性值上將不會(huì)有效果;

PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left",0,100,0);  

比如,這里ofInt(“l(fā)eft”,0,100,0)第一個(gè)值和最后一個(gè)值都是0,所以這里會(huì)有效果的,如果我們改為ofInt(“l(fā)eft”,0,100);那么由于首尾值不一致,則將被視為無(wú)效參數(shù),將不會(huì)有效果!

4、在構(gòu)造PropertyValuesHolder時(shí),所使用的ofInt,ofFloat中,如果所有參數(shù)值都相同,也將不會(huì)有動(dòng)畫(huà)效果。

比如:

PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left",100,100);  

在這條語(yǔ)句中,雖然首尾一致,但由于全程參數(shù)值相同,所以left屬性上的這個(gè)動(dòng)畫(huà)會(huì)被放棄,在left屬性上也不會(huì)應(yīng)用上任何動(dòng)畫(huà)。

看到了吧,坑就是如此多,至于這些都是為什么,我也懶得去研究它的源碼,因?yàn)長(zhǎng)ayoutTransition的問(wèn)題實(shí)在是太!多!了!至于這篇文章嘛,由于這是一個(gè)Android 動(dòng)畫(huà)的系列,而LayoutTransition也是其中一員,本著尊重知識(shí)的原則,還是給大家講一講,至于應(yīng)用嘛!呵呵,慎用之……

我們上面講了,left,top屬性是必須的,下面我們給他擴(kuò)展一下,除了給它添加left,top屬性以外,再給它加上scale屬性,讓它同時(shí)放大,代碼即:

PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left",0,100,0);  
PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top",1,1);  
PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofFloat("ScaleX",1f,9f,1f);  
Animator changeAppearAnimator  
       = ObjectAnimator.ofPropertyValuesHolder(layoutTransitionGroup, pvhLeft,pvhTop,pvhScaleX);  
mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING,changeAppearAnimator);  

對(duì)應(yīng)動(dòng)畫(huà)效果為:

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

LayoutTransition.CHANGE_DISAPPEARING實(shí)現(xiàn)

好了,我們下面來(lái)看看LayoutTransition.CHANGE_DISAPPEARING的具體實(shí)現(xiàn)

PropertyValuesHolder outLeft = PropertyValuesHolder.ofInt("left",0,0);  
PropertyValuesHolder outTop = PropertyValuesHolder.ofInt("top",0,0);  

Keyframe frame0 = Keyframe.ofFloat(0f, 0);  
Keyframe frame1 = Keyframe.ofFloat(0.1f, -20f);  
Keyframe frame2 = Keyframe.ofFloat(0.2f, 20f);  
Keyframe frame3 = Keyframe.ofFloat(0.3f, -20f);  
Keyframe frame4 = Keyframe.ofFloat(0.4f, 20f);  
Keyframe frame5 = Keyframe.ofFloat(0.5f, -20f);  
Keyframe frame6 = Keyframe.ofFloat(0.6f, 20f);  
Keyframe frame7 = Keyframe.ofFloat(0.7f, -20f);  
Keyframe frame8 = Keyframe.ofFloat(0.8f, 20f);  
Keyframe frame9 = Keyframe.ofFloat(0.9f, -20f);  
Keyframe frame10 = Keyframe.ofFloat(1, 0);  
PropertyValuesHolder mPropertyValuesHolder = PropertyValuesHolder.ofKeyframe("rotation",frame0,frame1,frame2,frame3,frame4,frame5,frame6,frame7,frame8,frame9,frame10);  

ObjectAnimator mObjectAnimatorChangeDisAppearing = ObjectAnimator.ofPropertyValuesHolder(this, outLeft,outTop,mPropertyValuesHolder);  
mTransitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, mObjectAnimatorChangeDisAppearing);

第一步:由于left,top屬性是必須的,但我們做響鈴效果時(shí),是不需要Left,top變動(dòng)的,所有給他們?cè)O(shè)置為無(wú)效值:(看到了沒(méi),必須設(shè)置的意思就是即使設(shè)置的值是無(wú)效的,也要設(shè)置!不然就會(huì)由于Left,top屬性沒(méi)有設(shè)置而整個(gè)PropertyValuesHolder動(dòng)畫(huà)無(wú)效,好惡心的用法……大家可以在源碼注掉哪句話(huà),或者把上面的所有無(wú)效設(shè)置嘗試一遍,看看效果便知)

PropertyValuesHolder outLeft = PropertyValuesHolder.ofInt("left",0,0);  
PropertyValuesHolder outTop = PropertyValuesHolder.ofInt("top",0,0);  

第二步:用KeyFrame構(gòu)造PropertyValuesHolder

Keyframe frame0 = Keyframe.ofFloat(0f, 0);  
Keyframe frame1 = Keyframe.ofFloat(0.1f, -20f);  
Keyframe frame2 = Keyframe.ofFloat(0.2f, 20f);  
Keyframe frame3 = Keyframe.ofFloat(0.3f, -20f);  
Keyframe frame4 = Keyframe.ofFloat(0.4f, 20f);  
Keyframe frame5 = Keyframe.ofFloat(0.5f, -20f);  
Keyframe frame6 = Keyframe.ofFloat(0.6f, 20f);  
Keyframe frame7 = Keyframe.ofFloat(0.7f, -20f);  
Keyframe frame8 = Keyframe.ofFloat(0.8f, 20f);  
Keyframe frame9 = Keyframe.ofFloat(0.9f, -20f);  
Keyframe frame10 = Keyframe.ofFloat(1, 0);  
PropertyValuesHolder mPropertyValuesHolder = PropertyValuesHolder.ofKeyframe("rotation",frame0,frame1,frame2,frame3,frame4,frame5,frame6,frame7,frame8,frame9,frame10);  

PropertyValuesHolder的構(gòu)造方法總共有四個(gè):ofInt,ofFloat,ofObject,ofKeyFrame,這些方法的具體用法已經(jīng)在《Animation動(dòng)畫(huà)詳解(八)——PropertyValuesHolder與Keyframe》中已經(jīng)詳細(xì)講解,這里就不再贅述,有關(guān)響鈴效果也是從這篇文章中摘出,所以這里也不再講解。

最后一步,設(shè)置LayoutTransition.CHANGE_DISAPPEARING動(dòng)畫(huà)

ObjectAnimator mObjectAnimatorChangeDisAppearing = ObjectAnimator.ofPropertyValuesHolder(this, outLeft,outTop,mPropertyValuesHolder);  
mTransitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, mObjectAnimatorChangeDisAppearing);  

對(duì)應(yīng)效果為:

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

所以所有動(dòng)畫(huà)所對(duì)應(yīng)的完整代碼如下:

public void onCreate(Bundle savedInstanceState) {  
   super.onCreate(savedInstanceState);  
   setContentView(R.layout.main);  

   layoutTransitionGroup = (LinearLayout) findViewById(R.id.layoutTransitionGroup);  
   findViewById(R.id.add_btn).setOnClickListener(this);  
   findViewById(R.id.remove_btn).setOnClickListener(this);  

   mTransitioner = new LayoutTransition();  
   //入場(chǎng)動(dòng)畫(huà):view在這個(gè)容器中消失時(shí)觸發(fā)的動(dòng)畫(huà)  
   ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 0f, 360f,0f);  
   mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);  

   //出場(chǎng)動(dòng)畫(huà):view顯示時(shí)的動(dòng)畫(huà)  
   ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotation", 0f, 90f, 0f);  
   mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animOut);  

   /** 
    * LayoutTransition.CHANGE_APPEARING動(dòng)畫(huà) 
    */  
   PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left",0,100,0);  
   PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top",1,1);  
   //必須第一個(gè)值與最后一值相同才會(huì)有效果,不然沒(méi)有效果  
   PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofFloat("ScaleX",1f,9f,1f);  
   Animator changeAppearAnimator  
           = ObjectAnimator.ofPropertyValuesHolder(layoutTransitionGroup, pvhLeft,pvhTop,pvhScaleX);  
   mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING,changeAppearAnimator);  

   /** 
    * LayoutTransition.CHANGE_DISAPPEARING動(dòng)畫(huà) 
    */  
   PropertyValuesHolder outLeft = PropertyValuesHolder.ofInt("left",0,0);  
   PropertyValuesHolder outTop = PropertyValuesHolder.ofInt("top",0,0);  

   Keyframe frame0 = Keyframe.ofFloat(0f, 0);  
   Keyframe frame1 = Keyframe.ofFloat(0.1f, -20f);  
   Keyframe frame2 = Keyframe.ofFloat(0.2f, 20f);  
   Keyframe frame3 = Keyframe.ofFloat(0.3f, -20f);  
   Keyframe frame4 = Keyframe.ofFloat(0.4f, 20f);  
   Keyframe frame5 = Keyframe.ofFloat(0.5f, -20f);  
   Keyframe frame6 = Keyframe.ofFloat(0.6f, 20f);  
   Keyframe frame7 = Keyframe.ofFloat(0.7f, -20f);  
   Keyframe frame8 = Keyframe.ofFloat(0.8f, 20f);  
   Keyframe frame9 = Keyframe.ofFloat(0.9f, -20f);  
   Keyframe frame10 = Keyframe.ofFloat(1, 0);  
   PropertyValuesHolder mPropertyValuesHolder = PropertyValuesHolder.ofKeyframe("rotation",frame0,frame1,frame2,frame3,frame4,frame5,frame6,frame7,frame8,frame9,frame10);  

   ObjectAnimator mObjectAnimatorChangeDisAppearing = ObjectAnimator.ofPropertyValuesHolder(this, outLeft,outTop,mPropertyValuesHolder);  
   mTransitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, mObjectAnimatorChangeDisAppearing);  

   layoutTransitionGroup.setLayoutTransition(mTransitioner);  
}     

源碼在文章底部給出

其它函數(shù)

(1)、基本設(shè)置 上面我們講了LayoutTransition的setAnimator方法,在LayoutTransition中還有一些其它方法,下面我們來(lái)講解下:

/** 
 * 設(shè)置所有動(dòng)畫(huà)完成所需要的時(shí)長(zhǎng) 
 */  
public void setDuration(long duration)  
/** 
 * 針對(duì)單個(gè)type,設(shè)置動(dòng)畫(huà)時(shí)長(zhǎng); 
 * transitionType取值為:APPEARING、DISAPPEARING、CHANGE_APPEARING、CHANGE_DISAPPEARING 
 */  
public void setDuration(int transitionType, long duration)   
/** 
 * 針對(duì)單個(gè)type設(shè)置插值器 
 * transitionType取值為:APPEARING、DISAPPEARING、CHANGE_APPEARING、CHANGE_DISAPPEARING 
 */  
public void setInterpolator(int transitionType, TimeInterpolator interpolator)  
/** 
 * 針對(duì)單個(gè)type設(shè)置動(dòng)畫(huà)延時(shí) 
 * transitionType取值為:APPEARING、DISAPPEARING、CHANGE_APPEARING、CHANGE_DISAPPEARING 
 */  
public void setStartDelay(int transitionType, long delay)  
/** 
 * 針對(duì)單個(gè)type設(shè)置,每個(gè)子item動(dòng)畫(huà)的時(shí)間間隔 
 */  
public void setStagger(int transitionType, long duration)  

除了setStagger以外,如果你把我的Animation系列一路看下來(lái)的話(huà),其它這些函數(shù)理解起來(lái)只能說(shuō)so easy,這里就不再舉例了,下面我們講講setStagger用法與效果

我們還回來(lái)看看上面的效果圖:

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

在這個(gè)效果圖中,當(dāng)插入一個(gè)控件時(shí),CHANGE_APPEARING動(dòng)畫(huà)時(shí)的所有控件是一起做動(dòng)畫(huà)的,我們需要做動(dòng)畫(huà)的控件,逐個(gè)做動(dòng)畫(huà),而不是一起全部來(lái)做動(dòng)畫(huà),setStagger就是用來(lái)設(shè)置單個(gè)item間的動(dòng)畫(huà)間隔的。

在上面的基礎(chǔ)上,我們給LayoutTransition.CHANGE_APPEARING添加上每個(gè)item間的時(shí)間間隔30ms:

mTransitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 30);

動(dòng)畫(huà)效果為:

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

明顯可以看出,做LayoutTransition.CHANGE_APPEARING的控件確實(shí)是有間隔的;

完整代碼為:

public void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.main);  

    layoutTransitionGroup = (LinearLayout) findViewById(R.id.layoutTransitionGroup);  
    findViewById(R.id.add_btn).setOnClickListener(this);  
    findViewById(R.id.remove_btn).setOnClickListener(this);  

    mTransitioner = new LayoutTransition();  
    //入場(chǎng)動(dòng)畫(huà):view在這個(gè)容器中消失時(shí)觸發(fā)的動(dòng)畫(huà)  
    ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 0f, 360f,0f);  
    mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);  

    //出場(chǎng)動(dòng)畫(huà):view顯示時(shí)的動(dòng)畫(huà)  
    ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotation", 0f, 90f, 0f);  
    mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animOut);  

    /** 
     * LayoutTransition.CHANGE_APPEARING動(dòng)畫(huà) 
     */  
    PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left",0,100,0);  
    PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top",1,1);  
    //必須第一個(gè)值與最后一值相同才會(huì)有效果,不然沒(méi)有效果  
    PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofFloat("ScaleX",1f,9f,1f);  
    Animator changeAppearAnimator  
            = ObjectAnimator.ofPropertyValuesHolder(layoutTransitionGroup, pvhLeft,pvhTop,pvhScaleX);  
    mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING,changeAppearAnimator);  

    /** 
     * LayoutTransition.CHANGE_DISAPPEARING動(dòng)畫(huà) 
     */  
    PropertyValuesHolder outLeft = PropertyValuesHolder.ofInt("left",0,0);  
    PropertyValuesHolder outTop = PropertyValuesHolder.ofInt("top",0,0);  

    Keyframe frame0 = Keyframe.ofFloat(0f, 0);  
    Keyframe frame1 = Keyframe.ofFloat(0.1f, -20f);  
    Keyframe frame2 = Keyframe.ofFloat(0.2f, 20f);  
    Keyframe frame3 = Keyframe.ofFloat(0.3f, -20f);  
    Keyframe frame4 = Keyframe.ofFloat(0.4f, 20f);  
    Keyframe frame5 = Keyframe.ofFloat(0.5f, -20f);  
    Keyframe frame6 = Keyframe.ofFloat(0.6f, 20f);  
    Keyframe frame7 = Keyframe.ofFloat(0.7f, -20f);  
    Keyframe frame8 = Keyframe.ofFloat(0.8f, 20f);  
    Keyframe frame9 = Keyframe.ofFloat(0.9f, -20f);  
    Keyframe frame10 = Keyframe.ofFloat(1, 0);  
    PropertyValuesHolder mPropertyValuesHolder = PropertyValuesHolder.ofKeyframe("rotation",frame0,frame1,frame2,frame3,frame4,frame5,frame6,frame7,frame8,frame9,frame10);  

    ObjectAnimator mObjectAnimatorChangeDisAppearing = ObjectAnimator.ofPropertyValuesHolder(this, outLeft,outTop,mPropertyValuesHolder);  
    mTransitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, mObjectAnimatorChangeDisAppearing);  

    //設(shè)置單個(gè)item間的動(dòng)畫(huà)間隔  
    mTransitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 30);  

    layoutTransitionGroup.setLayoutTransition(mTransitioner);  
}  

(2)、LayoutTransition設(shè)置監(jiān)聽(tīng)

LayoutTransition還給提供了一個(gè)監(jiān)聽(tīng)函數(shù):

public void addTransitionListener(TransitionListener listener)  
//其中:  
public interface TransitionListener {  
  public void startTransition(LayoutTransition transition, ViewGroup container,View view, int transitionType);  
  public void endTransition(LayoutTransition transition, ViewGroup container,View view, int transitionType);  
}  

在任何類(lèi)型的LayoutTransition開(kāi)始和結(jié)束時(shí),都會(huì)調(diào)用TransitionListener的startTransition和endTransition方法。

在TransitionListener中總共有四個(gè)參數(shù):

  • LayoutTransition transition:當(dāng)前的LayoutTransition實(shí)例
  • ViewGroup container:當(dāng)前應(yīng)用LayoutTransition的container
  • View view:當(dāng)前在做動(dòng)畫(huà)的View對(duì)象
  • int transitionType:當(dāng)前的LayoutTransition類(lèi)型,取值有:APPEARING、DISAPPEARING、CHANGE_APPEARING、CHANGE_DISAPPEARING

如果我們給上面的示例中的mTransitioner添加上addTransitionListener,然后打上log:

mTransitioner.addTransitionListener(new LayoutTransition.TransitionListener() {  
    @Override  
    public void startTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType) {  

        Log.d("qijian","start:"+"transitionType:"+transitionType +"count:"+container.getChildCount() + "view:"+view.getClass().getName());  
    }  

    @Override  
    public void endTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType) {  
        Log.d("qijian","end:"+"transitionType:"+transitionType +"count:"+container.getChildCount() + "view:"+view.getClass().getName());  
    }  
});  

看添加動(dòng)畫(huà)時(shí),Log的輸出是怎樣的:

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

對(duì)應(yīng)的Log輸出為:

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

先看添加第一個(gè)控件時(shí):

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

在startTransition中,除去transitionType:2的APPEARING所對(duì)應(yīng)的動(dòng)畫(huà)以外,在transitionType:0所對(duì)應(yīng)的CHANGE_APPEARING時(shí)竟然也傳給了LinearLayout控件! 同樣,在插入第二個(gè)控件時(shí),CHANGE_APPEARING事件也進(jìn)行了上傳和監(jiān)聽(tīng)! 同樣在刪除控件時(shí),CHANGE_DISAPPEARING也是會(huì)上傳給父控件的

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

所對(duì)應(yīng)的Log如下:

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

所以這里的結(jié)論就是:在使用addTransitionListener監(jiān)聽(tīng)LayoutTransition過(guò)程監(jiān)聽(tīng)時(shí),CHANGE_APPEARING和CHANGE_DISAPPEARING事件都會(huì)上傳給父類(lèi)控件!

源碼在文章底部給出

到這里,這個(gè)系列的知識(shí)基本就講完了,下一篇就是給大家講講第三方庫(kù)nieOldAndroid的用法,做為Android animation動(dòng)畫(huà)系列的結(jié)尾。

源碼內(nèi)容:

1、《BlogAnimateLayoutChanges》:第一部分AnimateLayoutChanges屬性所對(duì)應(yīng)的代碼 2、《BlogLayoutTransition》:第二部分LayoutTransition所對(duì)應(yīng)的代碼

源碼下載地址:http://download.csdn.net/download/harvic880925/9473049