LayoutAnimationController源码深入学习

一、概述

LayoutAnimationController用于一个布局或者ViewGroup的动画控制器,它能够让每个子View在不同的时间点分别执行相同的动画,ViewGroup通过LayoutAnimationController计算每个子view的动画开始的时间间隔,这个时间的间隔是通过某种规律特点进行计算,比如它在viewGroup中的index顺序。

间隔时间的计算是通过view在viewgroup的index顺序乘以单位时间间隔计算得出。它的子类应该重写:

  • getDelayForView(android.view.View):实现一种不同的计算delay的延迟时间方式。例如子类GridLayoutAnimationController,它基于child view的column和row值来计算延迟。

用于计算每个子view的动画延迟信息被存储在LayoutAnimationController.AnimationParameters中。XML文件配置通常有一下几个参数:

  • LayoutAnimation_delay:延迟时间
  • LayoutAnimation_animationOrder:动画播放顺序
  • LayoutAnimation_interpolator:动画的差值,有加速,减速等
  • LayoutAnimation_animation:动画的名称
二、动画播放顺序

LayoutAnimationController类中对应的播放顺序:

  • public static final int ORDER_NORMAL = 0:按照view添加到view group中的顺序进行播放动画展示
  • public static final int ORDER_REVERSE = 1;按照view添加到view group中倒叙进行播放动画展示。
  • public static final int ORDER_RANDOM = 2;随机顺序播放view的动画。
三、源代码简要分析

我们看下另外几个成员变量:

    public class LayoutAnimationController {
        public static final int ORDER_NORMAL  = 0;

        public static final int ORDER_REVERSE = 1;

        public static final int ORDER_RANDOM  = 2;
        //view展示的Animation动画
        protected Animation mAnimation;
        //当播放顺序为随机的时候,用于计算播放顺序
        protected Random mRandomizer;
        //Animation动画的插值器
        protected Interpolator mInterpolator;
        //动画的间隔时间
        private float mDelay;
        //动画的播放顺序
        private int mOrder;
        //动画的持续时间
        private long mDuration;
        //最大的间隔时间
        private long mMaxDelay;    
    ****
    }

我们如何构建一个LayoutAnimationController呢?看下它的构造函数。

  • public LayoutAnimationController(Context context, AttributeSet attrs)
  • public LayoutAnimationController(Animation animation)
  • public LayoutAnimationController(Animation animation, float delay)

我们看下构造函数的实现:
1、

        public LayoutAnimationController(Context context, AttributeSet attrs) {
            TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LayoutAnimation);

            Animation.Description d = Animation.Description.parseValue(
                    a.peekValue(com.android.internal.R.styleable.LayoutAnimation_delay));
            mDelay = d.value;

            mOrder = a.getInt(com.android.internal.R.styleable.LayoutAnimation_animationOrder, ORDER_NORMAL);

            int resource = a.getResourceId(com.android.internal.R.styleable.LayoutAnimation_animation, 0);
            if (resource > 0) {
                setAnimation(context, resource);
            }

            resource = a.getResourceId(com.android.internal.R.styleable.LayoutAnimation_interpolator, 0);
            if (resource > 0) {
                setInterpolator(context, resource);
            }

            a.recycle();
    }

通过上面的代码发现,构造函数中就是类似于我们的自定义View使用,获取attrs中所定义的属性值,然后赋值到对应的成员变量上。

2、

    public LayoutAnimationController(Animation animation) {
        this(animation, 0.5f);
    }

    public LayoutAnimationController(Animation animation, float delay) {
        mDelay = delay;
        setAnimation(animation);
    }

这两个构造函数中,传入Animation动画,同时设置的默认事件,延迟50%。

LayoutAnimationController中给我们提供了很多函数,同时提供了一些设置成员变量值的函数。我们挑一些来介绍:

1、判断两个view之间的动画是否具有重叠,重叠返回true,不重叠返回false

    public boolean willOverlap() {
        return mDelay < 1.0f;
    }

