详细讲解Android中的动画Animation(依据Android源码目录结构讲解)

       Android中Animation如果仔细的整理一下,是一个非常值得学习的模块。由于其中涉及的内容比较多,大家经常在实际的开发的过程中,只是略微的攫取其中的部分进行运用,比较零碎,下面我就对照着源码中的Animation的实际代码结构,对Animation的实际体系进行讲解一下,希望对大家理解Andorid中的Animation的设计理念有一定的理解。

      第一个类:AnimationThread

      顾名思义,这个类的本质是一个线程。我们主要关注下面几点:

1、其内部有一个静态的内部类,MessageBundle,这个类实际上是封装了当前线程执行的对象的单个的实体。一系列的这些实体将会被存储在一个优先级队列。排列的顺序依据的是任务执行的先后顺序。

     相信大家更加关注的是这个线程的run方法吧。一起来看一下run方法中的核心的代码块:

try {
	if (mListener.isCanceled()) {
		break;
	}
	bundle.mTarget.handleMessage(bundle.mMessage);
	if (mSession.render(false /* freshRender */).isSuccess()) {
	mListener.onNewFrame(session);
	}
} finally {
  mSession.release();
}

而且上面的代码库实际上是在while循环中执行的,直到优先级队列中没有一个元素的时候为止。

     第二个类:Animator

按照google文档上面的注释,这个类是为动画提供基本的Api支持的类的父类,比如开始、结束以及动画的事件的回调等等。

     第三个类:AnimatorInflater

顾名思义,正如文档上面所做的注释那样,当前的类是用来初始化从动画的xml文件中加载出来的动画的对象。

在这个类中,我们会看到下面的常量:

private static final int TOGETHER = 0;
private static final int SEQUENTIALLY = 1;

第一个常量表示的是解析出来的动画集合是集体进行动画,也就是没有先后顺序的。

第二个常量表示的是解析出来的动画集合是按照顺序一个接着一个进行动画。

我们可以从下面的代码块体会到这两个变量的含义:

if (parent != null && childAnims != null) {
    Animator[] animsArray = new Animator[childAnims.size()];
    int index = 0;
    for (Animator a : childAnims) {
        animsArray[index++] = a;
    }
    if (sequenceOrdering == TOGETHER) {
       parent.playTogether(animsArray);
    } else {
       parent.playSequentially(animsArray);
    }
}
    第四个类:AnimatorListenerAdapter

从类名,我们也就可以大致的明白,这个类是动画集合行为接口的适配器。当然了,这是一个抽象类,没有任何实现。

   第五个类:AnimatorSet

这个类在整个的动画的模块总算是极其复杂的了,无论是其内部处理的逻辑,还是它的整体的架构。下面我给大家简单的讲解一下:

1、这个类的功能作用是按照指定的顺序播放一整套的对象。动画播放的顺序可以是集体动画、序列动画或者是有一个指定的延迟。

2、大家关注一下,在其内部有这么几个集合变量:

 
private ArrayList<Animator> mPlayingSet = new ArrayList<Animator>();
private HashMap<Animator, Node> mNodeMap = new HashMap<Animator, Node>();
private ArrayList<Node> mNodes = new ArrayList<Node>();
private ArrayList<Node> mSortedNodes = new ArrayList<Node>();

第一个数据结构追踪当前的播放的动画的节点的集合。

第二个数据结构包含所有的Node的节点,映射到他们各自的动画的实体上。当其中某一个的动画需要产生一个依赖,当当前映射的Node的对象是存在的,那么就不需要重新的创建对象。

第三个数据结构是无序Node节点。是为AnimatorSet创建的Node的集合。

第四个数据结构是顺序Node节点。


值得大家注意的点是:虽然这里的方法虽然多,但是万变不离其宗,基本上大多数的操作都是离不开Node这个对象。无论是播放、排序,还是依赖关系的增加。

我们一下来看一下Node的数据结构的定义:

    private static class Node implements Cloneable {
        public Animator animation;
        public ArrayList<Dependency> dependencies = null;
        public ArrayList<Dependency> tmpDependencies = null;
        public ArrayList<Node> nodeDependencies = null;

        public ArrayList<Node> nodeDependents = null;

        public boolean done = false;

        public Node(Animator animation) {
            this.animation = animation;
        }

        public void addDependency(Dependency dependency) {
            if (dependencies == null) {
                dependencies = new ArrayList<Dependency>();
                nodeDependencies = new ArrayList<Node>();
            }
            dependencies.add(dependency);
            if (!nodeDependencies.contains(dependency.node)) {
                nodeDependencies.add(dependency.node);
            }
            Node dependencyNode = dependency.node;
            if (dependencyNode.nodeDependents == null) {
                dependencyNode.nodeDependents = new ArrayList<Node>();
            }
            dependencyNode.nodeDependents.add(this);
        }

        @Override
        public Node clone() {
            try {
                Node node = (Node) super.clone();
                node.animation = (Animator) animation.clone();
                return node;
            } catch (CloneNotSupportedException e) {
               throw new AssertionError();
            }
        }
    }

