zoukankan      html  css  js  c++  java
  • Android Animation学习(五) ApiDemos解析:容器布局动画 LayoutTransition

    Property animation系统还提供了对ViewGroup中的View改变加入动画的功能。

      你可以使用 LayoutTransition 对ViewGroup中的View改变进行动画显示。

      注意,本文所说的动画效果都是设置给容器(ViewGroup),然而效果是通过容器存放的View来体现的。

    四种容器转换动画类型

      当你添加或者移除ViewGroup中的View时,或者你调用View的setVisibility()方法来控制其显示或消失时,就处于一个转换状态。这种事件就有可能会激发动画。

      当前被增加或者移除的View可以经历一个出现的动画或者一个消失的动画。

      而且不止是当前要控制的View,ViewGroup中的其他View也可以随之进行变动,比如经历一个动画移动到新的位置。

      所以一共有四种相关的动画类型

      1.View本身的出现动画;

      2.消失动画;

      3.由于新增了其他View而需要改变位置的动画;

      4.由于移除了其他View而需要改变位置的动画。

      (如果增加或移除了其他View之后,当前View的位置不需要改变,则无动画)。

      你可以自定义这些动画,通过setAnimator() 方法把它们设置进一个 LayoutTransition 对象中去。

      设置的时候需要一个 Animator 对象和一个常数:

      APPEARING - A flag indicating the animation that runs on items that are appearing in the container.

      CHANGE_APPEARING - A flag indicating the animation that runs on items that are changing due to a new item appearing in the container.

      DISAPPEARING - A flag indicating the animation that runs on items that are disappearing from the container.

      CHANGE_DISAPPEARING - A flag indicating the animation that runs on items that are changing due to an item disappearing from the container.

      你可以自己定义这四种事件类型的动画,也可以使用默认的动画。

      最后通过setLayoutTransition(LayoutTransition)方法把这些动画以一个 LayoutTransition 对象的形式设置给一个ViewGroup即可。

      比如下面这个方法就生成了一个全新的LayoutTransition对象并set给容器(ViewGroup类型),这样四个动画就全是默认动画。

        // 重新生成LayoutTransition对象并设置给container
        private void resetTransition() {
            mTransitioner = new LayoutTransition();
            container.setLayoutTransition(mTransitioner);
        }

      为这个mTransitioner对象生成四个自定义动画:

     
        // 生成自定义动画
        private void setupCustomAnimations() {
            // 动画:CHANGE_APPEARING
            // Changing while Adding
            PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 1);
            PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 1);
            PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", 0,
                    1);
            PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom",
                    0, 1);
            PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofFloat("scaleX",
                    1f, 0f, 1f);
            PropertyValuesHolder pvhScaleY = PropertyValuesHolder.ofFloat("scaleY",
                    1f, 0f, 1f);
    
            final ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder(
                    this, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScaleX,
                    pvhScaleY).setDuration(
                    mTransitioner.getDuration(LayoutTransition.CHANGE_APPEARING));
            mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);
            changeIn.addListener(new AnimatorListenerAdapter() {
                public void onAnimationEnd(Animator anim) {
                    View view = (View) ((ObjectAnimator) anim).getTarget();
                    view.setScaleX(1f);
                    view.setScaleY(1f);
                }
            });
    
            // 动画:CHANGE_DISAPPEARING
            // Changing while Removing
            Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
            Keyframe kf1 = Keyframe.ofFloat(.9999f, 360f);
            Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
            PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe(
                    "rotation", kf0, kf1, kf2);
            final ObjectAnimator changeOut = ObjectAnimator
                    .ofPropertyValuesHolder(this, pvhLeft, pvhTop, pvhRight,
                            pvhBottom, pvhRotation)
                    .setDuration(
                            mTransitioner
                                    .getDuration(LayoutTransition.CHANGE_DISAPPEARING));
            mTransitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING,
                    changeOut);
            changeOut.addListener(new AnimatorListenerAdapter() {
                public void onAnimationEnd(Animator anim) {
                    View view = (View) ((ObjectAnimator) anim).getTarget();
                    view.setRotation(0f);
                }
            });
    
            // 动画:APPEARING
            // Adding
            ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 90f,
                    0f).setDuration(
                    mTransitioner.getDuration(LayoutTransition.APPEARING));
            mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);
            animIn.addListener(new AnimatorListenerAdapter() {
                public void onAnimationEnd(Animator anim) {
                    View view = (View) ((ObjectAnimator) anim).getTarget();
                    view.setRotationY(0f);
                }
            });
    
            // 动画:DISAPPEARING
            // Removing
            ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotationX", 0f,
                    90f).setDuration(
                    mTransitioner.getDuration(LayoutTransition.DISAPPEARING));
            mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animOut);
            animOut.addListener(new AnimatorListenerAdapter() {
                public void onAnimationEnd(Animator anim) {
                    View view = (View) ((ObjectAnimator) anim).getTarget();
                    view.setRotationX(0f);
                }
            });
    
        }
     

    默认的布局转换动画

      如果你要使用默认的动画,一个非常简单的方式是在ViewGroup的XML布局文件中把android:animateLayoutchanges 属性设置为true。

      这样就自动地按照默认方式来对要移除或添加的View,还有Group中的其他View进行动画。

      比如ApiDemos中的LayoutAnimationsByDefault:

     
    <?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" >
    
        <Button
            android:id="@+id/addNewButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Add Button" />
        <!--
        <GridLayout
            android:columnCount="4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/gridContainer"
            android:animateLayoutChanges="true"
            />
        -->
    
        <LinearLayout
            android:id="@+id/gridContainer"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:animateLayoutChanges="true" 
            android:orientation="vertical">
        </LinearLayout>
    
    </LinearLayout>
     

      

      

      我把布局改成了线性布局,只要是ViewGroup类型都可以。

      默认情况下,DISAPPEARING和CHANGE_APPEARING动画是立即开始的,其他动画都有一个默认的开始延迟。

      这是因为,比如:当一个新的View出现的时候,其他View要立即执行CHANGE_APPEARING动画腾出位置,而新出现的View在一定延迟之后再执行APPEARING出现;

      相反地,一个View消失的时候,它需要先DISAPPEARING动画消失,而其他的View需要先等它消失后再执行CHANGE_DISAPPEARING。

      当然这些默认的行为都可以通过 setDuration(int, long) 和setStartDelay(int, long)等方法改变。

    API Demos代码

      ApiDemos中布局动画相关的类有:LayoutAnimationsByDefault 、LayoutAnimations、LayoutAnimationsHideShow。

      完整的项目可以去github下载。https://github.com/mengdd/AnimationApiDemos

     

    参考资料

      API Guides: Property Animation

      http://developer.android.com/guide/topics/graphics/prop-animation.html

      其中的Animating Layout Changes to ViewGroups

      LayoutTransition类Reference:

      http://developer.android.com/reference/android/animation/LayoutTransition.html

      项目地址:

      https://github.com/mengdd/AnimationApiDemos

  • 相关阅读:
    命名之法 —— 男名、女名、家族(古诗词与古典名著)
    findContours函数参数详解
    推理集 —— 特殊的时间
    推理集 —— 特殊的时间
    分蛋糕问题 —— 9 个烧饼分给 10 个人
    分蛋糕问题 —— 9 个烧饼分给 10 个人
    辩论之术
    辩论之术
    findContours 轮廓查找
    坚持是一种品行
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/4384212.html
Copyright © 2011-2022 走看看