0

I created an app to draw and wanted to implement the functions undo / redo, I tried various methods found surfing but none of them work, can someone help me?

Here is my code:

Variables

'public class MainDrawingView extends View {
public MainDrawingView(Context context, AttributeSet attrs) {

    super(context, attrs);
    setupDrawing();

}

float TOUCH_TOLERANCE = 4;
float mX, mY;
//drawing path
private Path drawPath, drawX, drawY;
//drawing and canvas paint
private Paint drawPaint;
private Paint canvasPaint;
private View canvasback;
private Paint bccanvas;
//initial color
private int paintColor = 0xFF000000;
//canvas
private Canvas drawCanvas;
//canvas bitmap
private Bitmap canvasBitmap, trans;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();'

SetupDrawing

'private void setupDrawing() {
    drawPath = new Path();
    drawPaint = new Paint();
    bccanvas = new Paint();
    drawPaint.setColor(paintColor);
    drawPaint.setAntiAlias(true);
    drawPaint.setStrokeWidth(20);
    drawPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    drawPaint.setStrokeJoin(Paint.Join.ROUND);
    drawPaint.setStrokeCap(Paint.Cap.ROUND);
    canvasPaint = new Paint(Paint.DITHER_FLAG);

}'

onSizeChanged

'@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {

    super.onSizeChanged(w, h, oldw, oldh);
    canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    drawCanvas = new Canvas(canvasBitmap);

}'

Touch Event

'private void touch_start(float x, float y) {
    undonePaths.clear();
    drawPath.reset();
    drawPath.moveTo(x, y);

    mX = x;
    mY = y;
}

private void touch_move(float x, float y) {
    float dx, dy;
    dx = Math.abs(x - mX);
    dy = Math.abs(y - mY);
    if ((dx >= TOUCH_TOLERANCE) || (dy >= TOUCH_TOLERANCE)) {
        drawPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
        drawPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
        drawPath.lineTo(mX, mY);
        // commit the path to our offscreen
        drawCanvas.drawPath(drawPath, drawPaint);
        drawPath.reset();
        drawPath.moveTo(mX, mY);


        mX = x;
        mY = y;
    }
}

private void touch_up() {
    drawPath.reset();

}'

onTouchEvent

'public boolean onTouchEvent(MotionEvent event) {
    float x = event.getX();
    float y = event.getY();
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:

            touch_start(x, y);
            drawPath.moveTo(x, y);

            break;
        case MotionEvent.ACTION_MOVE:

            touch_move(x, y);
            drawPath.lineTo(x, y);
            invalidate();
            break;
        case MotionEvent.ACTION_UP:
            drawPath.reset();
            touch_up();

            invalidate();
            break;
    }
    return true;
}'

onDraw

'@SuppressLint("NewApi")
@Override
protected void onDraw(Canvas canvas) {

        canvas.drawPath(p, drawPaint);
        canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
    }'

onClickUndo

'public void onClickUndo () {
        if (paths.size()>0){

        undonePaths.add(paths.remove(paths.size()-1));
        invalidate();
        undonePaths.clear();
    }
    else
    {

    }
    //toast the user
}'

onClickRedo

'public void onClickRedo (){
    if (undonePaths.size()>0)
    {
        paths.add(undonePaths.remove(undonePaths.size()-1));
        invalidate();
    }
    else
    {

    }
    //toast the user
}'
rickyxd
  • 237
  • 1
  • 6
  • 14

2 Answers2

0

TL;DR.

I created a similar application in Java Swing a couple of months ago. The way I implemented the 2 methods was I had created a Stack of shapes which was added to as the user was adding shapes to the whiteboard and then push/popped from this to undo/redo (storing the shape which was removed in a temp variable)

protected Shape removed; //shape object of an undo-ed object
protected Stack<Shape> shapes = new Stack<Shape>(); //stack to store the shapes

public void undo() 
{
    if (!shapes.empty()) //only undo if there's shapes on the board
    {
        removed = shapes.pop(); //pop removes a shape and returns it into 'removed' object
        repaint();
    }
}

public void redo() 
{
    if (removed != null)  //only redo if something has been undone
    {
        shapes.push(removed); //push adds the object back onto the stack
        removed = null;
        repaint();
    }
}

I hope this helps!

Alan Kavanagh
  • 9,425
  • 7
  • 41
  • 65
  • I tried but did not work out. Casually I have to add something in OnDraw, OnTouchEvent, Touch Event or OnSizeChanged ?? – rickyxd May 17 '15 at 11:34
  • You will need to store your shapes somewhere so that you can undo/redo them. I would recommend creating instances of 2D shapes as you are placing points. Alternatively, you could undo and redo the vertices that you are drawing but again you will need to store a stack of the vertices so you know which was the last one placed for the undo. You could implement 2 stacks, one for vertices drawn and one for vertices undone. You can just pop the vertices from the drawn stack into the undone stack as you're undoing and then the opposite way for redo. – Alan Kavanagh May 17 '15 at 14:02
  • Update: Has a quick glance at your code, it seems you are storing paths. Convert your path arraylists to stacks and you can push/pop them as you undo/redo. Be sure that your paint method only draws the paths stack and not the undonePaths – Alan Kavanagh May 17 '15 at 14:04
0

A good approach is to create an interface called something like "Undoable". It has at least 3 overrrides: do(), undo() and redo(). Then do all your work in classes that implement Undoable, and keep a stack of these around = the undo history.

Ron Kuper
  • 817
  • 5
  • 14
  • This is my first application so I do not understand very well what I should do.You can post the code for the event? – rickyxd May 17 '15 at 11:29