i am new to android. I want to develop one simple application that can allow user to draw anything on screen like allowing user to draw shapes or signature on screen (View).in J2ME we can use pointerDragged() method but i dont know how to do it in android.i tried with onTouchEvent(MotionEvent) but not able to do. Please help.
Thanks a lot. Its working very fine. But one problem is there, Drawing is not that much smooth, i mean when i tried to drag the screen drawing is very bold and i want to restrict the user to draw in limited area. Please suggest me. Awaiting for your valuable suggestions.
One way to achieve is to create a view overriding onDraw(Canvas v),onTouchEvent(MotionEvent e) and onSizeChanged(int w, int h, int oldw, int oldh). Below is code snippet to it. I believe it is self-explanatory.
public class MyView extends View {
private static final float MINP = 0.25f;
private static final float MAXP = 0.75f;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
public MyView(Context c) {
super(c);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xFFFFFFFF);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, 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.reset();
}
#Override
public boolean onTouchEvent(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;
}
}
All you have to do is to call setContentView(new MyView(this)); from your activity class. Hope this will help you.
**Disclaimer: The code snippet isn't mine. I also got it from somewhere from the net. The credit goes to whoever wrote it first.
Related
I created some code that takes a picture and displays the picture, then the user is able to draw on the picture.
I want to implement an undo method. I based my code on many examples I've read. The problem is in my onDraw method - the examples don't use drawBitmap but for me I have to draw the bitmap on the canvas in order for the image to show up.
The code shown displays the image, allows drawing on the image, but does not undo the drawings. I can't figure out what's wrong/how to fix it.
public class PhotoView extends View {
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
private ArrayList<Path> paths = new ArrayList<>();
public PhotoView(Context c) {
super(c);
mBitmap = mutableBitmap;
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
mCanvas = new Canvas(mBitmap);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
//canvas.drawColor(0xFFAAAAAA);
****must call in order for image to show up *****
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
//canvas.drawPath(mPath, mPaint);
for (Path p : paths){
canvas.drawPath(p, mPaint);
}
canvas.drawPath(mPath, mPaint); //real time drawing on canvas
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touchStart(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touchMove(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 touchUp() {
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();
//mPath.reset();
//paths.add(mPath);
}
public void onClickUndo () {
if (paths.size()>0)
{
paths.remove(paths.size()-1);
invalidate();
}
}
public Bitmap getPic() {
mCanvas.save();
return mBitmap;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchStart(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touchMove(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touchUp();
invalidate();
break;
}
return true;
}
}
So there are two things you are doing when it comes to Paths that the user draws.
In the method touchUp() : mCanvas.drawPath(mPath, mPaint);
In onDraw() : canvas.drawPath(p, mPaint);
When you call onClickUndo(), the onDraw() things gets undone. But the one in touchUp() is not undone. That's why your Undo doesn't seem to work. Problem is in line mCanvas.drawPath(mPath, mPaint);
Solution:
Do not draw the mPath on mCanvas. When you do this, your mBitmap gets changed (you are drawing your paths on the mBitmap). There is no way to undo this. And this is not what you want. If you want your paths in your mBitmap, so that you can save it in a file or so, do this finally (perhaps have a method like save() and do this in that method).
I am working in a app, in which I have to develop ALPHABETS, one alphabet in one screen, and so on.
when some one move finger over the Alphabet, it draws color over the alphabet.
The color is visible only over the alphabet, not over the background of the screen.
I wish i can post screenshot. please refer this app for more information-
https://play.google.com/store/apps/details?id=kr.co.smartstudy.phonicsiap_android_googlemarket
in this app we draw alphabet. Same I want to draw.
I have no idea how to develop it, if anyone have some idea in it, please help me.
I am answering to my question.
My requirement was to draw only over the ALPHABETS. I achieved this functionality by creating custom view with override draw method.
Below is my code snippet:
public class DrawingView extends View{
private final Paint mAlphaPaint;
private final Paint clearPaint;
public int width;
public int height;
private Bitmap mBitmap;
private Canvas mCanvas,mCanvasWhite;
private Path mPath,alphaPath;
private Paint mBitmapPaint;
Context context;
private Paint circlePaint;
private Path circlePath;
private Bitmap mBitmapWhite;
public DrawingView(Context c) {
super(c);
context = c;
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
circlePaint = new Paint();
circlePath = new Path();
circlePaint.setAntiAlias(true);
circlePaint.setColor(Color.BLUE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeJoin(Paint.Join.ROUND);
circlePaint.setStrokeCap(Paint.Cap.ROUND);
circlePaint.setStrokeWidth(110f);
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
width = displayMetrics.widthPixels;
height = displayMetrics.heightPixels;
alphaPath = new Path();
alphaPath.moveTo(width*.5f, height*0.1f);
alphaPath.lineTo(100, 100);
alphaPath.lineTo(100, 150);
alphaPath.lineTo(width/2, height/2);
alphaPath.lineTo(100, height - 300);
mAlphaPaint = new Paint();
mAlphaPaint.setAntiAlias(true);
mAlphaPaint.setColor(Color.WHITE);
mAlphaPaint.setStyle(Paint.Style.STROKE);
mAlphaPaint.setStrokeJoin(Paint.Join.ROUND);
mAlphaPaint.setStrokeCap(Paint.Cap.ROUND);
mAlphaPaint.setStrokeWidth(220f);
clearPaint = new Paint();
clearPaint.setStyle(Paint.Style.STROKE);
clearPaint.setStrokeJoin(Paint.Join.ROUND);
clearPaint.setStrokeCap(Paint.Cap.ROUND);
clearPaint.setStrokeWidth(220f);
clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
clearPaint.setColor(Color.TRANSPARENT);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mBitmap.eraseColor(Color.GREEN);
mCanvas = new Canvas(mBitmap);
mCanvas.drawPath(alphaPath, clearPaint);
mBitmapWhite = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mBitmapWhite.eraseColor(Color.WHITE);
mCanvasWhite = new Canvas(mBitmapWhite);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmapWhite,0,0,circlePaint);
canvas.drawBitmap(mBitmap, 0, 0, circlePaint);
mCanvasWhite.drawPath(mPath, circlePaint);
}
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;
circlePath.reset();
circlePath.addCircle(mX, mY, 30, Path.Direction.CW);
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
circlePath.reset();
// commit the path to our offscreen
mCanvasWhite.drawPath(mPath, circlePaint);
// kill this so we don't double draw
mPath.reset();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
Log.d("onTouchEvent",x+","+y);
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;
}
}
Add this view in your layout. In it we can draw on line not on whole surface.
I hope it will help you.
I have worked on the fingerpaint class before and I was very much comfortable but this time I found myself in deep trouble implementing both these functionalities of Undo and deletion
This is my drawing view
public class MyView extends View {
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
public MyView(Context c) {
super(c);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
/*canvas.drawColor(Color.WHITE);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);*/
for (int i=0;i<paths.size();i++){
canvas.drawPath(paths.get(i).path, paths.get(i).paint);
}
canvas.drawPath(mPath, mPaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y)
{
if ( erase == true )
{
mPath.reset();
}
undonePaths.clear();
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 (check == 1 || (selectedId == ERASE_MENU_ID) )
{
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE)
{
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
mCanvas.drawPath(mPath, mPaint);
}
}
if(selectedId == ERASE_MENU_ID)
{
mCanvas.drawPath(mPath, mPaint);
mPath=new Path();
mX = x;
mY = y;
mPath.moveTo(x, y);
}
}
public void onClickUndo () {
if (paths.size()>0)
{
paths.remove(paths.size()-1);
invalidate();
}
else
{
}
//toast the user
}
private void touch_up() {
mCanvas.drawPath(mPath, mPaint);
MyDataHolder md=new MyDataHolder();
md.paint=mPaint;
md.path=mPath;
paths.add(md);
mPath=new Path();
}
#Override
public boolean onTouchEvent(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;
}
}
Now the problem is when I change mPaint's mode to clear
erase=true;
mPaint.setXfermode(null);
mPaint.setStrokeWidth(25.0f);
//mPaint.setColor(Color.TRANSPARENT);
mPaint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
every drawn path turns black with 25 width somebody suggested that I am using same paint so I made a wrapper class to store paint and path object but it's not working.
I can help you with Deletion (Clearing the screen). It's pretty simple and basic approach.
Create a Boolean variable (say mclears). set this to true when your "Clear" button is pressed.
in ondraw(), use
if(clears){
myBitmap.eraseColor(bcolor);
//Sets your canvas (bitmap) color to your background color. i.e. clears canvas
clears=false;
}
You may have to change your Touch_up method a bit. like
private void touch_up() {
mCanvas.drawPath(mPath, mPaint);
MyDataHolder md=new MyDataHolder();
mPath.reset();
md.paint=mPaint;
md.path=mPath;
paths.add(md);
mPath=new Path();
}
I have implemented this in my app. Hope it works for you.
I am creating an android app where in a user can learn to write alphabet. My requirements are like this:
An alphabet is displayed on canvas using Paint.drawText().
User will move his finger on the alphabet to trace it.
I am able to complete the above two requirements.
Now what I want is: if the user is touching his finger anywhere other than the alphabet region(path) it should vibrate/make a buzz.
The Question is: How do I come to know if the user is touching away from the alphabet path/region?
Here is the code snippet.
public class MyView extends View {
private static final float MINP = 0.25f;
private static final float MAXP = 0.75f;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
public MyView(Context c) {
super(c);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xFFAAAAAA);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setTextSize(200);
p.setColor(Color.BLACK);
canvas.drawText("A", 300, 150, p);
// canvas.drawLine(mX, mY, Mx1, My1, mPaint);
// canvas.drawLine(mX, mY, x, y, mPaint);
canvas.drawPath(mPath, mPaint);
Path pa;
canvas.get
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
}
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;
}
mPath.lineTo(mX, mY);
vibe.vibrate(10);
}
private void touch_up() {
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath.reset();
vibe.cancel();
}
#Override
public boolean onTouchEvent(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();
// Mx1=(int) event.getX();
// My1= (int) event.getY();
invalidate();
break;
}
return true;
}
}
I'm working on a test project which is something similar to FingerPaint example in Android SDK Demos. I was trying to implement undo/redo functionality in my project,but the things that I tried didn't work as I expect. I find some questions similar to this over internet and here,but they didn't help me, that's why I'm asking a new question.
Here is some idea what I'm doing actually :
public class MyView extends View {
//private static final float MINP = 0.25f;
//private static final float MAXP = 0.75f;
private Path mPath;
private Paint mBitmapPaint;
public MyView(Context c) {
super(c);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mCanvas.drawColor(Color.WHITE);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, 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.reset();
}
#Override
public boolean onTouchEvent(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;
}
}
Any suggestions/ideas/examples which is the best way to implement this kind of functionality on my project?
I don't know if this is what you had in mind but it's how i am doing it. Instead of storing it in only one path, you store an array with all the paths, like this the user can draw many lines, with a small modification you can add multi touch too.
To make the undo and redo, just remove or add the last path path from the paths variable
and store them in a new array. Something like:
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
}
Here is my modified panel, I cant try it right now but the methods above should work! Hope it helps! (there are few extra variables just remove them :)
private ArrayList<Path> undonePaths = new ArrayList<Path>();
public class DrawingPanel extends View implements OnTouchListener {
private Canvas mCanvas;
private Path mPath;
private Paint mPaint,circlePaint,outercirclePaint;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
private float xleft,xright,xtop,xbottom;
public DrawingPanel(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
circlePaint = new Paint();
mPaint = new Paint();
outercirclePaint = new Paint();
outercirclePaint.setAntiAlias(true);
circlePaint.setAntiAlias(true);
mPaint.setAntiAlias(true);
mPaint.setColor(0xFFFFFFFF);
outercirclePaint.setColor(0x44FFFFFF);
circlePaint.setColor(0xAADD5522);
outercirclePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStyle(Paint.Style.FILL);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(6);
outercirclePaint.setStrokeWidth(6);
mCanvas = new Canvas();
mPath = new Path();
paths.add(mPath);
cx = 400*DrawActivity.scale;
cy = 30*DrawActivity.scale;
circleRadius = 20*DrawActivity.scale;
xleft = cx-10*DrawActivity.scale;
xright = cx+10*DrawActivity.scale;
xtop = cy-10*DrawActivity.scale;
xbottom = cy+10*DrawActivity.scale;
}
public void colorChanged(int color) {
mPaint.setColor(color);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
for (Path p : paths){
canvas.drawPath(p, mPaint);
}
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 0;
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);
}
#Override
public boolean onTouch(View arg0, MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (x <= cx+circleRadius+5 && x>= cx-circleRadius-5) {
if (y<= cy+circleRadius+5 && cy>= cy-circleRadius-5){
paths.clear();
return true;
}
}
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;
}
}
The best solution is that you implement your own Undo / Redo engine.
Save into an array every single action you perform in an array (ie. [0] circle in position x1, y1, [1] line from x2, y2 to x3, y3, etc)
draw
if you need to undo, clear the canvas and repaint all the n - 1 actions from [0] to [n - 1]
if you undo more just have to to paint from [0] to [n - 2] etc
I hope it gives you a hint
Cheers!
One way to implement a do/redo functionality is to encapsulate a method call and all info needed for the call in an object so that you can store it and call it later - the Command Pattern.
In this pattern, each action has its own object: DrawCircleCommand, DrawPathCommand, FillColorCommand, etc. In each object the draw() method is implemented in a unique way but is always called as draw(Canvas canvas) which allows the CommandManager to iterate through the commands. To undo you iterate over the objects calling the undo() method;
Each command object implements an Interface
public interface IDrawCommand {
public void draw(Canvas canvas);
public void undo();
}
An object would look like:
public class DrawPathCommand implements IDrawCommand{
public Path path;
public Paint paint;
public void setPath(path){
this.path = path
}
public void draw(Canvas canvas) {
canvas.drawPath( path, paint );
}
public void undo() {
//some action to remove the path
}
}
Your commands are added to the CommandManager:
mCommandManager.addCommand(IDrawCommand command)
and to undo a command you just call:
mCommandManager.undo();
The CommandManager stores the commands in a data structure e.g. list that allows it to iterative over the command objects.
You can find a complete tutorial here on how to implement the Command Pattern with do/undo for Canvas drawing on Android.
Here is a another tutorial on how implement the Command Pattern in Java;
Fortunately,I solved this today. I find a way to do this.Something like:
private ArrayList<Path> paths = new ArrayList<>();
private ArrayList<Path> undonePaths = new ArrayList<>();
public void undo() {
if (paths.size() > 0) {
LogUtils.d("undo " + paths.size());
clearDraw();
undonePaths.add(paths.remove(paths.size() - 1));
invalidate();
}
}
public void redo() {
if (undonePaths.size() > 0) {
LogUtils.d("redo " + undonePaths.size());
clearDraw();
paths.add(undonePaths.remove(undonePaths.size() - 1));
invalidate();
}
}
public void clearDraw() {
mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
mCanvas.setBitmap(mBitmap);
invalidate();
}
public void clear() {
paths.clear();
undonePaths.clear();
invalidate();
}
in Activity.
private DrawView mDrawView;
if (v == mIvUndo) {
mDrawView.undo();
} else if (v == mIvRedo) {
mDrawView.redo();
in xml
<com.cinread.note.view.DrawView
android:id="#+id/paintView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
If you have a better solution you can contact me.
[here is my blog that I write in
http://blog.csdn.net/sky_pjf/article/details/51086901]
I think that in this case you can use two canvases. You know when user starts drawing and when finishes. So, in touch_start you can create copy of your current canvas. When user clicks undo you replace your current canvas with previously saved.
This should guarantee that you will have previous state of picture, but I am not sure about performance.