4
votes

I used to do my drawing in an ImageView in the onDraw method. However, I've learnt that's better to draw the canvas outside of the onDraw and just update the canvas in onDraw.

I know this is clearly wrong (because it's not working) but how would I accomplish what I'm trying to do:

@Override 
public void onDraw(Canvas c) {
  c = this.newCanvas;
  super.onDraw(c);
}
2

2 Answers

1
votes
    public class GameLoopThread extends Thread {
private GameView view;
private boolean running = false;

public GameLoopThread(GameView view) {
      this.view = view;
}

public void setRunning(boolean run) {
      running = run;
}

@Override
public void run() {

      while (running) {
             Canvas c = null;

             try {
                    c = view.getHolder().lockCanvas();
                    synchronized (view.getHolder()) {
                        if (c != null) {
                            view.onDraw(c);
                        }

                    }
             } finally {
                    if (c != null) {
                           view.getHolder().unlockCanvasAndPost(c);
                    }
             }

             try {

                           sleep(10);
             } catch (Exception e) {}
      }
}
}

make that thread then in your activity do something like this

    @Override
public void onCreate(Bundle savedInstanceState) {
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
    WindowManager.LayoutParams.FLAG_FULLSCREEN);
    super.onCreate(savedInstanceState);
    setContentView(new GameView(GameActivity.this));
}

then in a GameViewClass do something like this

   public class GameView extends SurfaceView {

   private SurfaceHolder holder;
   private GameLoopThread gameLoopThread;


   public GameView(Context context) {
         super(context);
         gameLoopThread = new GameLoopThread(this);
         holder = getHolder();
         holder.addCallback(new SurfaceHolder.Callback() {

                @Override
                public void surfaceDestroyed(SurfaceHolder holder) {
                       boolean retry = true;
                       gameLoopThread.setRunning(false);
                       while (retry) {
                              try {
                                    gameLoopThread.join();
                                    retry = false;
                              } catch (InterruptedException e) {
                              }
                       }
                }

                @Override
                public void surfaceCreated(SurfaceHolder holder) {
                       gameLoopThread.setRunning(true);
                       gameLoopThread.start();
                }

                @Override
                public void surfaceChanged(SurfaceHolder holder, int format,
                              int width, int height) {
                }
         });

   }

   @Override
   protected void onDraw(Canvas canvas) {
         //Do Drawing
   }
}

The important things here is that the thread is manually auto calling the onDraw() method repeatedly, and that you are locking a canvas, drawing on it, and then posting it. If you dont need a super fast refresh rate then you might be better off doing something like this:

    @Override 
    public void onDraw(Canvas c) {
        c = this.getHolder().lockCanvas();
        if (c != null) {
           //draw on canvas
        }
        if (c != null) {
            this.getHolder().unlockCanvasAndPost(c);
        }
    }

I just dont know if that last bit there will work, never tested it. also if you want to do your drawing outside the on draw method, you could run your updating (drawing on your canvas) in a thread, and every time the onDraw method is called have it check to see if the Canvas is ready for it to post. for example have your thread have a boolean that once the canvas gets pulled it is set to false, so the thread will draw you a new one, but once it is done drawing set the boolean to true. in the ondraw method check to see if the boolean is true and if it is pull the canvas.

1
votes

A Canvas is just a handle for drawing onto something -- you need to get at the something itself. The Canvas that you draw into outside of onDraw() needs to be backed by a Bitmap. Then in onDraw(), simply draw that Bitmap into the Canvas provided:

Bitmap my_bitmap = null; /* this needs to be initialized whereever it is drawn into */
@Override
public void onDraw(Canvas c) {
   if (my_bitmap != null) {
      c.drawBitmap(my_bitmap, 0.0, 0.0, null);
   }
}

onSizeChanged() would be a reasonable place to initialize the Bitmap, because then you know its size:

@Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
   my_bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
}

And to draw on my_bitmap, just make a new Canvas with:

Canvas c = new Canvas(my_bitmap);