How to animate a path on canvas - android
You can transform your canvas by time, i.e:
class MyView extends View { int framesPerSecond = 60; long animationDuration = 10000; // 10 seconds Matrix matrix = new Matrix(); // transformation matrix Path path = new Path(); // your path Paint paint = new Paint(); // your paint long startTime; public MyView(Context context) { super(context); // start the animation: this.startTime = System.currentTimeMillis(); this.postInvalidate(); } @Override protected void onDraw(Canvas canvas) { long elapsedTime = System.currentTimeMillis() - startTime; matrix.postRotate(30 * elapsedTime/1000); // rotate 30° every second matrix.postTranslate(100 * elapsedTime/1000, 0); // move 100 pixels to the right // other transformations... canvas.concat(matrix); // call this before drawing on the canvas!! canvas.drawPath(path, paint); // draw on canvas if(elapsedTime < animationDuration) this.postInvalidateDelayed( 1000 / framesPerSecond); }}
try this:
class PathDrawable extends Drawable implements AnimatorUpdateListener { private Path mPath; private Paint mPaint; private ValueAnimator mAnimator; public PathDrawable() { mPath = new Path(); mPaint = new Paint(); mPaint.setColor(0xffffffff); mPaint.setStrokeWidth(5); mPaint.setStyle(Style.STROKE); } public void startAnimating() { Rect b = getBounds(); mAnimator = ValueAnimator.ofInt(-b.bottom, b.bottom); mAnimator.setDuration(1000); mAnimator.addUpdateListener(this); mAnimator.start(); } @Override public void draw(Canvas canvas) { canvas.drawPath(mPath, mPaint); } @Override public void setAlpha(int alpha) { } @Override public void setColorFilter(ColorFilter cf) { } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override public void onAnimationUpdate(ValueAnimator animator) { mPath.reset(); Rect b = getBounds(); mPath.moveTo(b.left, b.bottom); mPath.quadTo((b.right-b.left)/2, (Integer) animator.getAnimatedValue(), b.right, b.bottom); invalidateSelf(); }}
to test it add in your onCreate method:
TextView view = new TextView(this);view.setText("click me");view.setTextColor(0xffcccccc);view.setGravity(Gravity.CENTER);view.setTextSize(48);final PathDrawable d = new PathDrawable();view.setBackgroundDrawable(d);OnClickListener l = new OnClickListener() { @Override public void onClick(View v) { d.startAnimating(); }};view.setOnClickListener(l);setContentView(view);
You can create a PathMeasure
for your path and determine the length of it:
private PathMeasure pathMeasure; // field// After you've created your pathpathMeasure = new PathMeasure(path, false); pathLength = pathMeasure.getLength();
You can then get get a specific point on the path using getPosTan() inside, say, a ValueAnimator's update listener:
final float[] position = new float[2]; // field// Inside your animation's update method, where dt is your 0..1 animated fractionfinal float distance = dt * pathLength;pathMeasure.getPosTan(distance, position, null);// If using onDraw you'll need to tell the view to redraw using the new positioninvalidate();
You can then make use of the position in onDraw (or whatever).
canvas.drawCircle(position[0], position[1], radius, paint);
The advantages of this approach is that it is straightforward, doesn't require chunky maths, and works on all APIs.
If using API 21+, you can use a ValueAnimator and pass in a Path to use its positions, which is simpler. Example SO question.