Android属性动画用法(中)

来源:互联网 发布:mac flash下载不了 编辑:程序博客网 时间:2024/06/04 00:44

           昨天,我们通过Java代码的形式实现了一些简单的属性动画效果,今天,让我们通过xml文件来实现一下同样的效果,之后,我们再用属性动画实现小球的运动。虽然xml写起来比较麻烦,不像昨天代码中编写的只需要几行代码就搞定,但是xml形式好在易于重用,不需要在每个界面都实现一下同样的效果,下面让我们看看如何实现吧:

使用XML来编写动画,首先要在res目录下面新建一个animator文件夹,所有属性动画的XML文件都应该存放在这个文件夹当中。然后在XML文件中我们一共可以使用如下三种标签:

  • <animator>  对应代码中的ValueAnimator
  • <objectAnimator>  对应代码中的ObjectAnimator
  • <set>  对应代码中的AnimatorSet
实现昨天效果代码如下:

<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android"    android:ordering="together">    <objectAnimator        android:duration="2000"        android:propertyName="translationX"        android:valueFrom="-600"        android:valueTo="0"        android:valueType="floatType" >    </objectAnimator>    <objectAnimator        android:duration="2000"        android:propertyName="alpha"        android:valueFrom="0"        android:valueTo="1"        android:valueType="floatType" >    </objectAnimator></set>
set标签中的ordering属性为动画制定执行顺序,然后我们在activity启动这个动画:

  1. Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_test);  
  2. animator.setTarget(textview);  
  3. animator.start();

效果就不演示了,同昨天一样的。

下面,我们来看一下属性动画更加高级一点的用法,之所以称之为高级,主要体现在属性动画中TypeEvaluator接口实现中,TypeEvaluator是用来干什么的呢?

  • 属性动画进阶- TypeEvaluator的使用

简单来说,就是告诉动画系统如何从初始值过度到结束值。因为之前的动画过渡过程是系统负责的,我们并没有做出相应的干涉和处理,如果你想实现更加复杂和炫酷的动画效果,这个接口相信你会经常用到。

上一篇文章中学到的ValueAnimator.ofFloat()方法就是实现了初始值与结束值之间的平滑过度,那么这个平滑过度是怎么做到的呢?其实就是系统内置了一个FloatEvaluator,它通过计算告知动画系统如何从初始值过度到结束值,我们来看一下FloatEvaluator的代码实现:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class FloatEvaluator implements TypeEvaluator {  
  2.     public Object evaluate(float fraction, Object startValue, Object endValue) {  
  3.         float startFloat = ((Number) startValue).floatValue();  
  4.         return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);  
  5.     }  
  6. }  

可以看到,FloatEvaluator实现了TypeEvaluator接口,然后重写evaluate()方法。evaluate()方法当中传入了三个参数,第一个参数fraction非常重要,这个参数用于表示动画的完成度的,我们应该根据它来计算当前动画的值应该是多少,第二第三个参数分别表示动画的初始值和结束值。那么上述代码的逻辑就比较清晰了,用结束值减去初始值,算出它们之间的差值,然后乘以fraction这个系数,再加上初始值,那么就得到当前动画的值了。好的,那FloatEvaluator是系统内置好的功能,并不需要我们自己去编写,但介绍它的实现方法是要为我们后面的功能铺路的。

上面的方法是对浮点值进行操作的,如果我们想要实现复杂效果,肯定不仅仅局限于这几种常用类型,对象是肯定需要用到的,那么到底该如何使用呢?我们通过一个小例子来看一下实现思路:

  • 如何实现自定义view小球的运动?

在自定义view中,我们经常用到小球这个简单的view实现一些效果,大家都知道,我们一般使用坐标来实现它位置的不断重绘,进而实现动画效果,所以,让它运动的方法就是只要不断改变它的中心点坐标就可以了。那么,我们要通过属性动画实现小球运动,改变的不是所谓int或者float型的初始值和结束值,而是点坐标即Point对象。既然抓住了突破点,我们就可以下手去做了。

