上一篇文章我们分析了ObjectAnimator的使用方法,这篇文章我们将分析ValueAnimator的使用方法。如果还不会使用ObjectAnimator的朋友可以看一下Android属性动画-Property Animation(二) 使用ObjectAnimator完成动画
在Android属性动画-Property Animation(一) 原理分析那篇文章中我们提到过,使用ValueAnimator的话必须要实现ValueAnimator.AnimatorUpdateListener接口,并且要在onAnimationUpdate()方法中手动更新属性值,并且调用invalidate()方法重画。
我们直接看一个例子
public class MainActivity extends Activity {
private ImageView mBall;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBall=(ImageView) findViewById(R.id.my_ball);
mBall.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startAnimation(mBall);
}
});
}
private void startAnimation(final View view) {
//使用ValueAnimator的静态方法ofFloat(float... values)
//这个方法的参数是一个可变数组,理论上可以传很多个,第一个是起始位置,最后一个是结束位置
//也就是evaluate方法里面的(startValue和endValue),一般传一个起始值和一个结束
//值就够了。起始值是必须要传的!ValueAnimator无法自动获得初始值,不传就会空指针异常,
//而ObjectAnimator是可以不传起始值的,至于为什么我们下面会分析。
ValueAnimator anim = ValueAnimator.ofFloat(0.0f,600.0f);
//设置动画执行时间为3000ms
anim.setDuration(3000);
//设置TimeInterpolator为反弹效果
anim.setInterpolator(new BounceInterpolator());
//设置TypeEvaluator为FloatEvaluator,这个要与ofFloat一致,
//我们的起始值和结束值都为float类型,那么计算的时候也要用float计算。
//如果我们使用ofInt的话,那TypeEvaluator就要用IntEvaluator。
//这里我们直接用默认的evaluate计算方法了,即result=startValue+fraction*(startValue+endValue)
anim.setEvaluator(new FloatEvaluator());
//启动动画
anim.start();
//增加一个监听器,用来更新属性
anim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//通过getAnimatedValue()方法获得计算出的实时的值
float disY=(Float) animation.getAnimatedValue();
//把这个值赋给小球的y坐标,这样小球就动起来了
view.setY(disY);
}
});
}
}
有了前面的基础,这个例子已经非常容易理解了。
不知道大家是否还有印象,在第一篇文章中我们说过,Evaluator是用来计算属性值的,它有IntEvaluator、FloatEvaluator、ArgbEvaluator、和 TypeEvaluator,其中前三个都非常好理解,都是计算对应类型数据的属性。而最后一个TypeEvaluator,是由我们自定义类型的,这个类型需要自己实现一个Bean实体类,还是直接看一个例子
首先,我们要创建一个封装了机器人横纵坐标的实体类
public class PointAndrid {
//机器人的x坐标
public float x;
//机器人的y坐标
public float y;
public PointAndroid(){}
public PointAndroid(int x, int y){
this.x=x;
this.y=y;
}
}
接着,是MainActivity
public class MainActivity extends Activity {
private ImageView mBall;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBall=(ImageView) findViewById(R.id.my_ball);
mBall.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startAnimation(mBall);
}
});
}
private void startAnimation(final View view) {
//新建一个ValueAnimator实例
ValueAnimator anim = new ValueAnimator();
//设置动画执行时间
anim.setDuration(3000);
//设置TimeInterpolator,这里设置的是一个反弹效果的interpolator
anim.setInterpolator(new BounceInterpolator());
//设置初始值,如果不设置的话会报空指针异常
anim.setObjectValues(new PointBall(0,0));
//设置TypeEvaluator,这里我们自定义一个TypeEvaluator,为小球的横纵坐标点
anim.setEvaluator(new TypeEvaluator
() {
/**
* 重写这个evaluate方法,这个方法是根据fraction和startValue和endValue
* 来计算属性值的,上篇文章中我们是基于默认的evaluate的计算方法
* 即result=startValut+fraction*(endValue-startValue)分析的
* 现在我们要自定义一个evaluate计算方法,目的是模拟小球的一个抛物线下落的过程,
* 没有固定的写法,自己随意创造,如果不清楚fraction怎么来的,请参考第一篇文章。
*/
@Override
public PointBall evaluate(float fraction, PointBall startValue,
PointBall endValue) {
PointBall point = new PointBall();
point.x = fraction*380;
point.y = 60 * (fraction * 3) * (fraction * 3);
return point;
}
});
//调用start()方法,动画就开始执行了
anim.start();
//第一篇文章中说过,我们要实现一个监听器,用于更新属性值与重画等操作
anim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//通过getAnimatedValue()可以得到实时计算出来的小球坐标点的值
PointAndroid point = (PointAndroid) animation.getAnimatedValue();
//调用View的setX()和setY()方法,设置小球的横纵坐标,由于setX()和setY()
//中系统已经写好了重画的操作,我们就不用自己去重画了,否则我们要调用invalidate()方法进行重画
view.setX(point.x);
view.setY(point.y);
}
});
}
}
当然,也可以这样写
private void startAnimation(final View view) {
ValueAnimator anim = ValueAnimator.ofObject(new TypeEvaluator
() {
@Override
public PointF evaluate(float fraction, PointF startValue,
PointF endValue) {
PointF point = new PointF();
point.x = fraction * 420;
point.y = 55 * (fraction * 3) * (fraction * 3);
return point;
}
}, new PointF(0,0));
anim.setDuration(3000);
anim.setInterpolator(new BounceInterpolator());
anim.start();
anim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF = (PointF) animation.getAnimatedValue();
view.setX(pointF.x);
view.setY(pointF.y);
}
});
}
这两种写法是完全一样的。
注意:我想我还是有必要再强调一下,在ValueAnimator.AnimatorUpdateListener接口中我们重写了onAnimationUpdate()方法,在这个方法中我们修改了属性的值,我们需要调用Invalidate()方法来重画更新过属性值的视图。那为什么我们在上面例子中没有调用invalidate()方法呢,那是因为我们调用的setX()、setScaleY()等函数时,系统已经帮我们写好了invalidate()方法,不用我们自己再去调用了,但如果是我们自己写的setter方法,那一定要记得调用invalidate(),否则视图不会更新的,动画也不会动的!
最后附上一张Interpolators表格,系统自带的这些interpolators已经够我们用得了,基本没有机会让我们自己去写一个。
ValueAnimator我们已经分析完了,下一篇文章我们将分析如何使用AnimatorSet实现动画组合,如何给动画加监听,以及如何使用XML实现属性动画。