39

I'm trying to modify the SurfaceView I use for doing a camera preview in order to display an overlaying square. However, the onDraw method of the extended SurfaceView is never called.

Here is the source :

public class CameraPreviewView extends SurfaceView {

    protected final Paint rectanglePaint = new Paint();

    public CameraPreviewView(Context context, AttributeSet attrs) {
        super(context, attrs);
        rectanglePaint.setARGB(255, 200, 0, 0);
        rectanglePaint.setStyle(Paint.Style.FILL);
        rectanglePaint.setStrokeWidth(2);
    }

    @Override
    protected void onDraw(Canvas canvas){
        canvas.drawRect(new Rect(10,10,200,200), rectanglePaint);
        Log.w(this.getClass().getName(), "On Draw Called");
    }
}

public class CameraPreview extends Activity implements SurfaceHolder.Callback{

    private SurfaceHolder holder;
    private Camera camera;

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);

        // We remove the status bar, title bar and make the application fullscreen
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        // We set the content view to be the layout we made
        setContentView(R.layout.camera_preview);

        // We register the activity to handle the callbacks of the SurfaceView
        CameraPreviewView surfaceView = (CameraPreviewView) findViewById(R.id.camera_surface);
        holder = surfaceView.getHolder();

        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {

        Camera.Parameters params = camera.getParameters();

        params.setPreviewSize(width, height);
        camera.setParameters(params);

        try {
            camera.setPreviewDisplay(holder);
        } catch (IOException e) {
            e.printStackTrace();
        }

        camera.startPreview();

    }

    public void surfaceCreated(SurfaceHolder holder) {
        camera = Camera.open();
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        camera.stopPreview();
        camera.release();   
    }


}
Gab Royer
  • 9,587
  • 8
  • 40
  • 58
  • For the record, I'm saying onDraw is never called based on the fact that the debugger never stops on a breakpoint in the method as opposed to a breakpoint in the constructor. The camera preview, on the other hand, is displaying correctly. – Gab Royer Apr 21 '10 at 22:31

4 Answers4

83

Found it on the android-developers Google group. You simply have to add :

setWillNotDraw(false)

To the constructor. Now if someone could explain me why, that would be greatly appreciated.

Gab Royer
  • 9,587
  • 8
  • 40
  • 58
  • Wow, thanks for this, I was wondering why my View's onDraw() method was never called. I see the javadoc says "Typically, if you override onDraw(Canvas) you should clear this flag.", so probably if you have a View without any widgets it seems like this needs to be set. Just a guess :) – Shawn Lauzon Jan 16 '11 at 18:06
  • Oh god thanks for the answer, I had a different problem but it also stemmed from not setting this flag. Had been searching for AGES for a solution. – user272043 Sep 04 '11 at 13:16
  • 10
    I think SurfaceView has setWillNotDraw set to false by default because it's really meant to be updated from another thread (using getHolder().lockCanvas()). Having additional drawing in onDraw would be inefficient. If you only need onDraw, you can derive straight from android.view.View – Juozas Kontvainis Sep 14 '12 at 09:40
27

Minor correction:

Adding setWillNotDraw(false) to the constructor will cause crashes, because the underlying Surface object is not created yet.

Instead, put the setWillNotDraw(false) statement into the surfaceCreated() method. This delays the call until there's a real object to work with, and works properly.

(and many thanks to the users who posted this solution, it solved a major problem for me)

dreambig
  • 271
  • 3
  • 4
  • While it's fairly common for people to subclass `SurfaceView` and have it implement `SurfaceHolder.Callback`, it isn't required; doing it in `surfaceCreated` is only an option if their class does implement it. On a related note, what circumstances will calling `setWillNotDraw(false)` in the constructor cause a crash? – Brian Vandenberg Sep 06 '12 at 07:56
7

Romain Guy explained it:

For efficiency, layouts do not get their onDraw() method called. To enable it, call setWillNotDrawEnabled(false) (or set the equivalent XML attribute to false.)

Rahul Sharma
  • 2,867
  • 2
  • 27
  • 40
HostedMetrics.com
  • 3,525
  • 3
  • 26
  • 31
4

Based on all above comments I used :

Add setWillNotDraw(false) in the onAttachedToWindow() event.

@Override protected void onAttachedToWindow() { super.onAttachedToWindow(); setWillNotDraw(false); }

and it worked.

M.Hefny
  • 2,677
  • 1
  • 26
  • 30