ホームページ  >  記事  >  ウェブフロントエンド  >  アニメーションアニメーションの詳しい解説(12) --animateLayoutChangesとLayoutTransition_html/css_WEB-ITnose

アニメーションアニメーションの詳しい解説(12) --animateLayoutChangesとLayoutTransition_html/css_WEB-ITnose

WBOY
WBOYオリジナル
2016-06-24 11:22:441348ブラウズ

まえがき: 人生は目の前のことだけではなく、詩や遠くの野原でもあります


関連記事:

1.「アニメーション詳細解説(1) - アルファ、スケール」 、translate、rotate、xmlの属性とsetの使い方》
2.「アニメーションアニメーションの詳細解説(2) - Interpolator」
3.「アニメーションアニメーションの詳細説明(3) - コード生成アルファ、スケール、トランスレート、回転、 setとinterpolator』アニメーション》
4.『アニメーションアニメーションの詳細解説(4)~ValueAnimatorの基本的な使い方』
5.『アニメーションアニメーションの詳細説明(5)~ValueAnimatorの応用編(1)』
6.『アニメーションアニメーションの詳細説明(4)~ValueAnimatorの基本的な使い方』
6.アニメーションアニメーションの詳細解説(6) - ValueAnimator Advanced Advanced (2)》
7.「アニメーションアニメーションの詳細解説(7) - ObjectAnimatorの基本的な使い方」
8.「アニメーションアニメーションの詳細解説(8) - PropertyValuesHolderとKeyframe」
9. 《アニメーションの詳細解説(9) —— ジョイントアニメーションのコード実装》
10. 《アニメーションの詳細説明(10) —— ジョイントアニメーションのXML実装と使用例》
11. 《アニメーションの詳細説明アニメーション(11)——layoutAnimationとgridLayoutAnimation》

12. アニメーションアニメーションの詳細説明(12) - animateLayoutChangesとLayoutTransition》


前編ではLayoutAnimationの知識を説明しましたが、ViewGroupの入口アニメーションを実現することができます。作成された場合にのみ有効です。作成後にコントロールを追加してもアニメーションは表示されません。 API 11 以降、作成後にコントロールを追加した後でもアニメーションを適用できる 2 つのメソッド、つまり android:animateLayoutChanges 属性と LayoutTransition クラスが追加されました。この記事では、その使用法について簡単に説明します。 API レベルは >=11 でなければならないため、API レベルは若干高く、問題も多いため、この記事では詳細には触れずに具体的な使用方法のみを説明します。

1. android:animateLayoutChanges 属性
API 11 以降、ViewGroup クラス コントロールをサポートするために、Android はコントロールの追加と削除時にアニメーションを自動的に追加し、非常に単純な属性を提供します: 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>
レイアウト コード非常にシンプルな 2 つのボタンで、一番下のボタンは btn を動的に追加するためのコンテナーとして LinearLayout を追加しました。つまり、内部コントロールが追加および削除されるときに、それらが追加されることに注意してください。デフォルトのアニメーションが表示されます。

2. MyActivity コード
MyActivity のコードも非常に単純です。つまり、追加ボタンをクリックするとボタンが動的に追加され、削除ボタンをクリックすると最初のボタンが削除されます。
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 に変化して消え、下のコントロールが上に移動します。

ソースコードは記事の最後に記載されています

2. LayoutTransaction

1. 概要
上記ではありますが、ViewGroup クラスのコントロール XML に android:animateLayoutChanges=[true] を 1 行追加するだけでアニメーションを実現できます。内部コントロールの削除。ただし、使用できるのはデフォルトのアニメーション効果のみであり、アニメーションをカスタマイズすることはできません。
アニメーションをカスタマイズできるようにするために、Google は API 11 で LayoutTransaction クラスも導入しました。
LayoutTransaction の使用は非常に簡単で、必要なステップは 3 つだけです:

ステップ 1: インスタンスを作成する

LayoutTransaction transitioner = new LayoutTransition();
ステップ 2: アニメーションを作成して設定する

🎜
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());    }});
    看添加动画时,Log的输出是怎样的:

    对应的Log输出为:


    先看添加第一个控件时:


    在startTransition中,除去transitionType:2的APPEARING所对应的动画以外,在transitionType:0所对应的CHANGE_APPEARING时竟然也传给了LinearLayout控件!
    同样,在插入第二个控件时,CHANGE_APPEARING事件也进行了上传和监听!
    同样在删除控件时,CHANGE_DISAPPEARING也是会上传给父控件的


    所对应的Log如下:


    所以这里的结论就是:在使用addTransitionListener监听LayoutTransition过程监听时,CHANGE_APPEARING和CHANGE_DISAPPEARING事件都会上传给父类控件!

    源码在文章底部给出 

    到这里,这个系列的知识基本就讲完了,下一篇就是给大家讲讲第三方库nieOldAndroid的用法,做为Android animation动画系列的结尾。


    源码内容:
    1、《BlogAnimateLayoutChanges》:第一部分AnimateLayoutChanges属性所对应的代码
    2、《BlogLayoutTransition》:第二部分LayoutTransition所对应的代码


    如果本文有帮到你,记得加关注哦

    源码下载地址:http://download.csdn.net/download/harvic880925/9473049

    请大家尊重原创者版权,转载请标明出处:


    声明:
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。