今天,我们来讲,怎么让一个小圆圈从屏幕左上角慢慢滑到右下角,其实这个网上很多,但里面有许多小知识点,小细节。我觉得可以拿出来讲讲。
在开始讲今天的主题时,我们先来补充一个知识点:
(1)TypeEvaluator类。
那么TypeEvaluator的作用到底是什么呢?简单来说,就是告诉动画系统如何从初始值过度到结束值。我们在上一篇文章中学到的ValueAnimator.ofFloat()方法就是实现了初始值与结束值之间的平滑过度,那么这个平滑过度是怎么做到的呢?其实就是系统内置了一个FloatEvaluator,它通过计算告知动画系统如何从初始值过度到结束值,我们来看一下FloatEvaluator的代码实现:
public class FloatEvaluator implements TypeEvaluator {
public Object evaluate(float fraction, Object startValue, Object endValue) {
float startFloat = ((Number) startValue).floatValue();
return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
}
}
可以看到,FloatEvaluator实现了TypeEvaluator接口,然后重写evaluate()方法。evaluate()方法当中传入了三个参数,第一个参数fraction非常重要,这个参数用于表示动画的完成度的,我们应该根据它来计算当前动画的值应该是多少,第二第三个参数分别表示动画的初始值和结束值。那么上述代码的逻辑就比较清晰了,用结束值减去初始值,算出它们之间的差值,然后乘以fraction这个系数,再加上初始值,那么就得到当前动画的值了。
好的,那FloatEvaluator是系统内置好的功能,并不需要我们自己去编写,但介绍它的实现方法是要为我们后面的功能铺路的。前面我们使用过了ValueAnimator的ofFloat()和ofInt()方法,分别用于对浮点型和整型的数据进行动画操作的,但实际上ValueAnimator中还有一个ofObject()方法,是用于对任意对象进行动画操作的。但是相比于浮点型或整型数据,对象的动画操作明显要更复杂一些,因为系统将完全无法知道如何从初始对象过度到结束对象,因此这个时候我们就需要实现一个自己的TypeEvaluator来告知系统如何进行过度。
PointEvaluator implements TypeEvaluator {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
Point pointStart= (Point) startValue;
Point pointEnd= (Point) endValue;
float x=pointStart.x+fraction*(pointEnd.x-pointStart.x);
float y=pointStart.y+fraction*(pointEnd.y-pointStart.y);
Point point=new Point((int)x,(int)y);
return point;
}
可以看到,PointEvaluator同样实现了TypeEvaluator接口并重写了evaluate()方法。其实evaluate()方法中的逻辑还是非常简单的,先是将startValue和endValue强转成Point对象,然后同样根据fraction来计算当前动画的x和y的值,最后组装到一个新的Point对象当中并返回。
这样我们就将PointEvaluator编写完成了,接下来我们就可以非常轻松地对Point对象进行动画操作了,比如说我们有两个Point对象,现在需要将Point1通过动画平滑过度到Point2,就可以这样写:
Point point1 = new Point(0, 0);
Point point2 = new Point(300, 300);
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), point1, point2);
anim.setDuration(5000);
anim.start();
代码很简单,这里我们先是new出了两个Point对象,并在构造函数中分别设置了它们的坐标点。然后调用ValueAnimator的ofObject()方法来构建ValueAnimator的实例,这里需要注意的是,ofObject()方法要求多传入一个TypeEvaluator参数,这里我们只需要传入刚才定义好的PointEvaluator的实例就可以了。
好的,这就是自定义TypeEvaluator的全部用法,掌握了这些知识之后,我们就可以来尝试一下如何通过对Point对象进行动画操作,从而实现整个自定义View的动画效果。
自定义的控件:Customeview类:
public class Customeview extends View{
private Point currentPoint;
private Paint paint;
private int REDIUS=50;
public Customeview(Context context, AttributeSet attrs) {
super(context, attrs);
//画笔的初始化
paint=new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLUE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (currentPoint==null){
currentPoint=new Point(REDIUS,REDIUS);
drawCricle(canvas);//画圆
startCricleAnimation();//开始动画
}else {
drawCricle(canvas);
}
}
private void drawCricle(Canvas canvas){
canvas.drawCircle(currentPoint.x,currentPoint.y,REDIUS,paint);
}
private void startCricleAnimation(){
Point startPoint=new Point(currentPoint.x,currentPoint.y);
Point endPoint=new Point(getWidth()-REDIUS,getHeight()-REDIUS);
ValueAnimator valueAnimator= ValueAnimator.ofObject(new PointEvaluator(){},startPoint,endPoint);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentPoint= (Point) animation.getAnimatedValue();
invalidate();
}
});
ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(this,"alpha",1,0,1);
//把两个动画组合在一起
AnimatorSet animatorSet=new AnimatorSet();
animatorSet.play(valueAnimator).with(objectAnimator);
animatorSet.setDuration(5000);
animatorSet.start();
}
}
代码都不过多解释了,都是Android valueAnimator和ObjectAnimator浅谈(一)讲过的内容。
(2)xml布局:
你会说,这个也需要讲,请您,继续往下读,会有点小收获。
注意看Customeview类的这段代码:
Point endPoint=new Point(getWidth()-REDIUS,getHeight()-REDIUS);
getWidth:代表控件的宽度,那你说上面这段代码:getWidth()-REDIUS为左下角小圆圈圆心X坐标???
我估计应该跟Android 源码设计有关,这个问题后续会做出解释。唯一确定点的是Canvas的大小,就是咱们自定义控件的大小。例如你把:Customeview固定好宽度,高度。那么小圆圈,就只会在这个范围内显示。