Android 动画之LayoutTransition

Android动画系列:

  • 补间动画详解
  • 帧动画
  • LayoutAnimation
  • LayoutTransition
  • 属性动画 - 基本使用
  • 属性动画 - Interpolator(内插器)
  • 属性动画 - TypeEvaluator
  • 属性动画 - Keyframe
  • AnimatorSet

场景一,当向一个ViewGroup添加控件或者移除控件;场景2,想ListView、GridView或者RecyclerView中添加或者移除数据并更新显示;场景3,通过调用View.setVisibility()设置View的显示或者隐藏。这三种场景虽然能够实现效果,并没有一点过度效果,直来直去的添加或者移除,显得有点生硬。有没有办法添加一定的过度效果,让实现的效果显得圆滑呢?看很多开源框架都已经实现了这些场景的动画效果,动画效果到底是怎么实现的呢?那就不得不说LayoutTransition,接着往下看把。

LayoutTransition

LayoutTransition类实际上Android系统中的一个实用工具类。使用LayoutTransition类在一个ViewGroup中对布局更改进行动画处理。

以场景1为例,向一个ViewGroup添加控件或者移除控件,这两种效果的过段应对应于控件的显示、控件添加时其他控件的位置移动、控件的消失、控件移除时其他控件的位置移动等四种动画效果。这些动画效果在LayoutTransition中,由以下四个关键字做出了相关声明,

  • APPEARIN:元素在容器中显现时需要动画显示。
  • CHANGE_APPEARING:由于容器中要显现一个新的元素,其它元素的变化需要动画显示。
  • DISAPPEARING:元素在容器中消失时需要动画显示。
  • CHANGE_DISAPPEARING:由于容器中某个元素要消失,其它元素的变化需要动画显示。

也就是说,我们ViewGroup中有多个Button对象,如果需要删除其中一个Button对象的话,该Button对象可以设置动画(即DISAPPEARING 动画形式),ViewGroup中的其它Button对象此时移动到新的位置的过程中也可以设置相关的动画(即CHANGE_DISAPPEARING 动画形式);若向ViewGroup中添加一个Button,Button对象可以设置动画(即APPEARIN 动画形式),ViewGroup中的其它Button对象此时移动到新的位置的过程中也可以设置相关的动画(即CHANGE_APPEARING 动画形式)。

既然了解到了LayoutTransition的与ViewGroup联动四种动画形式,该如何使用LayoutTransition,老惯例,先了解下其常用的API。

常用API

关键字常量

  • APPEARIN:元素在容器中显现时需要动画显示。
  • CHANGE_APPEARING:由于容器中要显现一个新的元素,其它元素的变化需要动画显示。
  • DISAPPEARING:元素在容器中消失时需要动画显示。
  • CHANGE_DISAPPEARING:由于容器中某个元素要消失,其它元素的变化需要动画显示。
  • CHANGING:当元素本身某个属性发生变化,但元素并没有添加和移除时需要动画的显示

常用方法

  • addChild(ViewGroup parent, View child):将child添加至parent

  • addTransitionListener(LayoutTransition.TransitionListener):添加Layout变化监听

  • removeTransitionListener(LayoutTransition.TransitionListener listener):移除Layout变化监听
  • setAnimator(int transitionType, Animator animator):为转换类型transitionType设置动画
  • setDuration(int transitionType, long duration):为转换类型transitionType设置动画执行时间
  • setDuration(long duration):设置整个Layout变换动画时间
  • setInterpolator(int transitionType, TimeInterpolator interpolator):为转换类型transitionType设置动画内插器
  • setStagger(int transitionType, long duration):设置开始类型transitionType与上一个类型的间隔时间
  • setStartDelay(int transitionType, long delay):设置开始类型transitionType的动画执行的延迟时间

  • hideChild(ViewGroup parent, View child, int newVisibility):当子视图即将在容器中不可见时,ViewGroup调用此方法。

  • showChild(ViewGroup parent, View child, int oldVisibility):当子视图即将在容器中可见时,ViewGroup调用此方法。

LayoutTransition.TransitionListener

LayoutTransition.TransitionListener用于监听LayoutTransition的开始和结束,显而意见其有两个回调.

  • endTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType):LayoutTransition某一类型动画结束
  • startTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType):LayoutTransition某一类型动画开始

示例代码

系统默认LayoutTransition

在系统中,为ViewGroup设置了默认的LayoutTransition,只需要在XML中将设置android:animateLayoutChanges=”true”即可,true代表当前Layout的布局发生改变时使用动画效果,false则不使用

  1. XML定义