1.首先,我们定义一个Point类,表示小球中心点的坐标:

/** * Created by MuFeng on 2017/4/17. * fc:坐标点 */public class Point {    private float x;    private float y;    public Point(float x, float y) {        this.x = x;        this.y = y;    }    public float getX() {        return x;    }    public void setX(float x) {        this.x = x;    }    public float getY() {        return y;    }    public void setY(float y) {        this.y = y;    }}
代码很简单,让我们直接思考下一步该如何入手呢?刚刚上面说过了,TypeEvaluator这个接口用来处理起始值与结束值之间过渡过程的处理,因为我们这里重新定义一个自己的类型作为初始值和结束值,所以,理所当然地要实现它的过渡过程的逻辑,那么下一步当然是写一个实现类了。

2.PointEvaluator类实现TypeEvaluator接口:

/** * Created by MuFeng on 2017/4/17. * fc:制定自己的动画过渡过程,这里实现的是初始坐标到终点坐标的过渡 */public class PointEvaluator implements TypeEvaluator {    @Override    public Object evaluate(float fraction, Object startValue, Object endValue) {        Point startPoint= (Point) startValue;        Point endPoint = (Point) endValue;        float x=startPoint.getX()+fraction*(endPoint.getX()-startPoint.getX());        float y=startPoint.getY()+fraction*(endPoint.getY()-startPoint.getY());        Point point=new Point(x,y);        return point;    }}
实现TypeEvaluator首先要重写它的方法:evaluate()。点的变化取决于横坐标和纵坐标,所以我们只需要将x与y过渡的过程实现一下就好了,这里我们先要定义两个点用于方法传入的起始值与结束值,然后对他们的横纵坐标进行处理,依旧采用的是起始值+fraction*(终点x/y-起点x/y)来得到的,最后新建一个Point传入计算得到的x与y即可。这样,我们的起点到终点的过渡实现就完成了,那下面我们该画出小球并实现其动画效果了,然而并不是!在此之前,我们需要先看一下这个Point以及过渡逻辑能否有效,我们先对一个点进行动画操作看看;

3.对Point进行动画操作,查看过渡过程的数据:

根据前面的学习,我们知道ValueAnimator是对值的操作,所以,这里我们就用它看一下点运动的轨迹:

