1. 程式人生 > >android.animation(7) - android:animateLayoutChanges屬性和LayoutTransition

android.animation(7) - android:animateLayoutChanges屬性和LayoutTransition

inter 引入 pro 會有 明顯 @+ ner btn sdn

前篇給大家講了LayoutAnimation的知識,LayoutAnimation雖能實現ViewGroup的進入動畫,但只能在創建時有效。在創建後,再往裏添加控件就不會再有動畫。在API 11後,又添加了兩個能實現在創建後添加控件仍能應用動畫的方法,分別是android:animateLayoutChanges屬性和LayoutTransition類。這篇文章就來簡單說一下他們的用法。由於他們的API 等級必須>=11,API等級稍高,且存在較多問題,並不建議讀者使用,本篇只講解具體用法,不做深究.

一、android:animateLayoutChanges屬性

在API 11之後,Android為了支持ViewGroup類控件,在添加和移除其中控件時自動添加動畫,為我們提供了一個非常簡單的屬性:android:animateLayoutChanges=[true/false],所有派生自ViewGroup的控件都具有此屬性,只要在XML中添加上這個屬性,就能實現添加/刪除其中控件時,帶有默認動畫了。


我們來看下這次的效果圖:

技術分享圖片

然後來看看具體代碼是如何來做的:

1、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>

布局代碼很簡單,兩個按鈕,最底部是一個LinearLayout做為動態添加btn的container,註意,我們給它添加了android:animateLayoutChanges="true"也就是說,它內部的控件在添加和刪除時,是會帶有默認動畫。

2、MyActivity代碼

MyActivity的代碼也很簡單,就是在點擊添加按鈕時向其中動態添加一個btn,在點擊刪除按鈕時,將其中第一個按鈕給刪除。

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

代碼很簡單就不再細講了。

由上面的效果圖可見,我們只需要在viewGroup的XML中添加一行代碼android:animateLayoutChanges=[true]即可實現內部控件添加刪除時都加上動畫效果。
下面我們來做下對比,如果把上面LinearLayout中的android:animateLayoutChanges=[true]給去掉的效果是怎樣的?大家來看下原始添加控件是怎樣的,就知道默認動畫效果是什麽了。
在沒加android:animateLayoutChanges=true時:

技術分享圖片

可見,在添加和刪除控件時是沒有任何動畫的。經過對比就可知道,默認的進入動畫就是向下部控件下移,然後新添控件透明度從0到1顯示出來。默認的退出動畫是控件透明度從1變到0消失,下部控件上移。

二、LayoutTransaction

1、概述

上面雖然在ViewGroup類控件XML中僅添加一行android:animateLayoutChanges=[true]即可實現內部控件添加刪除時都加上動畫效果。但卻只能使用默認動畫效果,而無法自定義動畫。
為了能讓我們自定義動畫,谷歌在API 11時,同時為我們引入了一個類LayoutTransaction。
要使用LayoutTransaction是非常容易的,只需要三步:

第一步:創建實例

LayoutTransaction transitioner = new LayoutTransition();  

第二步:創建動畫並設置

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

第三步:將LayoutTransaction設置進ViewGroup

linearLayout.setLayoutTransition(mTransitioner);  

其中第三步中,在API 11之後,所有派生自ViewGroup類,比如LinearLayout,FrameLayout,RelativeLayout等,都具有一個專門用來設置LayoutTransition的方法:

public void setLayoutTransition(LayoutTransition transition)  

在第二步中,transitioner.setAnimator設置動畫的函數聲明為:

public void setAnimator(int transitionType, Animator animator)  

其中
第一個參數int transitionType:表示當前應用動畫的對象範圍,取值有:

  • APPEARING —— 元素在容器中出現時所定義的動畫。
  • DISAPPEARING —— 元素在容器中消失時所定義的動畫。
  • CHANGE_APPEARING —— 由於容器中要顯現一個新的元素,其它需要變化的元素所應用的動畫
  • CHANGE_DISAPPEARING —— 當容器中某個元素消失,其它需要變化的元素所應用的動畫

這幾個具體的意義,我們後面會具體來講。
第二個參數Animator animator:表示當前所選範圍的控件所使用的動畫。

2、LayoutTransition.APPEARING與LayoutTransition.DISAPPEARING示例

我們先來看看LayoutTransition.APPEARING與LayoutTransition.DISAPPEARING分別代表什麽意義:
我們先來看效果圖:

技術分享圖片

大家可以看到,在新增一個btn時,這個新增的btn會有一個繞Y軸旋轉360度的動畫。這個就是LayoutTransition.APPEARING所對應的當一個控件出現時所對應的動畫。
當我們從容器中移除一個控件時,這個被移除的控件會繞Z軸旋轉90度後,再消失。這個就是LayoutTransition.DISAPPEARING在一個控件被移除時,此被移除的控件所對應的動畫。
這樣大家就理解了,LayoutTransition.APPEARING和LayoutTransition.DISAPPEARING的意義。下面我們就來看看代碼吧。
這個示例也是建立在上個android:animateLayoutChanges屬性所對應示例的基礎上的,所以框架部分是一樣的,僅列出代碼,不再多講,只講關鍵部分
首先是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中沒有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();  
        //入場動畫:view在這個容器中消失時觸發的動畫  
        ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 0f, 360f,0f);  
        mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);  
  
        //出場動畫:view顯示時的動畫  
        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();  
        }  
  
    }  
}  