2、启动动画:

    public void start() {
        mDuration = mAnimation.getDuration();
        mMaxDelay = Long.MIN_VALUE;
        mAnimation.setStartTime(-1);
    }

3、获取应用在view上的Animation动画对象:

    public final Animation getAnimationForView(View view) {
        final long delay = getDelayForView(view) + mAnimation.getStartOffset();
        mMaxDelay = Math.max(mMaxDelay, delay);

        try {
            final Animation animation = mAnimation.clone();
            animation.setStartOffset(delay);
            return animation;
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }

4、判断动画是否完成:所有子view完成动画返回true,否则返回false

    public boolean isDone() {
        return AnimationUtils.currentAnimationTimeMillis() >
                mAnimation.getStartTime() + mMaxDelay + mDuration;
    }

5、获取view的动画延迟数:

    /**
         * 返回view的动画延迟毫秒数,动画一定会延迟,子类需要重写该方法,来
         * 计算返回一个数
         * child animation delay = child index * delay
         *
         * index序列从ViewGroup.LayoutParams的AnimationParameters中获取
         */
        protected long getDelayForView(View view) {
            //获取view的LayoutParams
            ViewGroup.LayoutParams lp = view.getLayoutParams();
            //在LayoutParams属性中获取view的AnimationParameters
            AnimationParameters params = lp.layoutAnimationParameters;
            //判断AnimationParameters是否为空
            if (params == null) {
                return 0;
            }
            //计算动画的延迟时间delay * duration
            final float delay = mDelay * mAnimation.getDuration();
            //计算view的延迟时间index * delay
            final long viewDelay = (long) (getTransformedIndex(params) * delay);
            //view的全部延迟时间delay * count
            final float totalDelay = delay * params.count;
            //动画的插值器是否为空
            if (mInterpolator == null) {
                mInterpolator = new LinearInterpolator();
            }

            float normalizedDelay = viewDelay / totalDelay;
            normalizedDelay = mInterpolator.getInterpolation(normalizedDelay);

            return (long) (normalizedDelay * totalDelay);
        }

6、获取变换的index顺序

    /**
         * 变换的index值存储在AnimationParameters对象中
         * 子类需要重写这个方法来提供额外的不同顺序。
         * 该方法被getDelayForView(android.view.View)调用
         */
        protected int getTransformedIndex(AnimationParameters params) {
            switch (getOrder()) {
                case ORDER_REVERSE:
                    return params.count - 1 - params.index;
                case ORDER_RANDOM:
                    if (mRandomizer == null) {
                        mRandomizer = new Random();
                    }
                    return (int) (params.count * mRandomizer.nextFloat());
                case ORDER_NORMAL:
                default:
                    return params.index;
            }
        }

主要的几个方法介绍完了,我们看下AnimationParameters这个内部类。

 public static class AnimationParameters {
        //view group中的view个数
        public int count;

        //即将展示view的index序列
        public int index;
    }

针对上面的几个主要方法的源码分析,我们知道如果我们自定义一个LayoutAnimationController,需要我们控制顺序,控制顺序就需要我们重写getTransformedIndex()方法,我们通过AnimationParameters类中的属性进行index的计算。然后由getDelayForView()方法计算每个view的延迟时间。

四、GridLayoutAnimationController案例分析

仔细看了上面的介绍,我们粗略的说了GridLayoutAnimationController是通过row和column两个值来进行计算各个view之间的延迟时间。一般我们自定义LayoutAnimationController的时候需要我们重写getTransformedIndex()方法来确定我们的view展现的顺序,然后getDelayForView()函数进行计算delay的时间,如有需要我们也可以重写。所以针对分析,我们仅仅看看GridLayoutAnimationController中的这两个方法的实现,其它的方法基本与LayoutAnimationController的类似。

先来观摩下getDelayForView()的源码吧!

    protected long getDelayForView(View view) {
        //获取LayoutParams
        ViewGroup.LayoutParams lp = view.getLayoutParams();
        //获取AnimationParameters
        AnimationParameters params = (AnimationParameters) lp.layoutAnimationParameters;

        if (params == null) {
            return 0;
        }
        //获取column的index
        final int column = getTransformedColumnIndex(params);
        //获取row的index
        final int row = getTransformedRowIndex(params);
        //获取column的个数和row的个数
        final int rowsCount = params.rowsCount;
        final int columnsCount = params.columnsCount;
        //计算列、行的延迟数
        final long duration = mAnimation.getDuration();
        final float columnDelay = mColumnDelay * duration;
        final float rowDelay = mRowDelay * duration;

        float totalDelay;
        long viewDelay;

        if (mInterpolator == null) {
            mInterpolator = new LinearInterpolator();
        }
        //判断动画的展示方向
        switch (mDirectionPriority) {
            case PRIORITY_COLUMN://列优先
                viewDelay = (long) (row * rowDelay + column * rowsCount * rowDelay);
                totalDelay = rowsCount * rowDelay + columnsCount * rowsCount * rowDelay;
                break;
            case PRIORITY_ROW://行优先
                viewDelay = (long) (column * columnDelay + row * columnsCount * columnDelay);
                totalDelay = columnsCount * columnDelay + rowsCount * columnsCount * columnDelay;
                break;
            case PRIORITY_NONE:
            default:
                viewDelay = (long) (column * columnDelay + row * rowDelay);
                totalDelay = columnsCount * columnDelay + rowsCount * rowDelay;
                break;
        }
        //计算延迟时间
        float normalizedDelay = viewDelay / totalDelay;
        normalizedDelay = mInterpolator.getInterpolation(normalizedDelay);

        return (long) (normalizedDelay * totalDelay);
    }

上面的注释很清晰了,代码也不是很难,在上面代码中通过getTransformedColumnIndex()、getTransformedRowIndex()方法来获取列和行的index值。我们看下这两个函数的源代码。

    private int getTransformedColumnIndex(AnimationParameters params) {
        int index;
        switch (getOrder()) {//获取动画执行的顺序
            case ORDER_REVERSE:
                index = params.columnsCount - 1 - params.column;
                break;
            case ORDER_RANDOM:
                if (mRandomizer == null) {
                    mRandomizer = new Random();
                }
                index = (int) (params.columnsCount * mRandomizer.nextFloat());
                break;
            case ORDER_NORMAL:
            default:
                index = params.column;
                break;
        }

        int direction = mDirection & DIRECTION_HORIZONTAL_MASK;
        if (direction == DIRECTION_RIGHT_TO_LEFT) {
            index = params.columnsCount - 1 - index;
        }

        return index;
    }

    private int getTransformedRowIndex(AnimationParameters params) {
        int index;
        switch (getOrder()) {
            case ORDER_REVERSE:
                index = params.rowsCount - 1 - params.row;
                break;
            case ORDER_RANDOM:
                if (mRandomizer == null) {
                    mRandomizer = new Random();
                }
                index = (int) (params.rowsCount * mRandomizer.nextFloat());
                break;
            case ORDER_NORMAL:
            default:
                index = params.row;
                break;
        }

        int direction = mDirection & DIRECTION_VERTICAL_MASK;
        if (direction == DIRECTION_BOTTOM_TO_TOP) {
            index = params.rowsCount - 1 - index;
        }

        return index;
    }

这里面也是计算index,同时又定义了一个新的AnimationParameters内部类。

 public static class AnimationParameters extends
            LayoutAnimationController.AnimationParameters {
        /**
         * The view group's column to which the view belongs.
         */
        public int column;

        /**
         * The view group's row to which the view belongs.
         */
        public int row;

        /**
         * The number of columns in the view's enclosing grid layout.
         */
        public int columnsCount;

        /**
         * The number of rows in the view's enclosing grid layout.
         */
        public int rowsCount;
    }

基本的实现就是这个样子。在以后的开发中我们也可以结合实际的项目需要,进行LayoutAnimationController的自定义实现。下一篇博客中,实现几个案例来进行练手使用。LayoutAnimationController实战案例

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值