Android Animation(帧动画)原理个人见解

Android 动画分三大类

  • Frame Animation (帧动画)
  • Tween Animation (补间动画)
  • Property Animator (属性动画) 3.0之后

同时,复杂程度,也是从简到繁,由易到难,今天先说帧动画。

帧动画的使用方式网络上很多很多,也很简单,就不多扯淡,主要说下原理。

原理引入

 ImageView iv_anima = (ImageView) findViewById(R.id.iv);
 AnimationDrawable ad =(AnimationDrawable) iv_anima.getBackground();
 ad.start();

在使用帧动画的时候,会给 ImageView 设置 backgroud,并将backgroud 的drawable 对象 转化为AnimationDrawable 对象,然后用此对象去启动动画。那么问题来了,我们去看看这个牛逼的类

AnimationDrawable :

AnimationDrawable 是创建帧动画的,定义一系列的drawable对象,并且以XML文件的形式放到res/drawable/folder文件夹下,同时将此drawable对象设置到view的backgroud属性.

AnimationDrawable重要属性

  • oneshot 动画播放次数,ture表示只播放一次,false表示循环播放
  • duration 动画播放时间(每帧)

AnimationDrawable如何将对象转换为动画的

  /**
     * Create a drawable from an XML document. For more information on how to
     * create resources in XML, see
     * "{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
     */
    public static Drawable createFromXml(Resources r, XmlPullParser parser)
            throws XmlPullParserException, IOException {
        return createFromXml(r, parser, null);
    }

由上面的源码得知,createFromXml()方法的使用,将xml文件转化了,转换为什么?接着看源码

  public static Drawable createFromXml(Resources r, XmlPullParser parser, Theme theme)
            throws XmlPullParserException, IOException {
        AttributeSet attrs = Xml.asAttributeSet(parser);

        int type;
        while ((type=parser.next()) != XmlPullParser.START_TAG &&
                type != XmlPullParser.END_DOCUMENT) {
            // Empty loop
        }

        if (type != XmlPullParser.START_TAG) {
            throw new XmlPullParserException("No start tag found");
        }

        Drawable drawable = createFromXmlInner(r, parser, attrs, theme);

        if (drawable == null) {
            throw new RuntimeException("Unknown initial tag: " + parser.getName());
        }

        return drawable;
    }
public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs,
            Theme theme) throws XmlPullParserException, IOException {
        final Drawable drawable;

        final String name = parser.getName();
        switch (name) {
            case "selector":
                drawable = new StateListDrawable();
                break;
            case "animated-selector":
                drawable = new AnimatedStateListDrawable();
                break;
            case "level-list":
                drawable = new LevelListDrawable();
                break;
            case "layer-list":
                drawable = new LayerDrawable();
                break;
            case "transition":
                drawable = new TransitionDrawable();
                break;
            case "ripple":
                drawable = new RippleDrawable();
                break;
            case "color":
                drawable = new ColorDrawable();
                break;
            case "shape":
                drawable = new GradientDrawable();
                break;
            case "vector":
                drawable = new VectorDrawable();
                break;
            case "animated-vector":
                drawable = new AnimatedVectorDrawable();
                break;
            case "scale":
                drawable = new ScaleDrawable();
                break;
            case "clip":
                drawable = new ClipDrawable();
                break;
            case "rotate":
                drawable = new RotateDrawable();
                break;
            case "animated-rotate":
                drawable = new AnimatedRotateDrawable();
                break;
            case "animation-list":
                drawable = new AnimationDrawable();
                break;
            case "inset":
                drawable = new InsetDrawable();
                break;
            case "bitmap":
                drawable = new BitmapDrawable();
                break;
            case "nine-patch":
                drawable = new NinePatchDrawable();
                break;
            default:
                throw new XmlPullParserException(parser.getPositionDescription() +
                        ": invalid drawable tag " + name);

        }
        drawable.inflate(r, parser, attrs, theme);
        return drawable;
    }

很明显,直接将xml文件经过XmlPullParser解析转换成了drawable对象,createFromXmlInner()方法中,根据不同的标签名称去生成不同的drawable,最后去调用inflate方法。

到此,我们已经基本理清了从XML文件到Drawable对象的转换流程。对帧动画的工作原理有了一个小了解,这样,也许会让我们对帧动画的使用更加的熟练。那么问题来了,帧动画animation-list节点下的节点是如何形成一个个帧动画效果的?我们继续往下看:

private final static class AnimationState extends DrawableContainerState
private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
            Theme theme) throws XmlPullParserException, IOException {
        int type;

        final int innerDepth = parser.getDepth()+1;
        int depth;
        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
                && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }

            if (depth > innerDepth || !parser.getName().equals("item")) {
                continue;
            }

            final TypedArray a = obtainAttributes(r, theme, attrs,
                    R.styleable.AnimationDrawableItem);

            final int duration = a.getInt(R.styleable.AnimationDrawableItem_duration, -1);
            if (duration < 0) {
                throw new XmlPullParserException(parser.getPositionDescription()
                        + ":  tag requires a 'duration' attribute");
            }

            Drawable dr = a.getDrawable(R.styleable.AnimationDrawableItem_drawable);

            a.recycle();

            if (dr == null) {
                while ((type=parser.next()) == XmlPullParser.TEXT) {
                    // Empty
                }
                if (type != XmlPullParser.START_TAG) {
                    throw new XmlPullParserException(parser.getPositionDescription()
                            + ":  tag requires a 'drawable' attribute or child tag"
                            + " defining a drawable");
                }
                dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
            }

            mAnimationState.addFrame(dr, duration);
            if (dr != null) {
                dr.setCallback(this);
            }
        }
    }

上面的代码中我们可以看出, AnimationState 是继承 DrawableContainerState 的,在DrawableContainerState这个类中有一个成员变量Drawable[] mDrawables;用于保存drawable信息。

inflateChildElements 这方法里面通过TypeArray获取drawable的信息,然后调用AnimationState的addFrame方法,将一系列动画信息就存储在drawable数组中。

    public void start() {
        mAnimating = true;

        if (!isRunning()) {
            // Start from 0th frame.
            setFrame(0, false, mAnimationState.getChildCount() > 1
                    || !mAnimationState.mOneShot);
        }
    }

通过setFrame方法设置我们的drawable。接着就可以进行drawable的展示了。

帧动画的大概原理就是这样,个人见解,希望能帮到其他人。

你可能感兴趣的:(Android基础学习)