1、从上面德类成员变量可以看出,主要关联的是两个方面,动画Animator与依赖关系Dependency。

2、主要关注一下,Node节点是如何产生依赖的。当一个依赖作为参数传递进来的时候,首先将其添加到一个临时的集合列表中去,然后处理当前的依赖中的Node节点。

       第六个类:ArgbEvaluator

 这个类,从直观上来讲就是颜色数值的计算程序。

      第七个类:Keyframe

参照Google文档上的说明,这个类的大致意义是:这个类掌握了一个动画的时间上面的数值,被用来定义这个目标在动画的过程中所具有的数值。

其成员变量如下:

float mFraction;
Class mValueType;
private TimeInterpolator mInterpolator = null;
boolean mHasValue = false;
上面的四个成员变量的含义大致是:

mFraction表示的是当前的对象所存储的数值。mValueType表示的是存储的数据的类型。mInterpolator表示的是frame的时间间隔。mHasValue表示的是当前的对象是否存储了数值。

为了便于大家理解,取其中的一个来分析一下:

static class IntKeyframe extends Keyframe {
        int mValue;

        IntKeyframe(float fraction, int value) {
            mFraction = fraction;
            mValue = value;
            mValueType = int.class;
            mHasValue = true;
        }

        IntKeyframe(float fraction) {
            mFraction = fraction;
            mValueType = int.class;
        }

        public int getIntValue() {
            return mValue;
        }

        public Object getValue() {
            return mValue;
        }

        public void setValue(Object value) {
            if (value != null && value.getClass() == Integer.class) {
                mValue = ((Integer)value).intValue();
                mHasValue = true;
            }
        }

        @Override
        public IntKeyframe clone() {
            IntKeyframe kfClone = mHasValue ?
                    new IntKeyframe(getFraction(), mValue) :
                    new IntKeyframe(getFraction());
            kfClone.setInterpolator(getInterpolator());
            return kfClone;
        }
    }
从上面的代码,我们可以看出这么几点:

1、IntKeyframe内部存储的数值mValue肯定是整数的类型。mValueType也肯定是int.class。

2、在setValue的方法中,需要进行一次强制性的类型的转换

3、在克隆当前的对象,根据当前是否已经存储了数值,会选择不同的构造函数。


      第八个类:KeyframeSet

这个类的功能如下:

这个类掌握了一个keyFrame对象的集合,并且会被ValueAnimator调用去计算针对一个指定的ValueAnimation,keyFrame之间的数值。分析类,我们主要来看下面的三点的内容。

1、成员变量:

int mNumKeyframes;
Keyframe mFirstKeyframe;
Keyframe mLastKeyframe;
TimeInterpolator mInterpolator; // only used in the 2-keyframe case
ArrayList<Keyframe> mKeyframes; // only used when there are not 2 keyframes
TypeEvaluator mEvaluator;

对于这些成员变量进行大致的说明一下:

mNumKeyframes表明的是当前的集合中存储的KeyFrame的数目,mFirstKeyframe与mLastKeyFrame分别表明的是第一个KeyFame与最后一个KeyFrame的对象。mInterpolator计算的是KeyFrame与KeyFrame之间的速率。mKeyframes存储的是KeyFrame对象的集合。mEvaluator是类型的计算的应用程序。

2、构造函数

public KeyframeSet(Keyframe... keyframes) {
        mNumKeyframes = keyframes.length;
        mKeyframes = new ArrayList<Keyframe>();
        mKeyframes.addAll(Arrays.asList(keyframes));
        mFirstKeyframe = mKeyframes.get(0);
        mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
        mInterpolator = mLastKeyframe.getInterpolator();
    }

主要是成员变量的赋值。

3、选取就一种类型处理的函数来说明:

public static KeyframeSet ofInt(int... values) {
        int numKeyframes = values.length;
        IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
        if (numKeyframes == 1) {
            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
            keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
        } else {
            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
                keyframes[i] = (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
            }
        }
        return new IntKeyframeSet(keyframes);
    }

3、1 获取当前的int类型的数目的长度

3、2 构建IntKeyframe的数组,数组的长度就是上面计算的长度

3、3 调用KeyFrame的内部的构造函数,通过每一个value,来构建KeyFrame的对象。


         第九个类:ValueAnimator

与上面一样,先是进行功能上的说明。

按照google上面的文档:

这个类提供了一个简单的时间上面的引擎针对的是运行的动画,计算出于动画中的数值,并且将它们设置到了指定的Target上面。


Ok,Animation的分析到这里就结束了,希望大家能够从中加深对Animation的理解。

       









郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。