Translate this page to

Search Android

Search For Android Tutorials (NEW, Custom Search for Android Developers)
Loading

Monday, June 8, 2009

Drawing with Canvas in Android

If you had been reading previous post then you'll know i'm not a java guy, i literally spend hours trying to figure out how canvas works perfectly on java. Then i remember that javascript/html5 has canvas too, so i revisited this one (haven't visited it for almost half a year) https://developer.mozilla.org/en/drawing_graphics_with_canvas and see that i must use paths (2nd example, beginPath) rather than drawing point by point. So together with this article http://www.droidnova.com/playing-with-graphics-in-android-part-iv,182.html. I come up with a pretty descend drawing app.

To do this:
Copy the whole code from droidnova the replace the following.

On your Activity
private ArrayList _graphics = new ArrayList();
private Paint mPaint;
@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(new DrawingPanel(this));
  mPaint = new Paint();
  mPaint.setDither(true);
  mPaint.setColor(0xFFFFFF00);
  mPaint.setStyle(Paint.Style.STROKE);
  mPaint.setStrokeJoin(Paint.Join.ROUND);
  mPaint.setStrokeCap(Paint.Cap.ROUND);
  mPaint.setStrokeWidth(3);
}



On your SurfaceView
@Override
public boolean onTouchEvent(MotionEvent event) {
  synchronized (_thread.getSurfaceHolder()) {
    if(event.getAction() == MotionEvent.ACTION_DOWN){
      path = new Path();
      path.moveTo(event.getX(), event.getY());
      path.lineTo(event.getX(), event.getY());
    }else if(event.getAction() == MotionEvent.ACTION_MOVE){
      path.lineTo(event.getX(), event.getY());
    }else if(event.getAction() == MotionEvent.ACTION_UP){
      path.lineTo(event.getX(), event.getY());
      _graphics.add(path);
    }
    return true;
  }
}
@Override
public void onDraw(Canvas canvas) {
  for (Path path : _graphics) {
    //canvas.drawPoint(graphic.x, graphic.y, mPaint);
    canvas.drawPath(path, mPaint);
  }
}





Explanation
We create a path and start that path when the MotionEvent is down, or when the user first touch the screen, then add a lineTo, the x and y when the user moves his fingers. Then stop and push the path we had build to our array of Paths.
public boolean onTouchEvent(MotionEvent event) { .... }

Then during the draw function we loop through the array and print them on our canvas.
public void onDraw(Canvas canvas) { ... }

Sources
CanvasDrawing.java

References
http://www.droidnova.com/playing-with-graphics-in-android-part-iv,182.html
https://developer.mozilla.org/en/drawing_graphics_with_canvas
Android IRC - irc://freenode/android-dev

10 comments:

Rolli said...

Thanks, great tutorial!
I would like to ask if it's a way to draw while moving
I'v tried to modify the code writing this:

public boolean onTouchEvent(MotionEvent event) {
synchronized (_thread.getSurfaceHolder()) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
path = new Path();
path.moveTo(event.getX(), event.getY());
path.lineTo(event.getX(), event.getY());
}else if(event.getAction() == MotionEvent.ACTION_MOVE){
path.lineTo(event.getX(), event.getY());
_graphics.add(path);
path = new Path();
path.moveTo(event.getX(), event.getY());
}else if(event.getAction() == MotionEvent.ACTION_UP){
path.lineTo(event.getX(), event.getY());
_graphics.add(path);
}

return true;
}
}

this let to draw every time we do an ACTION_MOVE instead only on ACTION_UP.. but I've noticed a slowly progressive loss of performance.. what do you think about?
Rolli

stu said...

Thanks, great tutorial!
i have been any question
why do you used it(surfaceView)?
i need View

Hollowback said...

Re: Rolli
Hi, I need solution for that too :). Please let me know, if you resolve it.
Thanks

Zingo said...

Rolli:
I think you get you path stuffed into the path array many times (one per move) try to move _graphics.add(path); to ACTION_DOWN and remove it from the other places. This will add one path per down event and make the path longer while moving.

e.g.

@Override
public boolean onTouchEvent(MotionEvent event) {
synchronized (_thread.getSurfaceHolder()) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
path = new Path();
path.moveTo(event.getX(), event.getY());
path.lineTo(event.getX(), event.getY());
_graphics.add(path);
}else if(event.getAction() == MotionEvent.ACTION_MOVE){
path.lineTo(event.getX(), event.getY());
}else if(event.getAction() == MotionEvent.ACTION_UP){
path.lineTo(event.getX(), event.getY());
}

return true;
}
}

Anonymous said...

Hello, I can't get the code to compile. Would it be too much to ask for, if you'd could paste the whole source code somewhere? Have been scratchin my head for quite som time now:)

Sincerely Oskar

Almond said...

@Oskar Here you go http://sites.google.com/site/tutorialsformobileprogramming/android-tutorials/android-files you can download the zip file there :)

Anonymous said...

Thanks a lot, my friend! You made my day:D

//Oskar

Anonymous said...

Hi, is there any way to reset the canvas? To delete all of the previously painted stuff to a blank start screen whithout having to restart the app?:)

Otherwise a truly great tutorial mate!

/Peter

Almond said...

There are a lot of ways to delete or reset the canvas but the reset is kinda not directly clear it. If your canvas came with bitmap, you could recycle the bitmap and redraw the bitmap. If you canvas is purely a background color, then you could just drawColor on your canvas. Hope this link helps http://stackoverflow.com/questions/2423327/android-view-ondraw-always-has-a-clean-canvas

Anonymous said...

Can't get this to work at all. My screen stays black with nothing ever changing, even though I do c.drawARGB(255, 255, 255, 255); in onDraw().