同樣是在點擊“添加控件”按鈕時,向LinearLayout中動態添加一個控件,在點擊“移除控件”按鈕時,將LinearLayout中第一個控件給移除。
但是非常註意的是我們的LayoutTransition是在OnCreate中設置的,也就是說是在LinearLayout創建時就給它定義好控件的入場動畫和出場動畫的,定義代碼如下:

mTransitioner = new LayoutTransition();  
//入場動畫:view在這個容器中消失時觸發的動畫  
ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 0f, 360f,0f);  
mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);  
  
//出場動畫:view顯示時的動畫  
ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotation", 0f, 90f, 0f);  
mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animOut);  
  
layoutTransitionGroup.setLayoutTransition(mTransitioner);  

代碼難度不大,也就是我們這節開始時所講的那三步:

第一步,定義LayoutTransition實例:

mTransitioner = new LayoutTransition();  

第二步:創建動畫並設置

//入場動畫:view在這個容器中消失時觸發的動畫  
ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 0f, 360f,0f);  
mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);  
  
//出場動畫:view顯示時的動畫  
ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotation", 0f, 90f, 0f);  
mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animOut);  

分別定義了,當一個控件被插入時,這個被插入的控件所使用的動畫:即繞Y軸旋轉360度。

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

然後是當一個控件被移除時,這個被移除的控件所使用的動畫:即繞Z軸旋轉90度:

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

第三步:將LayoutTransaction設置進ViewGroup

layoutTransitionGroup.setLayoutTransition(mTransitioner);  

這段代碼很容易理解,沒什麽難度,這裏涉及到ObjectAnimator相關的動畫知識,如果有不理解的同學請參考《Animation動畫詳解(七)——ObjectAnimator基本使用》

3、LayoutTransition.CHANGE_APPEARING與LayoutTransition.CHANGE_DISAPPEARING

我們先來看下本例的效果圖,先理解LayoutTransition.CHANGE_APPEARING和LayoutTransition.CHANGE_DISAPPEARING分別是什麽意義

技術分享圖片

在這個效果圖中,在添加控件時,除了被添加控件本身的入場動畫以外,其它需要移動位置的控件,在移動位置時,也被添加上了動畫(left點位移動畫),這些除了被添加控件以外的其它需要移動位置的控件組合,所對應的動畫就是LayoutTransition.CHANGE_APPEARING
同樣,在移除一個控件時,因為移除了一個控件,而其它所有需要改變位置的控件組合所對應的動畫就是LayoutTransition.CHANGE_DISAPPEARING,這裏LayoutTransition.CHANGE_DISAPPEARING所對應的動畫是
《 Animation動畫詳解(八)——PropertyValuesHolder與Keyframe》的響鈴效果。

(1)、LayoutTransition.CHANGE_APPEARING實現

我們這裏先看看LayoutTransition.CHANGE_APPEARING所對應的完整代碼

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();  
    //入場動畫:view在這個容器中消失時觸發的動畫  
    ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 0f, 360f,0f);  
    mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);  
  
    //出場動畫:view顯示時的動畫  
    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);  
}  

