Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Canvas Redo and Undo Operation

Tags:

I am working on a drawing project. My code is working perfectly other than canvas redo and undo operations. My undo operation removes paths from the paths ArrayList and saves to the undonePaths ArrayList, and the redo operation removes the last element from undonePaths and saves to paths.

Here's my code:

import java.util.ArrayList; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener;  public class DrawView extends View implements OnTouchListener {     private Canvas  mCanvas;     private Path    mPath;     private Paint       mPaint;        private ArrayList<Path> paths = new ArrayList<Path>();     private ArrayList<Path> undonePaths = new ArrayList<Path>();       private Bitmap im;     public DrawView(Context context)      {         super(context);         setFocusable(true);         setFocusableInTouchMode(true);               this.setOnTouchListener(this);         mPaint = new Paint();         mPaint.setAntiAlias(true);         mPaint.setDither(true);         mPaint.setColor(0xFFFFFFFF);         mPaint.setStyle(Paint.Style.STROKE);         mPaint.setStrokeJoin(Paint.Join.ROUND);         mPaint.setStrokeCap(Paint.Cap.ROUND);         mPaint.setStrokeWidth(6);         mCanvas = new Canvas();         mPath = new Path();         paths.add(mPath);          im=BitmapFactory.decodeResource(context.getResources(),R.drawable.ic_launcher);       }                        @Override         protected void onSizeChanged(int w, int h, int oldw, int oldh) {             super.onSizeChanged(w, h, oldw, oldh);         }          @Override         protected void onDraw(Canvas canvas) {             //mPath = new Path();             //canvas.drawPath(mPath, mPaint);             for (Path p : paths){                 canvas.drawPath(p, mPaint);             }         }          private float mX, mY;         private static final float TOUCH_TOLERANCE = 4;          private void touch_start(float x, float y) {             mPath.reset();             mPath.moveTo(x, y);             mX = x;             mY = y;         }         private void touch_move(float x, float y) {             float dx = Math.abs(x - mX);             float dy = Math.abs(y - mY);             if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {                 mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);                 mX = x;                 mY = y;             }         }         private void touch_up() {             mPath.lineTo(mX, mY);             // commit the path to our offscreen             mCanvas.drawPath(mPath, mPaint);             // kill this so we don't double draw                         mPath = new Path();             paths.add(mPath);         }          public void onClickUndo () {              if (paths.size()>0)              {                 undonePaths.add(paths.remove(paths.size()-1));                invalidate();              }             else             {              }              //toast the user          }          public void onClickRedo (){            if (undonePaths.size()>0)             {                 paths.add(undonePaths.remove(undonePaths.size()-1));                 invalidate();            }             else             {             }              //toast the user          }      @Override     public boolean onTouch(View arg0, MotionEvent event) {           float x = event.getX();           float y = event.getY();            switch (event.getAction()) {               case MotionEvent.ACTION_DOWN:                   touch_start(x, y);                   invalidate();                   break;               case MotionEvent.ACTION_MOVE:                   touch_move(x, y);                   invalidate();                   break;               case MotionEvent.ACTION_UP:                   touch_up();                   invalidate();                   break;           }           return true;     } } 

This code is working perfectly for drawing but not working perfectly for undo and redo operations. What's wrong with my code?

Here's my full source code:

http://www.4shared.com/rar/8PQQEZdH/test_draw.html

UPDATED:

Finally my problem was solved; here's my drawing class:

import java.util.ArrayList; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener;  public class DrawView extends View implements OnTouchListener {     private Canvas  mCanvas;     private Path    mPath;     private Paint       mPaint;        private ArrayList<Path> paths = new ArrayList<Path>();     private ArrayList<Path> undonePaths = new ArrayList<Path>();       private Bitmap im;     public DrawView(Context context)      {         super(context);         setFocusable(true);         setFocusableInTouchMode(true);               this.setOnTouchListener(this);         mPaint = new Paint();         mPaint.setAntiAlias(true);         mPaint.setDither(true);         mPaint.setColor(0xFFFFFFFF);         mPaint.setStyle(Paint.Style.STROKE);         mPaint.setStrokeJoin(Paint.Join.ROUND);         mPaint.setStrokeCap(Paint.Cap.ROUND);         mPaint.setStrokeWidth(6);         mCanvas = new Canvas();         mPath = new Path();          im=BitmapFactory.decodeResource(context.getResources(),R.drawable.ic_launcher);       }                        @Override         protected void onSizeChanged(int w, int h, int oldw, int oldh) {             super.onSizeChanged(w, h, oldw, oldh);         }          @Override         protected void onDraw(Canvas canvas) {             //mPath = new Path();             //canvas.drawPath(mPath, mPaint);             for (Path p : paths){                 canvas.drawPath(p, mPaint);             }             canvas.drawPath(mPath, mPaint);         }          private float mX, mY;         private static final float TOUCH_TOLERANCE = 4;          private void touch_start(float x, float y) {             undonePaths.clear();             mPath.reset();             mPath.moveTo(x, y);             mX = x;             mY = y;         }         private void touch_move(float x, float y) {             float dx = Math.abs(x - mX);             float dy = Math.abs(y - mY);             if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {                 mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);                 mX = x;                 mY = y;             }         }         private void touch_up() {             mPath.lineTo(mX, mY);             // commit the path to our offscreen             mCanvas.drawPath(mPath, mPaint);             // kill this so we don't double draw             paths.add(mPath);             mPath = new Path();                      }          public void onClickUndo () {              if (paths.size()>0)              {                 undonePaths.add(paths.remove(paths.size()-1));                invalidate();              }             else             {              }              //toast the user          }          public void onClickRedo (){            if (undonePaths.size()>0)             {                 paths.add(undonePaths.remove(undonePaths.size()-1));                 invalidate();            }             else             {             }              //toast the user          }      @Override     public boolean onTouch(View arg0, MotionEvent event) {           float x = event.getX();           float y = event.getY();            switch (event.getAction()) {               case MotionEvent.ACTION_DOWN:                   touch_start(x, y);                   invalidate();                   break;               case MotionEvent.ACTION_MOVE:                   touch_move(x, y);                   invalidate();                   break;               case MotionEvent.ACTION_UP:                   touch_up();                   invalidate();                   break;           }           return true;     } } 

That code is working perfectly...!

like image 772
Dinesh Avatar asked Jun 20 '12 07:06

Dinesh


People also ask

How do you undo and redo in canvas?

if you draw something else and release it will do the same. However if you click undo it will pop the top image of undo array and print that to canvas and then push it onto the redo stack. redo when clicked will pop from itself and push to undo. the top of undo will be printed after each mouse off.

How do you implement redo undo?

If “UNDO” string is encountered, pop the top element from Undo stack and push it to Redo stack. If “REDO” string is encountered, pop the top element of Redo stack and push it into the Undo stack. If “READ” string is encountered, print all the elements of the Undo stack in reverse order.

How does canvas work on Android?

The Canvas class holds the "draw" calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).


1 Answers

At first glance I see the following problems:

  • By adding your empty Path to paths as soon as you make it, you're going to have a problem as soon as you undo: you're popping that empty Path first, making the first undo not seem to work. Then if you draw into that Path, it's not added to paths. The solution is to add the completed Path to paths in touch_up() before creating a new one.

That is, remove

paths.add(mPath); 

from the constructor, and in touch_up(), change

mPath = new Path(); paths.add(mPath); 

to

paths.add(mPath); mPath = new Path(); 

You'll also want to add

canvas.drawPath(mPath, mPaint); 

after your for loop in onDraw() in order to draw the in-progress Path.

  • You're not emptying undonePaths when the user starts drawing again.
like image 103
Darshan Rivka Whittle Avatar answered Sep 20 '22 14:09

Darshan Rivka Whittle