本篇记录一个小球沿着正弦曲线运动并且自身不断放大缩小还能变换自身颜色的属性动画。
1、梳理一下实现上面效果的需要用到那些知识点:
1.1这个动画的主角是“一个小球”因此我们需要使用canvas绘制一个小球,为了突出它是是沿正弦曲线运动我们在屏幕上加一条横线,同样使用canvas绘制
1.2 正弦曲线就需要ValueAnimator使用自定义一个估值器继承TypeEvaluator,计算小球的运动轨迹
1.3 小球放大缩小也需要一个ValueAnimator计算小球的半径
1.4 变换小球自身颜色,需要定义一个动画使用ArgbEvaluator估值器
1.5 使用一个AnimatorSet属性动画合集让他们同时生效达到预期效果
1.6 还可以给AnimatorSet加上插值器实现非匀速运动
2、代码实现
2.1首先是定义PointAnimView extends View
2.2 定义了个成员变量
private Point currentPoint; //记录小球运动轨迹的x、y坐标值
private Paint mPaint, linePaint;//绘制小球和屏幕中间横线的画笔
private AnimatorSet animSet;//动画合集
private TimeInterpolator mInterpolator = new LinearInterpolator();//定义了个插值器的缺省值,后面有方法可以自由切换
private int RADIUS = 0; //小球半径
//实现关于color 的属性动画
private int color; //小球颜色
private float radius = RADIUS; //小球半径
2.3 在构造方法中调用了一个init方法
private void drawCircle(Canvas canvas) {
float x = currentPoint.x;
float y = currentPoint.y;
canvas.drawCircle(x, y, radius, mPaint);
}
2.4 使用canvas绘制小球和屏幕中间一条横线
private void drawCircle(Canvas canvas) {
float x = currentPoint.x;
float y = currentPoint.y;
canvas.drawCircle(x, y, radius, mPaint);
}
private void drawLine(Canvas canvas) {
canvas.drawLine(10, getHeight() / 2, getWidth(), getHeight() / 2, linePaint);
}
2.5 定义正弦曲线使用的估值器
public class SinTypeEvaluator implements TypeEvaluator {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
Point startPoit = (Point) startValue;
Point endPoint = (Point) endValue;
Log.e("估值器参数", "evaluate: -3 = "+startPoit.x+" "+startPoit.y );
Log.e("估值器参数", "evaluate: -4 = "+endPoint.x+" "+endPoint.y );
int x = (int) (startPoit.x+fraction*(endPoint.x-startPoit.x));
int y = (int) ((Math.sin(x*Math.PI/180))*100+endPoint.y/2);
Point point = new Point(x, y);
return point;
}
}
2.6改变小球运动轨迹的animator,使用了自定义的估值器
final ValueAnimator valueAnimator = ValueAnimator.ofObject(new SinTypeEvaluator(), currentPoint, endP);
valueAnimator.setRepeatCount(-1);
valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentPoint=(Point) animation.getAnimatedValue();
Log.e("估值器参数", "evaluate: -5 = "+currentPoint.x+" "+currentPoint.y );
postInvalidate();
}
});
2.7 改变小球颜色的animator
ObjectAnimator animColor = ObjectAnimator.ofObject(this, "color", new ArgbEvaluator(), Color.GREEN,
Color.YELLOW, Color.BLUE, Color.WHITE, Color.RED);
animColor.setRepeatCount(-1);
animColor.setRepeatMode(ValueAnimator.REVERSE);
animColor.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
}
});
看到上面ofObject方法内的"color"了吧。这里有个知识点:想要对象没有的属性执行属性动画,如果有条件修改我们可以使用自定义它的get/set方法,本类增加的getColor/setColor方法用来修改属性,这里第二个的propertyName不区分首字母大小写但区分其他字母,比如可以是color或者Color但不能是ColoR,它会自动校验这个属性你使用ColoR它不会有效果但如果你用的连字母贫血都是错的他就会报错提示你:Could not find property setter method setColo on 包名.文件夹.类名,比如我的是直接修改了成员变量color:
public int getColor() {
return color;
}
/**
* @param color
* 应对为view自身加属性动画时的"color"属性,因为属性动画填入的属性要求必须有set和get(没有对象初始值时用,其他地方不用)方法
*/
public void setColor(int color) {
this.color = color;
mPaint.setColor(this.color);
}
2.8 小球放大缩小的动画
ValueAnimator animScale = ValueAnimator.ofFloat(20f, 80f, 60f, 10f, 35f, 55f, 10f);
animScale.setRepeatCount(-1);
animScale.setRepeatMode(ValueAnimator.REVERSE);
animScale.setDuration(5000);
animScale.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
radius = (float) animation.getAnimatedValue();
Log.e(TAG, "onAnimationUpdate: 缩放动画radius="+radius );
}
});
2.9 创建动画合集、设置插值器(非必须)、start执行动画
animSet = new AnimatorSet();
//同时执行sign正弦曲线、颜色、缩放三个动画。用到了自定义估值器、颜色动画用到了系统的ARGBEvaluator
try {
animSet.play(valueAnimator).with(animColor).with(animScale);
animSet.setDuration(5000);
//使用不同的插值器
animSet.setInterpolator(getInterpolatorType(10));
animSet.start();
/**
* 不同插值器
* @param type
* @return
*/
public TimeInterpolator getInterpolatorType(int type) {
switch (type) {
case 1:
mInterpolator = new BounceInterpolator();
break;
case 2:
mInterpolator = new AccelerateDecelerateInterpolator();
break;
case 3:
mInterpolator = new DecelerateInterpolator();
break;
case 4:
mInterpolator = new AnticipateInterpolator();
break;
case 5:
mInterpolator = new LinearInterpolator();
break;
case 6:
mInterpolator =new LinearOutSlowInInterpolator();
break;
case 7:
mInterpolator = new OvershootInterpolator();
case 8:
mInterpolator = new AccelerateInterpolator();
case 9:
mInterpolator = new AnticipateOvershootInterpolator();
case 10:
mInterpolator = new CycleInterpolator(1.0F);
default:
mInterpolator = new LinearInterpolator();
break;
}
return mInterpolator;
}
2.10 动画启动了就要记得关闭
public void stopAnimation() {
if (animSet != null) {
animSet.cancel();
this.clearAnimation();
}
}
2.11 这动画能执行、中间横线能画出来都离不开它
@Override
protected void onDraw(Canvas canvas) {
if (currentPoint == null) {
currentPoint = new Point(RADIUS, RADIUS);
}
drawCircle(canvas);
drawLine(canvas);
}
onDraw是view的声明周期方法,除了首次显示会执行之外后面动画之所以能动。其实依赖上面animator的onAnimationUpdate回调方法中调用了postInvalidate();每次调用postInvalidate都会调用onDraw,每次使用新的坐标、半径和颜色绘制小球达到预期效果。
3、浅浅总结一下
3.1 color、radius、currentPoint这些值不断变动并通过postInvalidate触发onDraw引起重绘。
3.2 想要对象没有的属性执行属性动画,如果有条件修改我们可以使用自定义它的get/set方法,本类增加的getColor/setColor方法用来修改属性。在使用时一定注意上文提到的拼写要求不要写错。
3.3 插值器呢有很多,这里是随便用了一个,有需要可以自己测试。
才疏学浅,如有错误,欢迎指正,多谢。