Android中的补间动画分为下面几种:
(1)AlphaAnimation :透明度改变的动画。
(2)ScaleAnimation:大小缩放的动画。
(3)TranslateAnimation:位移变化的动画。
(4)RotateAnimation:旋转动画。

然而在实际项目中透明度、缩放、位移、旋转这几种动画并不能满足我们的需求,比如我们需要一个类似下面的3D旋转动画。

这时候就需要用到自定义动画,自定义动画需要继承Animation,并重写applyTransformation(float interpolatedTime, Transformation t)方法和initialize方法。

applyTransformation方法中的两个参数说明:

interpolatedTime: 该参数代表了时间的进行程度(如:你设置的时间是1000ms,
那么interploatedTime就会从0开始一直到1,当该参数为1时表明动画结束)

Transformation:
代表补间动画在不同时刻对图形或组建的变形程度。该对象中封装了一个Matrix对象,对它所包含的Matrix对象进行位移、倾斜、旋转等变换时,Transformation将会控制对应的图片或视图进行相应的变换。

initialize(int width, int height, int parentWidth, int parentHeight)函数,这是一个回调函数告诉Animation目标View的大小参数,在这里可以初始化一些相关的参数,例如设置动画持续时间、设置Interpolator、设置动画的参考点等。

为了控制图片或View进行三维空间的变换,还需要借助于Android提供的一个Camera类,该类是一个空间变换工具,作用有点类似于Matrix,提供了如下常用的方法。

getMatrix(Matrix matrix) :将Camera所做的变换应用到指定的maxtrix上
rotateX(float deg):将目标组件沿X轴旋转
rotateY(float deg)、
rotateZ(float deg)
translate(float x, float y, float z):把目标组件在三维空间类进行位移变换。
applyToCanvas(Canvas canvas):把Camera所做的变换应用到Canvas上。

初级应用——代码中创建动画

下面我们先来个简单的实现, 只在activity中创建动画 ,而不使用xml文件的方式来创建动画。
具体实现如下:
自定义rotate3dAnimation 继承自Animation ,并重写applyTransformation(float interpolatedTime, Transformation t)方法。

public class Rotate3dAnimation extends Animation{    // 旋转点类型 默认为 ABSOLUTE    private int mPivotXType = ABSOLUTE;    private int mPivotYType = ABSOLUTE;    private float mPivotXValue = 0.0f;    private float mPivotYValue = 0.0f;    private float mFromDegrees;    private float mToDegrees;    private float mPivotX;    private float mPivotY;    private Camera mCamera;    private int mRollType;    /**     * 旋转轴     */    public static final int ROLL_BY_X = 0;    public static final int ROLL_BY_Y = 1;    public static final int ROLL_BY_Z = 2;    public Rotate3dAnimation(int rollType, float fromDegrees, float toDegrees) {        mRollType = rollType;        mFromDegrees = fromDegrees;        mToDegrees = toDegrees;        mPivotX = 0.0f;        mPivotY = 0.0f;    }    public Rotate3dAnimation(int rollType, float fromDegrees, float toDegrees,            float pivotX, float pivotY) {        mRollType = rollType;        mFromDegrees = fromDegrees;        mToDegrees = toDegrees;        mPivotXType = ABSOLUTE;        mPivotYType = ABSOLUTE;        mPivotXValue = pivotX;        mPivotYValue = pivotY;        initializePivotPoint();    }    public Rotate3dAnimation(int rollType, float fromDegrees, float toDegrees,            int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) {        mRollType = rollType;        mFromDegrees = fromDegrees;        mToDegrees = toDegrees;        mPivotXValue = pivotXValue;        mPivotXType = pivotXType;        mPivotYValue = pivotYValue;        mPivotYType = pivotYType;        initializePivotPoint();    }    private void initializePivotPoint()    {        if (mPivotXType == ABSOLUTE)        {            mPivotX = mPivotXValue;        }        if (mPivotYType == ABSOLUTE)        {            mPivotY = mPivotYValue;        }    }    // Animation类中的初始化方法 有点类似于onMeasure    @Override    public void initialize(int width, int height, int parentWidth,            int parentHeight)    {        super.initialize(width, height, parentWidth, parentHeight);        mCamera = new Camera();        mPivotX = resolveSize(mPivotXType, mPivotXValue, width, parentWidth);        mPivotY = resolveSize(mPivotYType, mPivotYValue, height, parentHeight);    }    @Override    protected void applyTransformation(float interpolatedTime, Transformation t)    {         final float fromDegrees = mFromDegrees;            float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);            final Matrix matrix = t.getMatrix();            mCamera.save();            switch (mRollType) {                case ROLL_BY_X:                //绕X轴旋转                    mCamera.rotateX(degrees);                    break;                case ROLL_BY_Y:                //绕Y轴旋转                    mCamera.rotateY(degrees);                    break;                case ROLL_BY_Z:                //绕Z轴旋转                    mCamera.rotateZ(degrees);                    break;            }            mCamera.getMatrix(matrix);            mCamera.restore();            matrix.preTranslate(-mPivotX, -mPivotY);            matrix.postTranslate(mPivotX, mPivotY);    }}

在activity中的使用方法和 使用ScaleAnimation等动画没什么两样。

activity中的代码如下:

public class MainActivity extends ActionBarActivity{    @Override    protected void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ImageView img = (ImageView) findViewById(R.id.img);        Rotate3dAnimation animation = new Rotate3dAnimation(Rotate3dAnimation.ROLL_BY_X,0f,360f);        animation.setFillAfter(true);        animation.setDuration(1000);        img.startAnimation(animation);    }}

