Android当中的动画3—自定义Tween Animation

来源:互联网 发布:js定时执行 编辑:程序博客网 时间:2024/06/11 05:02

前言

我们在上一遍博客当中学会了如何使用Tween Animation,这一篇博客当中我们将详细的介绍Tween Animation动画的工作流程,并且会介绍我们如何自定义我们自己的动画效果,并且我们会做一个小小的Demo来让我们更加熟练的掌握Tween Animation,同时也为我们为以后学习属性动画,打下坚实的基础。


正文

首先,我们要自定义一个自己的动画,我们不知道怎么去自定义,那我们就去看看Aniamtion的源码,我们仿照Android的源码来自定义自己的动画,这样我们就可以自己去自定义我们的动画了。下面我就以AlphaAnimaton动画来作为我们要去参照的源码,为什么我们会选择它呢,因为是因为AlphaAnimaton动画的参数少,好方便我们去阅读,


package android.view.animation;import android.content.Context;import android.content.res.TypedArray;import android.util.AttributeSet;/** * An animation that controls the alpha level of an object. * Useful for fading things in and out. This animation ends up * changing the alpha property of a {@link Transformation} * */public class AlphaAnimation extends Animation {    private float mFromAlpha;    private float mToAlpha;    /**     * Constructor used when an AlphaAnimation is loaded from a resource.      *      * @param context Application context to use     * @param attrs Attribute set from which to read values     */    public AlphaAnimation(Context context, AttributeSet attrs) {        super(context, attrs);                TypedArray a =            context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AlphaAnimation);                mFromAlpha = a.getFloat(com.android.internal.R.styleable.AlphaAnimation_fromAlpha, 1.0f);        mToAlpha = a.getFloat(com.android.internal.R.styleable.AlphaAnimation_toAlpha, 1.0f);                a.recycle();    }        /**     * Constructor to use when building an AlphaAnimation from code     *      * @param fromAlpha Starting alpha value for the animation, where 1.0 means     *        fully opaque and 0.0 means fully transparent.     * @param toAlpha Ending alpha value for the animation.     */    public AlphaAnimation(float fromAlpha, float toAlpha) {        mFromAlpha = fromAlpha;        mToAlpha = toAlpha;    }        /**     * Changes the alpha property of the supplied {@link Transformation}     */    @Override    protected void applyTransformation(float interpolatedTime, Transformation t) {        final float alpha = mFromAlpha;        t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));    }    @Override    public boolean willChangeTransformationMatrix() {        return false;    }    @Override    public boolean willChangeBounds() {        return false;    }    /**     * @hide     */    @Override    public boolean hasAlpha() {        return true;    }}

我们现在对我们的AlphaAnimation动画,来进行分析,以便我们去自定义我们的动画,首先我们知道,动画总体的把控,就是Animation这个类,而不同动画则有不同的实现类去实现,比如我们的透明度的动画,


我们发现有两个构造方法,第一个构造方法有两个参数,其中一个就是我们在自定义控件的时候见过的AttributeSet,所以我们猜想这个构造方法就是我们在/res/anim/XXXX.xml中定义动画时,系统会去调用的方法,而第二个,这个我们熟悉就是我们在代码当中常常使用到的那个构造方法,现在我们来看,这两个构造方法是告诉我们,在构造方法里面去约束了动画,就是透明度从几到几的这种约束。


下面我们可以看到有三个方法,其实楼主也不知道是干嘛的,我们来测一下吧,

applyTransformation(float interpolatedTime, Transformation t)
willChangeTransformationMatrix()
willChangeBounds()

自己定义一个类,去仿照AlphaAnimation去写一下,写完以后,我们就像是用AlphaAnimation一样,去用我们自己写的这个测试类,下面我给大家看一下,我们的测试类。

public class TestAnimation extends Animation {    private String Tag = "suansuan";    private float mFromAlpha;    private float mToAlpha;      public TestAnimation(Context context, AttributeSet attrs) {        super(context, attrs);        TypedArray a =                context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AlphaAnimation);        mFromAlpha = a.getFloat(com.android.internal.R.styleable.AlphaAnimation_fromAlpha, 1.0f);        mToAlpha = a.getFloat(com.android.internal.R.styleable.AlphaAnimation_toAlpha, 1.0f);        Log.i(Tag,"------>>TestAnimation(Context context, AttributeSet attrs)");        a.recycle();    }    public TestAnimation(float fromAlpha, float toAlpha) {        mFromAlpha = fromAlpha;        mToAlpha = toAlpha;        Log.i(Tag,"------>>TestAnimation(float fromAlpha, float toAlpha)");    }      @Override    protected void applyTransformation(float interpolatedTime, Transformation t) {        final float alpha = mFromAlpha;        Log.i(Tag,"------>>applyTransformation(float interpolatedTime, Transformation t)|||||||" +                "interpolatedTime = " + interpolatedTime);        t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));    }    @Override    public boolean willChangeTransformationMatrix() {        Log.i(Tag,"------>>willChangeTransformationMatrix()");        return false;    }    @Override    public boolean willChangeBounds() {        Log.i(Tag,"------>>willChangeBounds()");        return false;    }}

