0

I am working on a drawing app for android users. I want to implement undo redo functionality on my app. I am referring questions posted on stackoverflow to solve my problem but I could not be able to find the right solution for my code. Below I am posting my code, please help me to get rid of this issue.Any help would be highly appreciated.

Thank you in advance.

public class DrawingView extends View {

 private ArrayList<Path> paths = new ArrayList<Path>();
 private ArrayList<Path> undonePaths = new ArrayList<Path>(); 

public DrawingView(Context context, AttributeSet attrs) {
    super(context, attrs);
    // TODO Auto-generated constructor stub
    this.context = context;
    setupDrawing();
}

public void setupDrawing(){

    drawPath = new Path();
    drawPaint = new Paint();

    canvasPaint = new Paint(Paint.DITHER_FLAG);

    brushSize = getResources().getInteger(R.integer.medium_size);
    lastBrushSize = brushSize;

    drawPaint.setColor(paintColor);

    drawPaint.setAntiAlias(true);
    drawPaint.setStrokeWidth(brushSize);
    drawPaint.setStyle(Paint.Style.STROKE);
    drawPaint.setStrokeJoin(Paint.Join.ROUND);
    drawPaint.setStrokeCap(Paint.Cap.ROUND);

    paths.add(drawPath);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
//view given size
    super.onSizeChanged(w, h, oldw, oldh);

    canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    drawCanvas = new Canvas(canvasBitmap);
}

@Override
protected void onDraw(Canvas canvas) {
    //draw view
    canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
    canvas.drawPath(drawPath, drawPaint);
    for(Path p : paths){
        canvas.drawPath(p, drawPaint);
    }
}

@Override
public boolean onTouchEvent(MotionEvent event) {

    float touchX = event.getX();
    float touchY = event.getY();

    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
    //  undonePaths.clear();
        drawPath.moveTo(touchX, touchY);
        break;
    case MotionEvent.ACTION_MOVE:
        drawPath.lineTo(touchX, touchY);
        break;
    case MotionEvent.ACTION_UP:
    //  drawPath.lineTo(touchX, touchY);
        drawCanvas.drawPath(drawPath, drawPaint);
        drawPath.reset();
        drawPath = new Path();
        paths.add(drawPath);  
        break;
    default:
        return false;
    }
    invalidate();
    return true;   
}


public void onClickUndo(){
    if(paths.size()>0){
        undonePaths.add(paths.remove(paths.size() - 1));
        Toast.makeText(getContext(), "Undo is working", Toast.LENGTH_LONG).show();
        invalidate();
    }
    else{
        Toast.makeText(getContext(), "Undo is not working", Toast.LENGTH_LONG).show();
    }
}

public void onClickRedo(){
    if(undonePaths.size()>0){
        paths.add(undonePaths.remove(undonePaths.size() - 1));
        Toast.makeText(getContext(), "Redo is working", Toast.LENGTH_LONG).show();
        invalidate();
    }
    else{
        Toast.makeText(getContext(), "Redo is not working", Toast.LENGTH_LONG).show();
    }
}

}
himanshu
  • 1,990
  • 3
  • 18
  • 36
  • What is happening when you click undo? Does it say "Undo is not working" or are you having some undesired effect? – CodeCamper Apr 12 '14 at 08:29

2 Answers2

0

Just a few minor changes.

There's an extra Path in your lists that will cause an Undo/Redo step to essentially do nothing when the rest is corrected. So in setupDrawing() remove:

paths.add(drawPath);

In the onDraw() method, remove:

canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);

This drawBitmap() call was redrawing the member Bitmap that is drawn to on every ACTION_UP, making it appear as though your Path lists weren't being updated.

Then, in the onTouchEvent() method, in MotionEvent.ACTION_DOWN un-comment:

undonePaths.clear();

and change the MotionEvent.ACTION_UP case to:

case MotionEvent.ACTION_UP:
    drawCanvas.drawPath(drawPath, drawPaint);
    paths.add(drawPath);  
    drawPath = new Path();
    break;

The reset() call was clearing the Path, which you don't want. Instead, here we add the recently completed Path to the list, and then create a new one to continue.

NB: The way this is currently working, the Undo/Redo functionality will not affect the canvasBitmap. As it is unclear what this Bitmap is for, I left it as is. If you want this Bitmap to mirror the View's, you can simply move the drawCanvas.drawPath() calls to onDraw(), in parallel to the canvas.drawPath() calls there.

Mike M.
  • 38,532
  • 8
  • 99
  • 95
  • Your code is working perfectly without canvasBitmap. But I need to make it work with canvasBitmap. I am referring this tutorial http://code.tutsplus.com/tutorials/android-sdk-create-a-drawing-app-touch-interaction--mobile-19202. Please suggest me to make it work with canvasBitmap too. – himanshu Apr 14 '14 at 08:38
  • What is `canvasBitmap` for? I.e., what's its functionality? – Mike M. Apr 14 '14 at 12:34
  • its a pattern fill bitmap, which i can choose and draw through finger touch. – himanshu Apr 14 '14 at 13:24
  • http://code.tutsplus.com/tutorials/android-sdk-drawing-with-pattern-fills--mobile-19527 see this link. some patterns are there like grass and sky bitmap. – himanshu Apr 14 '14 at 13:25
  • "its a pattern fill bitmap" - No, it's not. It's a "buffer" that's saving the View's image between `onDraw()` calls. – Mike M. Apr 14 '14 at 13:51
  • can you suggest me anything to implement undo,redo functionality on it? – himanshu Apr 14 '14 at 14:02
  • The way you're implementing your Undo/Redo, i.e. redrawing every Path in `onDraw()`, you don't need `canvasBitmap`. You will, however, need to keep a list of the Paint objects, too. – Mike M. Apr 14 '14 at 14:15
-1
var undoRedo = new Array();
var unStep = -1;

function historySave() {
    unStep++;
    while (undoRedo.length > 20) {
        undoRedo.shift();
        unStep--;
    }
    if (unStep !== 0 && unStep < undoRedo.length) {
        undoRedo.length = unStep;
        unStep++;
    } else {
        undoRedo.length = unStep;
    }
    undoRedo.push(document.getElementById('drawingCanvas').toDataURL());    
}

function Undo() {
    if (unStep > -1) {
        unStep--;
        var canvasPic = new Image();
        canvasPic.src = undoRedo[unStep];
        drawingContext.clearRect(0,0,drawingCanvas.width,drawingCanvas.height);
        canvasPic.onload = function () {
        drawingContext.drawImage(canvasPic, 0, 0);
            }
    }
}

function Redo() {
    if (unStep < undoRedo.length - 1) {
        unStep++;
        var canvasPic = new Image();
        canvasPic.src = undoRedo[unStep];
        canvasPic.onload = function ()  { 
        drawingContext.drawImage(canvasPic, 0, 0);
        }
    }
}