现在我们已经初步掌握了自定义动画类的使用,但是仅在代码中创建动画是不够的,我们很多情况下也需要在xml文件中创建动画。那该怎么办呢?

高级应用——XML创建动画

使用XML创建动画的过程有点类似于自定义控件的使用。

(一) 在attrs.xml文件中设置自定义属性

attrs.xml

<resources>    <declare-styleable name="Rotate3dAnimation">                <attr name="rollType" format="enum">            <enum name="x" value="0"/>            <enum name="y" value="1"/>            <enum name="z" value="2"/>        attr>                   <attr name="fromDeg" format="float" />                 <attr name="toDeg" format="float" />                 <attr name="pivotX" format="fraction"/>        <attr name="pivotY" format="fraction" />    declare-styleable>resources>

(二) 获取自定义属性

下面 我们就需要修改下我们的rotate3dAnimation类,在其中获取xml文件中声明的自定义属性并解析。

Rotate3dAnimation.class

package com.demo.customanimation;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Camera;import android.graphics.Matrix;import android.util.AttributeSet;import android.util.TypedValue;import android.view.animation.Animation;import android.view.animation.Transformation;public class Rotate3dAnimation extends Animation{    // 旋转点类型 默认为 ABSOLUTE    private int mPivotXType = ABSOLUTE;    private int mPivotYType = ABSOLUTE;    private float mPivotXValue = 0.0f;    private float mPivotYValue = 0.0f;    private float mFromDegrees;    private float mToDegrees;    private float mPivotX;    private float mPivotY;    private Camera mCamera;    private int mRollType;    /**     * 旋转轴     */    public static final int ROLL_BY_X = 0;    public static final int ROLL_BY_Y = 1;    public static final int ROLL_BY_Z = 2;//获取并解析自定义属性,  与在自定义控件中的使用相同    public Rotate3dAnimation(Context context, AttributeSet attrs) {        super(context, attrs);        TypedArray a = context.obtainStyledAttributes(attrs,                R.styleable.Rotate3dAnimation);        mFromDegrees = a.getFloat(R.styleable.Rotate3dAnimation_fromDeg, 0.0f);        mToDegrees = a.getFloat(R.styleable.Rotate3dAnimation_toDeg, 0.0f);        mRollType = a.getInt(R.styleable.Rotate3dAnimation_rollType, ROLL_BY_X);        Description d = parseValue(a                .peekValue(R.styleable.Rotate3dAnimation_pivotX));        mPivotXType = d.type;        mPivotXValue = d.value;        d = parseValue(a.peekValue(R.styleable.Rotate3dAnimation_pivotY));        mPivotYType = d.type;        mPivotYValue = d.value;        a.recycle();        // 初始化旋转点        initializePivotPoint();    }    public Rotate3dAnimation(int rollType, float fromDegrees, float toDegrees) {        mRollType = rollType;        mFromDegrees = fromDegrees;        mToDegrees = toDegrees;        mPivotX = 0.0f;        mPivotY = 0.0f;    }    public Rotate3dAnimation(int rollType, float fromDegrees, float toDegrees,            float pivotX, float pivotY) {        mRollType = rollType;        mFromDegrees = fromDegrees;        mToDegrees = toDegrees;        mPivotXType = ABSOLUTE;        mPivotYType = ABSOLUTE;        mPivotXValue = pivotX;        mPivotYValue = pivotY;        initializePivotPoint();    }    public Rotate3dAnimation(int rollType, float fromDegrees, float toDegrees,            int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) {        mRollType = rollType;        mFromDegrees = fromDegrees;        mToDegrees = toDegrees;        mPivotXValue = pivotXValue;        mPivotXType = pivotXType;        mPivotYValue = pivotYValue;        mPivotYType = pivotYType;        initializePivotPoint();    }    private void initializePivotPoint()    {        if (mPivotXType == ABSOLUTE)        {            mPivotX = mPivotXValue;        }        if (mPivotYType == ABSOLUTE)        {            mPivotY = mPivotYValue;        }    }    // Animation类中的初始化方法 有点类似于onMeasure    @Override    public void initialize(int width, int height, int parentWidth,            int parentHeight)    {        super.initialize(width, height, parentWidth, parentHeight);        mCamera = new Camera();        mPivotX = resolveSize(mPivotXType, mPivotXValue, width, parentWidth);        mPivotY = resolveSize(mPivotYType, mPivotYValue, height, parentHeight);    }    protected static class Description    {        public int type;        public float value;    }    Description parseValue(TypedValue value)    {        Description d = new Description();        if (value == null)        {            d.type = ABSOLUTE;            d.value = 0;        } else        {            if (value.type == TypedValue.TYPE_FRACTION)            {                d.type = (value.data & TypedValue.COMPLEX_UNIT_MASK) == TypedValue.COMPLEX_UNIT_FRACTION_PARENT ? RELATIVE_TO_PARENT                        : RELATIVE_TO_SELF;                d.value = TypedValue.complexToFloat(value.data);                return d;            } else if (value.type == TypedValue.TYPE_FLOAT)            {                d.type = ABSOLUTE;                d.value = value.getFloat();                return d;            } else if (value.type >= TypedValue.TYPE_FIRST_INT                    && value.type <= TypedValue.TYPE_LAST_INT)            {                d.type = ABSOLUTE;                d.value = value.data;                return d;            }        }        d.type = ABSOLUTE;        d.value = 0.0f;        return d;    }    @Override    protected void applyTransformation(float interpolatedTime, Transformation t)    {         final float fromDegrees = mFromDegrees;            float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);            final Matrix matrix = t.getMatrix();            mCamera.save();            switch (mRollType) {                case ROLL_BY_X:                    mCamera.rotateX(degrees);                    break;                case ROLL_BY_Y:                    mCamera.rotateY(degrees);                    break;                case ROLL_BY_Z:                    mCamera.rotateZ(degrees);                    break;            }            mCamera.getMatrix(matrix);            mCamera.restore();            matrix.preTranslate(-mPivotX, -mPivotY);            matrix.postTranslate(mPivotX, mPivotY);    }}

最后 ,我们在anim动画文件中使用我们自定义的动画类和属性就好了。
注意,在xml中使用自定义动画类的时候,需要自定义我们的命名空间,在使用动画标签的时候需要加上命名控件:包名。

rotate3d.xml

<set    xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:rotates="http://schemas.android.com/apk/res-auto"     android:interpolator="@android:anim/linear_interpolator"     android:shareInterpolator="true">     com.demo.customanimation.Rotate3dAnimation        rotates:rollType="x"        rotates:fromDeg="100"        rotates:toDeg="0"        rotates:pivotX="50%"       rotates:pivotY="50%"        android:duration="400"/>set>

接下来,在activity中使用AnimationUtil的loadAnimation方法来加载我们的xml动画文件。

public class MainActivity extends ActionBarActivity{    @Override    protected void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ImageView img = (ImageView) findViewById(R.id.img);        Animation animation = AnimationUtils.loadAnimation(this, R.anim.rotate3d);        animation.setFillAfter(true);        animation.setDuration(1000);        img.startAnimation(animation);    }}

