Title: Drawing on a canvas using a holder and a separate thread does not show anything on the screen
Tags: java;android;android-canvas;surfaceview
Question: I wanted to use a SurfaceView with a "sticky" canvas. i.e. one that preserves its state and does not get ivalidated. I followed an example that showed how to set up a holder and use a separate thread for the drawing. The idea is that when touching anywhere on the SurfaceView, drawStuff will be called, given some paint, and a random rectangle should be drawn.
Although the code executes perfectly, nothing is drawn at the end.
public class DrawingView extends SurfaceView implements Callback {
Paint currentPaint;
float verts[];
private Boolean drawing = false;
class DrawingThread extends Thread {
private SurfaceHolder surfaceHolder;
private SurfaceView surfaceView;
private Boolean isRunning = false;
public void setIsRunning(Boolean isRunning) {
this.isRunning = isRunning;
}
public DrawingThread(SurfaceHolder surfaceHolder, SurfaceView surfaceView) {
super();
this.surfaceHolder = surfaceHolder;
this.surfaceView = surfaceView;
}
@Override
public void run() {
while(isRunning) {
Canvas c = surfaceHolder.lockCanvas();
surfaceView.draw(c);
surfaceHolder.unlockCanvasAndPost(c);
}
}
DrawingThread thread;
public DrawingView(Context context) {
super(context);
this.setWillNotDraw(false);
this.setBackgroundColor(Color.WHITE);
this.getHolder().addCallback(this);
this.thread = new DrawingThread(getHolder(), this);
@Override
protected void onDraw(Canvas canvas) {
if (drawing) {
// TEST CODE. WILL NEVER USE THIS IN PRODUCTION
canvas.drawRect(new Rect(new Float(Math.random()*200).intValue(), 20, new Float(Math.random()*200).intValue(), 30), currentPaint);
drawing = false;
}
public void drawStuff(Paint paint) {
currentPaint = paint;
drawing = true;
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
@Override
public void surfaceCreated(SurfaceHolder holder) {
thread.setIsRunning(true);
thread.start();
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
thread.setIsRunning(false);
and the paint set up:
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.BLUE);
paint.setAlpha(55);
((DrawingView) v).drawStuff(paint);
//v.invalidate();
return false;


Here is another answer: In the end, you're not actually drawing things when you think you are.  Calling draw() schedules the draw on your UI thread, but doesn't actually call onDraw from your background thread so your Canvas is no longer valid with respect to the surface.  You should render whatever you are doing into a Bitmap object and when you are ready to write to the surface, notify the UI thread.  The UI thread can then lock the surface, draw the bitmap into the Canvas, then unlock and post the Canvas to the surface.

{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 1, 'CommentCount': 0, 'ContentLicense': 'CC BY-SA 3.0', 'CreationDate': '2014:00:01/10/14', 'FavoriteCount': 1.0, 'PostTypeId': 1, 'Tags': '<java><android><android-canvas><surfaceview>', 'Title': 'Drawing on a canvas using a holder and a separate thread does not show anything on the screen', 'ViewCount': 573, 'id': 21042874, 'user_AboutMe': '<p>Making the good old Web meet the new Web, one app at a time.</p>\n<p>A software engineer, entrepreneur, and educator. The tech mind behind <a href="http://murmel.social" rel="nofollow noreferrer">http://murmel.social</a></p>\n', 'user_AccountId': 1118646.0, 'user_DisplayName': 'Preslav Rachev', 'user_Reputation': 3873.0, 'user_Views': 407.0, 'user_id': 1107412.0}}