Android属性动画用法(中)

2 篇文章 0 订阅
2 篇文章 0 订阅

           昨天,我们通过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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值