好了,“大功告成”(真的这样么。。)! 运行一下试试!!!

哎? 怎么回事? 运行竟然报错了????!!!!

这是什么原因呢?

修改AnimationUtils 源码

通过查看AnimationUtils.loadAnimation源代码我们知道,在其从xml载入动画类的时候,只认alpha、scale、rotate、translate这几个SDK自带的动画类,而我们写入的自定义动画类Rotate3dAnimation会导致其报Unknown animation name的异常。官方SDK也没有提供解决这个问题的其他API方法,那么怎么解决呢? 很简单,只需在原有的AnimationUtils.loadAnimation源码上改动一行,通过java中的反射机制,通过包名从ClassLoader载入自定义动画类即可。将其源码拷贝过来,实现一个自己的loadAnimation方法,如下:

package com.demo.customanimation;import java.io.IOException;import org.xmlpull.v1.XmlPullParser;import org.xmlpull.v1.XmlPullParserException;import android.content.Context;import android.content.res.Resources.NotFoundException;import android.content.res.XmlResourceParser;import android.os.SystemClock;import android.util.AttributeSet;import android.util.Xml;import android.view.animation.AlphaAnimation;import android.view.animation.Animation;import android.view.animation.AnimationSet;import android.view.animation.GridLayoutAnimationController;import android.view.animation.LayoutAnimationController;import android.view.animation.RotateAnimation;import android.view.animation.ScaleAnimation;import android.view.animation.TranslateAnimation;public class MyAnimationUtil{    /**     * These flags are used when parsing AnimatorSet objects     */    private static final int TOGETHER = 0;    private static final int SEQUENTIALLY = 1;    /**     * Returns the current animation time in milliseconds. This time should be     * used when invoking {@link Animation#setStartTime(long)}. Refer to     * {@link android.os.SystemClock} for more information about the different     * available clocks. The clock used by this method is not the     * "wall" clock (it is not {@link System#currentTimeMillis}).     *      * @return the current animation time in milliseconds     *      * @see android.os.SystemClock     */    public static long currentAnimationTimeMillis()    {        return SystemClock.uptimeMillis();    }    /**     * Loads an {@link Animation} object from a resource     *      * @param context     *            Application context used to access resources     * @param id     *            The resource id of the animation to load     * @return The animation object reference by the specified id     * @throws NotFoundException     *             when the animation cannot be loaded     */    public static Animation loadAnimation(Context context, int id)            throws NotFoundException    {        XmlResourceParser parser = null;        try        {            parser = context.getResources().getAnimation(id);            return createAnimationFromXml(context, parser);        } catch (XmlPullParserException ex)        {            NotFoundException rnf = new NotFoundException(                    "Can't load animation resource ID #0x"                            + Integer.toHexString(id));            rnf.initCause(ex);            throw rnf;        } catch (IOException ex)        {            NotFoundException rnf = new NotFoundException(                    "Can't load animation resource ID #0x"                            + Integer.toHexString(id));            rnf.initCause(ex);            throw rnf;        } finally        {            if (parser != null)                parser.close();        }    }    private static Animation createAnimationFromXml(Context c,            XmlPullParser parser) throws XmlPullParserException, IOException    {        return createAnimationFromXml(c, parser, null,                Xml.asAttributeSet(parser));    }    // 从动画的XML文件创建动画    private static Animation createAnimationFromXml(Context c,            XmlPullParser parser, AnimationSet parent, AttributeSet attrs)            throws XmlPullParserException, IOException    {        Animation anim = null;        // Make sure we are on a start tag.        int type;        int depth = parser.getDepth();        while (((type = parser.next()) != XmlPullParser.END_TAG || parser                .getDepth() > depth) && type != XmlPullParser.END_DOCUMENT)        {            if (type != XmlPullParser.START_TAG)            {                continue;            }            // 开始标签的名称            String name = parser.getName();            /**             * 如果是set标签 则创建AnimationSet动画集合 然后递归调用 参数 c context attrs 属性集             * 代表duration startOffset等属性             */            if (name.equals("set"))            {                anim = new AnimationSet(c, attrs);                createAnimationFromXml(c, parser, (AnimationSet) anim, attrs);                /**                 * 如果是alpha标签 则创建AlphaAnimation动画集合                  * 参数 c :context                   * 参数attrs: 属性集代表duration、 startOffset等属性                 */            } else if (name.equals("alpha"))            {                anim = new AlphaAnimation(c, attrs);                /**                 * 如果是scale标签 则创建ScaleAnimation动画集合                  * 参数 c :context                   * 参数attrs: 属性集代表duration、 startOffset等属性                 */            } else if (name.equals("scale"))            {                anim = new ScaleAnimation(c, attrs);                /**                 * 如果是rotate标签 则创建RotateAnimation动画集合                  * 参数 c :context                   * 参数attrs: 属性集代表duration、 startOffset等属性                 */            } else if (name.equals("rotate"))            {                anim = new RotateAnimation(c, attrs);                /**                 * 如果是translate标签 则创建TranslateAnimation动画集合                  * 参数 c :context                   * 参数attrs: 属性集代表duration、 startOffset等属性                 */            } else if (name.equals("translate"))            {                anim = new TranslateAnimation(c, attrs);            }else{                try {                    anim = (Animation) Class.forName(name).getConstructor(Context.class, AttributeSet.class).newInstance(c, attrs);                } catch (Exception te) {                    throw new RuntimeException("Unknown animation name: " + parser.getName() + " error:" + te.getMessage());                }            }        }        if (parent != null) {            parent.addAnimation(anim);        }    return anim;}}

然后修改我们的activity中的代码 只需要将系统的AnimationUtils换成我们自己的MyAnimationUtils就行了。

    Animation animation = MyAnimationUtil.loadAnimation(this, R.anim.rotate3d);        animation.setFillAfter(true);        animation.setDuration(1000);        img.startAnimation(animation);

这次才是真正的大功告成!!!

**

源码奉送

**

更多相关文章

  1. Android(安卓)sd卡使用解说
  2. android:windowSoftInputMode属性
  3. Notification,Android中通知的使用
  4. Visual Studio 跨平台开发实战(5) - Xamarin Android(安卓)多页
  5. [Android] IntentFilter-->Action、Category属性详解
  6. Android(安卓)NDK C++中的多线程
  7. 基于android的俯卧撑计数器
  8. android之Android中的SQL查询语句LIKE绑定参数问题解决办法(sqlit
  9. Android(安卓)开发:(八)数据适配器 Adapter 篇

随机推荐

  1. 前端html数组去重的方法
  2. 多文件拖拽上传以及利用Jquery替代HTML5
  3. html5 地理位置定位的时候,想用enableHigh
  4. HTML5小试 双人贪吃蛇
  5. html高德地图ip定位之后拖动自定义定位地
  6. knitr html输出中的字符串太长
  7. 将div放在float下面:left divs
  8. 如何提交位于内的输入?
  9. (android 实战总结)android对html支持接
  10. 仅使用CSS的输入字段的一部分