我们其实也没有做什么东西,我们只是在我们TestAnimation当中加入了适当的Log,好了我们现在去使用一下我们的测试类吧,

我们得出的结论就是动画刚刚开始我们使用构造方法,还有一个叫做initialize()方法,用来初始化,让动画执行的时候,我们会去调用以下的三个方法,至于没个方法是干什么的,我在下方说的很明白


applyTransformation(float interpolatedTime, Transformation t):
每次执行动画,都会在执行动画期间不断地去调用这个方法,而这个方法也就是我们动画的核心代码,在这里面做一些操作,我们第一个参数:里面封装的是时间因子,第二个参数 Transformation,里面维护了一个矩形对象,而我们在不同的时间让我们矩阵以不同的形式展现出来,这就使我们的动画。
willChangeTransformationMatrix():从我们的字面意思去理解这个方法,就是当我们的矩阵规模发生改变的时候会回调 
willChangeBounds():我们理解就是当矩阵对象的边界发生改变回调的方法。
结论

就是我们在构造方法,initialize方法里面区初始化对象,使用applyTransformmation方法里面去不断地去刷新我们对象。

现在我们的问题出现了,我们怎么样通过矩形对我们的动画去做一个变换呢,这里我们就要熟悉图片矩阵的变化,其实我们还可以通过一个辅助类,Camera,这里的Camera不是我们手机当中的相机,而是底层有OpenGL的支持,去实现一些3D的效果,下面我们就去自定义一个3D的旋转动画,我们先来看一下效果




下面我们来看一下我们的/res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/activity_main"    android:layout_width="match_parent"    android:layout_height="match_parent"    >    <ImageView        android:id="@+id/image"        android:padding="20dip"        android:layout_width="match_parent"        android:layout_height="350dip"        android:src="@mipmap/empty_logo_black"        />    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        >        <Button            android:layout_width="0dip"            android:layout_weight="1"            android:layout_height="wrap_content"            android:background="@color/colorPrimary"            android:text="自定义动画X轴"            android:onClick="animationX"            android:textColor="#fff"/>        <Button            android:layout_width="0dip"            android:layout_weight="1"            android:layout_height="wrap_content"            android:background="@color/colorPrimary"            android:text="自定义动画Y轴"            android:onClick="animationY"            android:textColor="#fff"/>        <Button            android:layout_width="0dip"            android:layout_weight="1"            android:layout_height="wrap_content"            android:background="@color/colorPrimary"            android:onClick="animationZ"            android:text="自定义动画Z轴"            android:textColor="#fff"/>    </LinearLayout></RelativeLayout>

使用我们的java代码,MainActvity.java

package com.suansuan.tweenanimation;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.view.animation.Animation;import android.widget.ImageView;import com.suansuan.tweenanimation.animation.TestAnimation;public class MainActivity extends AppCompatActivity {    private ImageView mImage;    private long duration = 1000;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mImage = (ImageView) findViewById(R.id.image);    }    /** 自定义动画X轴 */    public void animationX(View view){        startAnimation(TestAnimation.X);    }    /** 自定义动画Y轴 */    public void animationY(View view){        startAnimation(TestAnimation.Y);    }    /** 自定义动画Z轴 */    public void animationZ(View view){        startAnimation(TestAnimation.Z);    }    /** 抽取动画重复代码 */    public void startAnimation(int state){        TestAnimation testAnimation = new TestAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f,                Animation.RELATIVE_TO_SELF, 0.5f, state);        testAnimation.setDuration(duration);        //保持最后的状态,        testAnimation.setFillAfter(true);        mImage.startAnimation(testAnimation);    }}
最后来看我们的自定义动画TestAnimation.java

