PathMeasure可用来画一些路径动画
比如这个搜索动画
下面看一下如何实现
先用path构造图形,分成两个path,里面的放大镜和外面的圆
path1 = new Path();
path1.addArc(100,100,300,300,44,359.9f);
path2 = new Path();
path2.addArc(150,150,250,250,45,359.9f);
path2.lineTo((float) (200 + 85 * Math.sin(45)),(float) (200 + 85 * Math.sin(45)));
再创建PathMeasure对象,并与path相关联
pathMeasure = new PathMeasure(path1,false);
pathMeasure2 = new PathMeasure(path2,false);
pathmeasure主要用到这两个函数
Return the total length of the current contour, or 0 if no path is associated with this measure object.
getLength()返回路径即该pathMeasure相关联的path的长度
getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)
Given a start and stop distance, return in dst the intervening segment(s).
getSegment()函数可返回一个路径片段,第一个参数是路径的起点,第二个参数是路径的重点,第三个参数是返回的路径片段,第四个参数是是否从起点开始,一般都是true
然后创建ValueAnimator
animator = ValueAnimator.ofFloat(0,1);
animator.setDuration(800);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
dst2.reset();
dst2.lineTo(0,0);
float start = value * pathMeasure2.getLength();
pathMeasure2.getSegment(start,pathMeasure2.getLength(),dst2,true);
postInvalidate();
}
});
animator2 = ValueAnimator.ofFloat(0,1);
animator2.setDuration(1000);
animator2.setInterpolator(new AccelerateDecelerateInterpolator());
animator2.setRepeatCount(ValueAnimator.INFINITE);
animator2.setRepeatMode(ValueAnimator.RESTART);
animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
dst.reset();
dst.lineTo(0,0);
float stop = value * pathMeasure.getLength();
float start = (float) (stop - (0.5 - Math.abs(value - 0.5)) * pathMeasure.getLength());
pathMeasure.getSegment(start,stop,dst,true);
postInvalidate();
}
});
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {}
@Override
public void onAnimationEnd(Animator animation) {
animator2.start();
state = State.searching;
}
@Override
public void onAnimationCancel(Animator animation) {}
@Override
public void onAnimationRepeat(Animator animation) {}
});
animator1是放大镜逐渐消失的动画,animator2是外面的圆圈不断的转的动画
可以看到,再animator中不断的取值,不断的构造新的path片段,连接起来就构成了动画
完整代码如下
public class MyView extends View{
private Path path1;
private Path path2;
private Path dst;
private Path dst2;
private PathMeasure pathMeasure;
private PathMeasure pathMeasure2;
private Paint paint;
private ValueAnimator animator;
private ValueAnimator animator2;
public static enum State {ready,start,searching};
private State state;
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
state = State.ready;
dst = new Path();
dst2 = new Path();
path1 = new Path();
path1.addArc(100,100,300,300,44,359.9f);
path2 = new Path();
path2.addArc(150,150,250,250,45,359.9f);
path2.lineTo((float) (200 + 85 * Math.sin(45)),(float) (200 + 85 * Math.sin(45)));
pathMeasure = new PathMeasure(path1,false);
pathMeasure2 = new PathMeasure(path2,false);
paint = new Paint();
paint.setStrokeWidth(15);
paint.setColor(Color.CYAN);
paint.setAntiAlias(true);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStyle(Paint.Style.STROKE);
initAnim();
}
public void initAnim(){
animator = ValueAnimator.ofFloat(0,1);
animator.setDuration(800);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
dst2.reset();
dst2.lineTo(0,0);
float start = value * pathMeasure2.getLength();
pathMeasure2.getSegment(start,pathMeasure2.getLength(),dst2,true);
postInvalidate();
}
});
animator2 = ValueAnimator.ofFloat(0,1);
animator2.setDuration(1000);
animator2.setInterpolator(new AccelerateDecelerateInterpolator());
animator2.setRepeatCount(ValueAnimator.INFINITE);
animator2.setRepeatMode(ValueAnimator.RESTART);
animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
dst.reset();
dst.lineTo(0,0);
float stop = value * pathMeasure.getLength();
float start = (float) (stop - (0.5 - Math.abs(value - 0.5)) * pathMeasure.getLength());
pathMeasure.getSegment(start,stop,dst,true);
postInvalidate();
}
});
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
animator2.start();
state = State.searching;
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
public void startAnim(){
state = State.start;
animator.start();
}
public void endAnim(){
if (animator != null) {
animator.end();
}
if (animator2 != null) {
animator2.end();
}
state = State.ready;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
switch (state){
case ready:
canvas.drawPath(path2,paint);
break;
case start:
canvas.drawPath(dst,paint);
canvas.drawPath(dst2,paint);
break;
case searching:
canvas.drawPath(dst,paint);
break;
}
}
}