How to draw an overlay on a SurfaceView used by Camera on Android?

AndroidCameraSurfaceview

Android Problem Overview


I have a simple program that draws the preview of the Camera into a SurfaceView. What I'm trying to do is using the onPreviewFrame method, which is invoked each time a new frame is drawn into the SurfaceView, in order to execute the invalidate method which is supposed to invoke the onDraw method. In fact, the onDraw method is being invoked, but nothing there is being printed (I guess the camera preview is overwriting the text I'm trying to draw).

This is a simplify version of the SurfaceView subclass I have:

public class Superficie extends SurfaceView implements SurfaceHolder.Callback {
 SurfaceHolder mHolder;
 public Camera camera;
 Superficie(Context context) {
  super(context);
  mHolder = getHolder();
  mHolder.addCallback(this);
  mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
 }
 public void surfaceCreated(final SurfaceHolder holder) {
  camera = Camera.open();
  try {
   camera.setPreviewDisplay(holder);
   camera.setPreviewCallback(new PreviewCallback() {
    public void onPreviewFrame(byte[] data, Camera arg1) {
     invalidar();
    }
   });
  } catch (IOException e) {}
 }
 public void invalidar(){
  invalidate();
 }
 public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
  Camera.Parameters parameters = camera.getParameters();
  parameters.setPreviewSize(w, h);
  camera.setParameters(parameters);
  camera.startPreview();
 }
 @Override
 public void draw(Canvas canvas) {
  super.draw(canvas);
  // nothing gets drawn :(
  Paint p = new Paint(Color.RED);
  canvas.drawText("PREVIEW", canvas.getWidth() / 2,
    canvas.getHeight() / 2, p);
 }
}

Android Solutions


Solution 1 - Android

SurfaceView probably does not work like a regular View in this regard.

Instead, do the following:

  1. Put your SurfaceView inside of a FrameLayout or RelativeLayout in your layout XML file, since both of those allow stacking of widgets on the Z-axis
  2. Move your drawing logic into a separate custom View class
  3. Add an instance of the custom View class to the layout XML file as a child of the FrameLayout or RelativeLayout, but have it appear after the SurfaceView

This will cause your custom View class to appear to float above the SurfaceView.

Solution 2 - Android

Try calling setWillNotDraw(false) from surfaceCreated:

public void surfaceCreated(SurfaceHolder holder) {
	try {
		setWillNotDraw(false); 
		mycam.setPreviewDisplay(holder);
		mycam.startPreview();
	} catch (Exception e) {
		e.printStackTrace();
		Log.d(TAG,"Surface not created");
	}
}

@Override
protected void onDraw(Canvas canvas) {

    canvas.drawRect(area, rectanglePaint);
    Log.w(this.getClass().getName(), "On Draw Called");
}

and calling invalidate from onTouchEvent:

public boolean onTouch(View v, MotionEvent event) {

	invalidate();
	return true;
}

Solution 3 - Android

I think you should call the super.draw() method first before you do anything in surfaceView's draw method.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCristianView Question on Stackoverflow
Solution 1 - AndroidCommonsWareView Answer on Stackoverflow
Solution 2 - Androidmchouhan_googleView Answer on Stackoverflow
Solution 3 - AndroidDevashish KunalView Answer on Stackoverflow