自定义LayoutTransition

1.XML定义一个GridLayout



    

        

2.核心代码
public class LayoutTransitionActivity extends AppCompatActivity {

    private void doAdd() {
        Button button = new Button(this);
        button.setText("Button- " + mCount);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        button.setLayoutParams(params);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                glContent.removeView(v);
            }
        });
        if (glContent.getChildCount() > 1) {
            glContent.addView(button, 1);
        } else {
            glContent.addView(button);
        }
        mCount++;

    }

    private void doRemove() {
        if (glContent.getChildCount() > 1) {
            glContent.removeView(glContent.getChildAt(1));
        } else {
            glContent.removeView(glContent.getChildAt(0));
        }
    }

    private void doRemoveAll() {
        if (glContent.getChildCount() > 0) {
            glContent.removeAllViews();
        }
    }


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

    // 生成自定义动画
    private void setupCustomAnimations() {

        // 控制ViewGroup其他自View的移动(除去添加或者移除的子View)
        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);

        // 动画:CHANGE_APPEARING
        // Changing while Adding
        PropertyValuesHolder pvhTransX = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_TRANSLATION_X,
                1f, 0f, 1f);
        PropertyValuesHolder pvhPivotX = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_PIVOT_X, 0.5f);
        PropertyValuesHolder pvhPivotY = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_PIVOT_Y, 0.5f);
        PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_SCALE_X,1f, 0f,
                1f);
        PropertyValuesHolder pvhScaleY = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_SCALE_Y,1f, 0f,
                1f);


        final ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder(
                this, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhTransX,pvhScaleX,
                pvhScaleY, pvhPivotX, pvhPivotY).setDuration(
                mTransition.getDuration(LayoutTransition.CHANGE_APPEARING));
        mTransition.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(0.5f, 2f);
        Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
        PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe(
                PropertyConstant.PROPERTY_SCALE_X, kf0, kf1, kf2);
        final ObjectAnimator changeOut = ObjectAnimator
                .ofPropertyValuesHolder(this, pvhLeft, pvhTop, pvhRight,
                        pvhBottom, pvhRotation)
                .setDuration(
                        mTransition
                                .getDuration(LayoutTransition.CHANGE_DISAPPEARING));
        mTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING,
                changeOut);
        changeOut.addListener(new AnimatorListenerAdapter() {
            public void onAnimationEnd(Animator anim) {
                View view = (View) ((ObjectAnimator) anim).getTarget();
                view.setAlpha(1f);
            }
        });

        // 动画:APPEARING
        // view出现时 view自身的动画效果
        PropertyValuesHolder appInScaleX = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_SCALE_X, 0f,
                1f);
        PropertyValuesHolder appInScaleY = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_SCALE_Y, 0f,
                1f);
        PropertyValuesHolder appInPivotX = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_PIVOT_X, 0.5f);
        PropertyValuesHolder appInPivotY = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_PIVOT_Y, 0.5f);
        ObjectAnimator animIn = ObjectAnimator.ofPropertyValuesHolder(this, appInPivotX, appInPivotY,
                appInScaleX, appInScaleY).setDuration(
                mTransition.getDuration(LayoutTransition.APPEARING));
        mTransition.setAnimator(LayoutTransition.APPEARING, animIn);
        animIn.addListener(new AnimatorListenerAdapter() {
            public void onAnimationEnd(Animator anim) {
                View view = (View) ((ObjectAnimator) anim).getTarget();
                view.setRotationY(0f);
            }
        });

        // 动画:DISAPPEARING
        // view 消失时,view自身的动画效果
        ObjectAnimator animOut = ObjectAnimator.ofFloat(this, "rotationX", 0f,
                90f).setDuration(
                mTransition.getDuration(LayoutTransition.DISAPPEARING));
        mTransition.setAnimator(LayoutTransition.DISAPPEARING, animOut);
        animOut.addListener(new AnimatorListenerAdapter() {
            public void onAnimationEnd(Animator anim) {
                View view = (View) ((ObjectAnimator) anim).getTarget();
                view.setRotationX(0f);
            }
        });

    }
}

结束语

LayoutTransition 是API Level 11 才出现的。LayoutTransition的动画效果,只有当ViewGroup中有View添加、删除、隐藏、显示的时候才会体现出来。

参考资料

  1. Android属性动画PropertyAnimation系列三之LayoutTransition(布局容器动画)
  2. Android Animation学习(五) ApiDemos解析:容器布局动画 LayoutTransition

你可能感兴趣的:(Andriod进阶,Android进阶)