package com.suansuan.tweenanimation.animation;import android.graphics.Camera;import android.graphics.Matrix;import android.view.animation.Animation;import android.view.animation.Transformation;/** * * Created by suansuan on 2016/11/4. */public class TestAnimation extends Animation {    public static final int X = 1;    public static final int Y = 2;    public static final int Z = 3;    private int mCurrentState = Z;    private float mFromDegrees;    private float mToDegrees;    private Camera mCamera;    private int mPivotXType = Animation.RELATIVE_TO_SELF;    private int mPivotYType = Animation.RELATIVE_TO_SELF;    private float mPivotXValue = 0.5f;    private float mPivotYValue = 0.5f;    private float mPivotX;    private float mPivotY;    /** 通过Java代码去实现一个该动画的效果 */    public TestAnimation(float formDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue, int state){        this.mFromDegrees = formDegrees;        this.mToDegrees = toDegrees;        if(state > 0 && state < 4){            this.mCurrentState = state;        }        mPivotXType = pivotXType;        mPivotYType = pivotYType;        mPivotXValue = pivotXValue;        mPivotYValue = pivotYValue;    }    @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) {        float degrees = mFromDegrees + ((mToDegrees - mFromDegrees) * interpolatedTime);        final Matrix matrix = t.getMatrix();        mCamera.save();        switch (mCurrentState){            case X:                mCamera.rotateX(degrees);                break;            case Y:                mCamera.rotateY(degrees);                break;            case Z:                mCamera.rotateZ(degrees);                break;        }        mCamera.getMatrix(matrix);        mCamera.restore();        matrix.preTranslate(-mPivotX, -mPivotY);        matrix.postTranslate(mPivotX, mPivotY);    }}

上述就是如果去通过一个Java代码去自定义一个动画,我们也知道,我们的动画是可以在XML当中进行定义的,那我们如何在XML当中定义一个自定义动画呢!下面跟着我一起来做,当然做过自定义控件的来做这个就比较简单了。

1、我们先来自定义属性,在/res/values/attrs.xml当中定义。

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="TestAnimation">        <!-- 旋转类型   x轴 y轴  z轴 -->        <attr name="currentState" format="enum">            <enum name="x" value="1"/>            <enum name="y" value="2"/>            <enum name="z" value="3"/>        </attr>        <!-- 初始角度 -->        <attr name="fromDeg" format="float" />        <!-- 目标角度 -->        <attr name="toDeg" format="float" />    </declare-styleable></resources>
2、在/res/anim/animation.xml定义我们的动画

<?xml version="1.0" encoding="utf-8"?><setxmlns: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"><rotates:com.suansuan.tweenanimation.animation.TestAnimation    rotates:currentState="x"    rotates:fromDeg="80"    rotates:toDeg="720"    android:duration="400"/></set>
3在我们的Activity当中使用我们在xml当中定义的
Animation animation = AnimationHelper.loadAnimation(this, R.anim.animation);animation.setFillAfter(true);mImage.startAnimation(animation);
然后运行,却发现:

E/AndroidRuntime(14985): Caused by: java.lang.RuntimeException: Unknown animation name: com.suansuan.tweenanimation.animation.TestAnimationE/AndroidRuntime(14985):        at android.view.animation.AnimationUtils.createAnimationFromXml(AnimationUtils.java:124)E/AndroidRuntime(14985):        at android.view.animation.AnimationUtils.createAnimationFromXml(AnimationUtils.java:114)E/AndroidRuntime(14985):        at android.view.animation.AnimationUtils.createAnimationFromXml(AnimationUtils.java:91)E/AndroidRuntime(14985):        at android.view.animation.AnimationUtils.loadAnimation(AnimationUtils.java:72)E/AndroidRuntime(14985):        at com.suansuan.tweenanimation.MainActivity$override.animationXML(MainActivity.java:42)E/AndroidRuntime(14985):        at com.suansuan.tweenanimation.MainActivity$override.access$dispatch(MainActivity.java)E/AndroidRuntime(14985):        at com.suansuan.tweenanimation.MainActivity.animationXML(MainActivity.java:0)E/AndroidRuntime(14985):        ... 14 more

不认识我们的名字,我去,我·我·我   好吧,那我们就去看看AnimationUtils的源码把!

/** 这个就是核心的方法了,我们看下去以后,发现,果然没有我们名字 */    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();            if (name.equals("set")) {                anim = new AnimationSet(c, attrs);                createAnimationFromXml(c, parser, (AnimationSet)anim, attrs);            } else if (name.equals("alpha")) {                anim = new AlphaAnimation(c, attrs);            } else if (name.equals("scale")) {                anim = new ScaleAnimation(c, attrs);            }  else if (name.equals("rotate")) {                anim = new RotateAnimation(c, attrs);            }  else if (name.equals("translate")) {                anim = new TranslateAnimation(c, attrs);            } else {                //我们自定义的属性肯定回到这里,那我们就在前面加一个我们的名字               throw new RuntimeException("Unknown animation name: " + parser.getName());            }            if (parent != null) {                parent.addAnimation(anim);            }        }        return anim;    }

我们不可能去修改源码,所以我们在这里的做法就是模仿AnimationUtils去做一个自己的AnimationHelper我们只需要在else if()哪里多加一个我们的 就OK了

else if(name.equals("com.suansuan.tweenanimation.animation.TestAnimation")){                    anim = new TestAnimation(c,attrs);                }
然后试试,OK  ,效果和上面一样。完成,好了,谢谢大家观看,希望如果有错的话,大家可以指出 我们一块进步,一块快乐的码代码。


1 0
原创粉丝点击