入場動畫((LayoutTransition.APPEARING)和出場動畫(LayoutTransition.DISAPPEARING)我們已經講過了,下面我們主要看看入場時,其它控件位移動畫的部分:

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

這裏有幾點註意事項:
1、LayoutTransition.CHANGE_APPEARING和LayoutTransition.CHANGE_DISAPPEARING必須使用PropertyValuesHolder所構造的動畫才會有效果,不然無效!也就是說使用ObjectAnimator構造的動畫,在這裏是不會有效果的!
2、在構造PropertyValuesHolder動畫時,”left”、”top”屬性的變動是必寫的。如果不需要變動,則直接寫為:

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

3、在構造PropertyValuesHolder時,所使用的ofInt,ofFloat中的參數值,第一個值和最後一個值必須相同,不然此屬性所對應的的動畫將被放棄,在此屬性值上將不會有效果;

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

比如,這裏ofInt(“left”,0,100,0)第一個值和最後一個值都是0,所以這裏會有效果的,如果我們改為ofInt(“left”,0,100);那麽由於首尾值不一致,則將被視為無效參數,將不會有效果!

4.在構造PropertyValuesHolder時,所使用的ofInt,ofFloat中,如果所有參數值都相同,也將不會有動畫效果。
比如:

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

在這條語句中,雖然首尾一致,但由於全程參數值相同,所以left屬性上的這個動畫會被放棄,在left屬性上也不會應用上任何動畫。
看到了吧,坑就是如此多,至於這些都是為什麽,我也懶得去研究它的源碼,因為LayoutTransition的問題實在是太!多!了!至於這篇文章嘛,由於這是一個Android 動畫的系列,而LayoutTransition也是其中一員,本著尊重知識的原則,還是給大家講一講,至於應用嘛!呵呵,慎用之……
我們上面講了,left,top屬性是必須的,下面我們給他擴展一下,除了給它添加left,top屬性以外,再給它加上scale屬性,讓它同時放大,代碼即:

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

對應動畫效果為:

技術分享圖片

(2)、LayoutTransition.CHANGE_DISAPPEARING實現

好了,我們下面來看看LayoutTransition.CHANGE_DISAPPEARING的具體實現

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屬性是必須的,但我們做響鈴效果時,是不需要Left,top變動的,所有給他們設置為無效值:(看

到了沒,必須設置的意思就是即使設置的值是無效的,也要設置!不然就會由於Left,top屬性沒有設置而整個PropertyValuesHolder動畫無效,好惡心的用法……大家可以在源碼註掉哪句話,或者把上面的所有無效設置嘗試一遍,看看效果便知)

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

第二步:用KeyFrame構造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的構造方法總共有四個:ofInt,ofFloat,ofObject,ofKeyFrame,這些方法的具體用法已經在《Animation動畫詳解(八)——PropertyValuesHolder與Keyframe》中已經詳細講解,這裏就不再贅述,有關響鈴效果也是從這篇文章中摘出,所以這裏也不再講解。
最後一步,設置LayoutTransition.CHANGE_DISAPPEARING動畫

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

對應效果為:

技術分享圖片

所以所有動畫所對應的完整代碼如下:

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();  
   //入場動畫:view在這個容器中消失時觸發的動畫  
   ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 0f, 360f,0f);  
   mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);  
  
   //出場動畫:view顯示時的動畫  
   ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotation", 0f, 90f, 0f);  
   mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animOut);  
  
   /** 
    * LayoutTransition.CHANGE_APPEARING動畫 
    */  
   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);  
  
   /** 
    * LayoutTransition.CHANGE_DISAPPEARING動畫 
    */  
   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);  
}     

4、其它函數

(1)、基本設置

上面我們講了LayoutTransition的setAnimator方法,在LayoutTransition中還有一些其它方法,下面我們來講解下:

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

除了setStagger以外,如果你把我的Animation系列一路看下來的話,其它這些函數理解起來只能說so easy,這裏就不再舉例了,下面我們講講setStagger用法與效果
我們還回來看看上面的效果圖:

技術分享圖片

在這個效果圖中,當插入一個控件時,CHANGE_APPEARING動畫時的所有控件是一起做動畫的,我們需要做動畫的控件,逐個做動畫,而不是一起全部來做動畫,setStagger就是用來設置單個item間的動畫間隔的。
在上面的基礎上,我們給LayoutTransition.CHANGE_APPEARING添加上每個item間的時間間隔30ms:

mTransitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 30);  

動畫效果為:

技術分享圖片

明顯可以看出,做LayoutTransition.CHANGE_APPEARING的控件確實是有間隔的;
完整代碼為:

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();  
    //入場動畫:view在這個容器中消失時觸發的動畫  
    ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 0f, 360f,0f);  
    mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);  
  
    //出場動畫:view顯示時的動畫  
    ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotation", 0f, 90f, 0f);  
    mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animOut);  
  
  
    /** 
     * LayoutTransition.CHANGE_APPEARING動畫 
     */  
    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);  
  
  
    /** 
     * LayoutTransition.CHANGE_DISAPPEARING動畫 
     */  
    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);  
  
    //設置單個item間的動畫間隔  
    mTransitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 30);  
  
    layoutTransitionGroup.setLayoutTransition(mTransitioner);  
}  

(2)、LayoutTransition設置監聽

LayoutTransition還給提供了一個監聽函數:

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

在任何類型的LayoutTransition開始和結束時,都會調用TransitionListener的startTransition和endTransition方法。
在TransitionListener中總共有四個參數:

  • LayoutTransition transition:當前的LayoutTransition實例
  • ViewGroup container:當前應用LayoutTransition的container
  • View view:當前在做動畫的View對象
  • int transitionType:當前的LayoutTransition類型,取值有: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());  
    }  
});  

下面是添加一個控件和刪除一個控件的日誌輸出:

技術分享圖片

首先,各transitionType的取值對應為:APPEARING = 2、CHANGE_APPEARING = 0、DISAPPEARING = 3、CHANGE_DISAPPEARING = 1
所以在添加控件時,先是start回調,再是end回調;APPEARING事件所對應的View是控件,而CHANGE_APPEARING所對應的控件是容器。刪除控件時,原理相同。
這是因為,在添加控件時,APPEARING事件只針對當前被添加的控件做動畫,所以返回的View是當前被添加的控件。而CHANGE_APPEARING是對容器中所有已存在的控件做動畫,所以返回的View是容器。

本文轉自:自定義控件三部曲之動畫篇(十二)——animateLayoutChanges與LayoutTransition

android.animation(7) - android:animateLayoutChanges屬性和LayoutTransition