/** * point 动画 */private void pointAnimation() {    Point start=new Point(0,0);    Point end=new Point(200,200);    ValueAnimator valueAnimator=ValueAnimator.ofObject(new PointEvaluator(),start,end);    valueAnimator.setDuration(3000);    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {        @Override        public void onAnimationUpdate(ValueAnimator animation) {            //获取当前的point            Point currentPoint= (Point) animation.getAnimatedValue();            float x=currentPoint.getX();//获取当前点的x            float y=currentPoint.getY();//获取当前点的y            Log.d("当前点坐标为:","("+x+","+y+")");        }    });    valueAnimator.start();}
这里我们调用的是ofObject()方法来对对象进行动画操作,第一参数是我们自定义的过度规则,第二第三个则是起点与终点了,我们通过监听器实时监听过渡过程point的变化,让我们查看一下LOG信息:



发现point的确是在不断的变化,最终到达终点的,说明我们前面的工作有效的,下面让我开始实现小球的动画吧。

3.小球运动动画实现:

由上面的分析可知,想要实现小球的运动,只要实现它中心点的运动即可,上面我们已经完成了该项工作,说明我们已经离成功还差一小步了,我们先来自定义一个BallView继承自view类,实现里面的构造方法、ondraw()方法以及小球运动的方法即可:

package test.myanimatortest;import android.animation.ValueAnimator;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.view.View;/** * Created by MuFeng on 2017/4/17. * fc:自定义view,实现小球的运动 */public class BallView extends View {    Point currentPoint;//当前point    Paint paint;//画笔    public static final float R=50f;    public BallView(Context context, AttributeSet attrs) {        super(context, attrs);        paint=new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿        paint.setColor(Color.RED);            }    /**     * 在画布上画一个球     */    private void drawBall(Canvas canvas){        float x=currentPoint.getX();        float y=currentPoint.getY();        canvas.drawCircle(x,y,R,paint);    }    /**     * ondraw()重写     */    @Override    protected void onDraw(Canvas canvas) {        if (currentPoint==null){            currentPoint=new Point(100,100);            drawBall(canvas);//画球            //startSport();//开始运动        }else {            drawBall(canvas);        }    }    /**     * 小球的运动动画     */    public void startSport() {        //设置起点终点        Point startPoint=new Point(100,100);        Point endPoint=new Point(getWidth()- R,getHeight()-R);        ValueAnimator ballAnim=ValueAnimator.ofObject(new PointEvaluator(),startPoint,endPoint);        ballAnim.setDuration(3000);        //监听动画过程        ballAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                currentPoint= (Point) animation.getAnimatedValue();//每次变化,都会重新设置currentPoint的位置                invalidate();//每次位置改变都重绘一下小球位置            }        });        ballAnim.start();    }}
我们设置好画笔以及当前点位置信息后,在ondraw()方法中画出一个小球,判断一下,如果当前的point是空的,则重新创建一个,再调用drawBall()方法画出半径为50.0的小球,小球动画是通过startSport()这个方法实现的,我们指定好起点终点后,通过ValueAnimator调用ofObject()方法传入PointEvaluator对象和起始点、结束点,接着通过监听器监听它的动画过程,方法中,每当point位置发生改变,都会重新给小球位置赋值,再调用invalidate()方法不断重绘,进而实现小球的运动效果。

4.最后,在布局文件中加入自定义view,activity中按钮点击实现小球运动:

布局文件:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/activity_sport_animation"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    tools:context="test.myanimatortest.SportAnimationActivity">    <Button        android:id="@+id/startSport"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="开始"        android:layout_margin="16dp"/>    <test.myanimatortest.BallView        android:id="@+id/ball"        android:layout_width="wrap_content"        android:layout_height="wrap_content" /></LinearLayout>
然后在activity中获取view并且实现点击按钮,小球运动的功能:

public class SportAnimationActivity extends AppCompatActivity implements View.OnClickListener {    private Button start;    private BallView ballView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_sport_animation);        start= (Button) findViewById(R.id.startSport);        ballView= (BallView) findViewById(R.id.ball);        start.setOnClickListener(this);    }    @Override    public void onClick(View v) {        switch (v.getId()){            case R.id.startSport:                //pointAnimation();                ballView.startSport();//通过点击“开始”按钮,开始小球的运动                break;        }    }    /**     * point 动画     */    private void pointAnimation() {        Point start=new Point(0,0);        Point end=new Point(200,200);        ValueAnimator valueAnimator=ValueAnimator.ofObject(new PointEvaluator(),start,end);        valueAnimator.setDuration(3000);        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                //获取当前的point                Point currentPoint= (Point) animation.getAnimatedValue();                float x=currentPoint.getX();//获取当前点的x                float y=currentPoint.getY();//获取当前点的y                Log.d("当前点坐标为:","("+x+","+y+")");            }        });        valueAnimator.start();    }}
代码有注释,理解很简单,这里就不多说了,看下运行后的效果吧:


有点卡,总之效果是实现了,今天内容就到这吧,后面会找个属性动画的实战训练一下自己,持续更新中。


补充:本文仅作学习记录和供大家参考,如果有不足和改进之处欢迎大家指出,谢谢。

参考:

http://blog.csdn.net/guolin_blog/article/details/43816093,特别致谢。

http://blog.csdn.net/chenzheng8975/article/details/53710492

1 0