Friday, 4 February 2022

How can I make a drawing animation with a child of View, drawing each Path's line one by one?

Using the code I found in this StackOverflow answer I successfully can draw anything in a Canvas by finger, and I will see what I draw while I draw it. From this, I want to make a function triggered on button press that will do two things:

  1. Erase what is drawn over the canvas.
  2. Replay whatever picture was drawn over it, by drawing each path's lines one by one at a constant speed.

In order to do so, I have slightly modificated the onTouchEvent code, so it stores each drawn point accordingly:

@Override
public boolean onTouchEvent(MotionEvent event) {
    StrokePoint point;

    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
            mPath.moveTo(event.getX(), event.getY());

            // Retrieve strokes in memory
            stroke_buffer = new Stroke();
            stroke_buffer.points = new ArrayList<StrokePoint>();
            point = new StrokePoint();
            point.x = event.getX();
            point.y = event.getY();
            stroke_buffer.points.add( point );

            break;
        case MotionEvent.ACTION_MOVE:
            mPath.lineTo(event.getX(), event.getY());

            // Retrieve strokes in memory
            point = new StrokePoint();
            point.x = event.getX();
            point.y = event.getY();
            stroke_buffer.points.add( point );

            invalidate();
            break;
        case MotionEvent.ACTION_UP:
            // Retrieve strokes in memory
            strokes.add(stroke_buffer);
            break;
        default:
            break;
    }
    return true;
}

That way, I can load the X and Y points later in loops (that's the idea at least). The problem is I am not sure how to trigger the "redraw" on the canvas for each line. I tried to store the canvas from the "onDraw" like this:

@Override
protected void onDraw(Canvas canvas) {
    canvas.drawPath(mPath, mPaint);

    // For trying to make the "redraw" later
    this.mCanvas = canvas;

    super.onDraw(canvas);
}

And then, I make this method for the button which I want to erase and replay the previously drawn strokes:

public void replayStrokes() {
    // I start a new path from scratch ad-hoc the "redraw" animation.
    mPath = new Path();
    Log.i("SANDBOX_INFO", "Button pressed");
    // Intention here is leaving the "whiteboard" clear again.
    mCanvas.drawRGB(255, 255, 255);
    invalidate();
    this.draw(mCanvas);
    SystemClock.sleep(1000);

    // Redraw "line by line" loop
    mPath.moveTo(strokes.get(0).points.get(0).x,
                 strokes.get(0).points.get(0).y);
    for (int i = 0; i < strokes.size(); i++) {
        for (int j = 0; j < strokes.get(i).points.size(); j++) {
            if (i == 0 && j == 0) continue;
            mPath.lineTo(strokes.get(i).points.get(j).x,
                         strokes.get(i).points.get(j).y);
            mCanvas.drawPath(mPath, mPaint);
            invalidate();
            this.draw(mCanvas);
            SystemClock.sleep(100);
        }
    }
}

When I press the button, the method replayStrokes doesn't behave as expected. It's like it remains "thinking" (processing) and, when you least expect it, refreshes all the changes in a single blow; instead, I want it to be a smooth animation where you can see the entire Path previously drawn by finger, replayed line by line, point by point. What am I doing wrong? What should I change to obtain the expected behaviour?



from How can I make a drawing animation with a child of View, drawing each Path's line one by one?

No comments:

Post a Comment