Android Camera preview in a fragment

14,004

At first - use FrameLayout for your camera preview.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/mainLayout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />

The second - no need to open camera two times. Your surfaceCreated method.

@Override
public void surfaceCreated(SurfaceHolder holder) {
    try {
        if (mCamera != null) {
            mCamera.setPreviewDisplay(holder);
            mCamera.setPreviewCallback(new Camera.PreviewCallback() {
                @Override
                public void onPreviewFrame(byte[] data, Camera camera) {
                }
            });
        }
    } catch (IOException exception) {
        Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
    }
}

The third - no need to release camera two times. You did it in Fragment, just remove it from surfaceDestroyed.

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (mCamera != null)
        {
//          mCamera.stopPreview();
//          mCamera.release();
        }
    }

And in your fragment.

@Override
public void onPause() {
    super.onPause();

    if (mCamera != null)
    {
        mCamera.stopPreview();
        mCamera.release();
    }
}

And you will see your camera preview in a fragmentas I see. Good luck!

Share:
14,004
Sonhja
Author by

Sonhja

Partially programmer, partially designer! An WPF lover, Android developer and .Net contributor! Learning more and more everyday :) And happy to help others work!

Updated on June 21, 2022

Comments

  • Sonhja
    Sonhja almost 2 years

    Until now I have a full working code that plugs in a camera to see the preview of the front camera.

    What I'm trying to do now is to get that camera working inside a Fragment.

    Full code:

    MainActivity.java

    public class MainActivity extends Activity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        setContentView(R.layout.fragment_main);
    
        getFragmentManager().beginTransaction().add(R.id.mainLayout, new CameraExtractionFragment()).commit();
    }
    }
    

    CameraExtractionFragment.java

    public class CameraExtractionFragment extends Fragment {
    
    private CameraExtraction mCameraExtraction;
    Camera mCamera;
    int mNumberOfCameras;
    int cameraId;
    int rotation;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        mCameraExtraction = new CameraExtraction(
                this.getActivity().getBaseContext(), 
                this.getActivity().getWindowManager().getDefaultDisplay().getRotation()
                );
    
        // Find the total number of cameras available
        mNumberOfCameras = Camera.getNumberOfCameras();
    
        // Find the ID of the rear-facing ("default") camera
        CameraInfo cameraInfo = new CameraInfo();
        for (int i = 0; i < mNumberOfCameras; i++) {
            Camera.getCameraInfo(i, cameraInfo);
            if (cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT) {
                cameraId = i;
            }
        }
    }
    
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)               {
         return mCameraExtraction;
     }
    
     @Override
    public void onResume() {
        super.onResume();
    
        // Use mCurrentCamera to select the camera desired to safely restore
        // the fragment after the camera has been changed
        mCamera = Camera.open(cameraId);
        mCameraExtraction.setCamera(mCamera);
    }
    
    @Override
    public void onPause() {
        super.onPause();
    
        if (mCamera != null)
        {
            mCamera.release();
        }
    }
    
    
    // Modo en el que se pinta la cámara: encajada por dentro o saliendo los bordes por fuera.
    public enum CameraViewMode {
    
        /**
         * Inner mode
         */
        Inner,
        /**
         * Outer mode 
         */
        Outer
    }
    }
    

    CameraExtraction.java

    public class CameraExtraction extends ViewGroup implements SurfaceHolder.Callback {
    
     private final String TAG = "CameraExtraction";
    
    Camera mCamera;
    SurfaceHolder mHolder;
    SurfaceView mSurfaceView;
    int mNumberOfCameras;
    int cameraId;
    Rect desiredSize;
    CameraViewMode cameraViewMode;
    boolean mSurfaceCreated = false;
    List<Size> mSupportedPreviewSizes;
    int rotation;
    Size mPreviewSize;
    
    public CameraExtraction(Context context, int rotation) {
        super(context);
    
        this.rotation = rotation;
    
        mSurfaceView = new SurfaceView(context);
    
        addView(mSurfaceView);
    
        // Install a SurfaceHolder.Callback so we get notified when the
        mHolder = mSurfaceView.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    
        cameraViewMode = CameraViewMode.Inner;
    }
    
    public void setCamera(Camera camera) {
        mCamera = camera;
        if (mCamera != null) {
            mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
            if (mSurfaceCreated) requestLayout();
        }
    }
    
    public void switchCamera(Camera camera) {
        setCamera(camera);
        try {
            camera.setPreviewDisplay(mHolder);
        } catch (IOException exception) {
            Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
        }
    }
    
    @SuppressLint("DrawAllocation")
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mSurfaceView == null ||mSurfaceView.getHolder() == null) return;
    
        if (mSurfaceView.getHolder().getSurface() == null) {
            // preview surface does not exist
            return;
        }
    
        final int width = resolveSize(getSuggestedMinimumWidth(),
                widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(),
                heightMeasureSpec);
        setMeasuredDimension(width, height);
    
        if (mSupportedPreviewSizes != null) {
    
            mPreviewSize = getNearestPreviewSize(mCamera.new Size(widthMeasureSpec,heightMeasureSpec));
        }
    
        if (mCamera != null) {
          Camera.Parameters parameters = mCamera.getParameters();
          parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
    
          mCamera.setParameters(parameters);
        }
    }
    
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (getChildCount() > 0) {
            final View child = getChildAt(0);
    
            final int width = r - l;
            final int height = b - t;
    
            int previewWidth = width;
            int previewHeight = height;
            if (mPreviewSize != null) {
                previewWidth = mPreviewSize.width;
                previewHeight = mPreviewSize.height;
            }
    
            // Center the child SurfaceView within the parent.
            if (width * previewHeight > height * previewWidth) {
                final int scaledChildWidth = previewWidth * height
                        / previewHeight;
                child.layout((width - scaledChildWidth) / 2, 0,
                        (width + scaledChildWidth) / 2, height);
            } else {
                final int scaledChildHeight = previewHeight * width
                        / previewWidth;
                child.layout(0, (height - scaledChildHeight) / 2, width,
                        (height + scaledChildHeight) / 2);
            }
        }       
    }
    
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mCamera = Camera.open(cameraId);        
    }
    
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
        if (mSurfaceView == null || mSurfaceView.getHolder() == null) return;
    
                if (mSurfaceView.getHolder().getSurface() == null) {
                    // preview surface does not exist
                    return;
                }
    
                // set preview size and make any resize, rotate or
                // reformatting changes here
                Camera.Parameters param = mCamera.getParameters();
                Point previewSize = new Point(640,480);
    
                Camera.Size size = getNearestPreviewSize(mCamera.new Size(previewSize.x,previewSize.y));
                param.setPreviewSize(size.width, size.height);
                mCamera.setParameters(param);
                rotation = setCameraDisplayOrientation(cameraId, mCamera);
    
                // start preview with new settings
                try {
                    mCamera.setPreviewCallback(new Camera.PreviewCallback() {
    
                        @Override
                        public void onPreviewFrame(byte[] data, Camera camera) {
                            // TODO Auto-generated method stub
    
                        }
                    });
                    mCamera.setPreviewDisplay(mSurfaceView.getHolder());
                    mCamera.startPreview();
    
                } catch (Exception e) {
                    Log.d("AndroidControlSurfaceView",
                            "Error starting camera preview: " + e.getMessage());
                }       
    }
    
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (mCamera != null)
        {
            mCamera.stopPreview();
            mCamera.release();
        }
    }
    
    
    protected Rect getCameraViewSizeCompensated(Camera.Size cameraPreviewSize, Point hostViewSize) {
        Rect toReturn=null;
    
        float ratioWidth = hostViewSize.x / (float)cameraPreviewSize.width;
        float ratioHeight = hostViewSize.y / (float)cameraPreviewSize.height;
    
        switch (cameraViewMode){
        case Inner:
            if (ratioWidth < ratioHeight) {
                int newHeight = (int)(cameraPreviewSize.height*ratioWidth);
                int y = (hostViewSize.y - newHeight) / 2;
                toReturn = new Rect(0, y, hostViewSize.x, y+newHeight);
            } else {
                int newWidth = (int)(cameraPreviewSize.width*ratioHeight);
                int x = (hostViewSize.x - newWidth) / 2;
                toReturn = new Rect(x, 0, x+newWidth,hostViewSize.y);
            }
            break;
        case Outer:
            if (ratioWidth < ratioHeight) {
                int newWidth = (int)(cameraPreviewSize.width*ratioHeight);
                int x = (hostViewSize.x - newWidth) / 2;
                toReturn = new Rect(x, 0, x+newWidth,hostViewSize.y);
            } else {
                int newHeight = (int)(cameraPreviewSize.height*ratioWidth);
                int y = (hostViewSize.y - newHeight) / 2;
                toReturn = new Rect(0, y, hostViewSize.x, y+newHeight);
            }
            break;
        }
        return toReturn;
    }
    
    private Camera.Size getNearestPreviewSize(Camera.Size size) {
      List<Camera.Size> availableSizes =  mCamera.getParameters().getSupportedPreviewSizes();
      if (availableSizes == null || availableSizes.size() <= 0) return null;
    
      Camera.Size toReturn = availableSizes.get(0);
      int distance = Math.abs(size.width*size.height - toReturn.width*toReturn.height);
      for (int a=1; a<availableSizes.size(); a++) {
          int temp = Math.abs(size.width*size.height - availableSizes.get(a).width*availableSizes.get(a).height);
          if (temp < distance) {
              distance = temp;
              toReturn = availableSizes.get(a);
          }
      }
      return toReturn;
     }
    
    
    public int setCameraDisplayOrientation(int cameraId, android.hardware.Camera camera) {
    
         CameraInfo info = new Camera.CameraInfo();
         Camera.getCameraInfo(cameraId, info);
         int degrees = 0;
    
         switch (rotation) {
             case Surface.ROTATION_0: degrees = 0; break;
             case Surface.ROTATION_90: degrees = 90; break;
             case Surface.ROTATION_180: degrees = 180; break;
             case Surface.ROTATION_270: degrees = 270; break;
         }
    
         int result;
         if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
             result = (info.orientation + degrees) % 360;
             result = (360 - result) % 360;  // compensate the mirror
         } else {  // back-facing
             result = (info.orientation - degrees + 360) % 360;
         }
         camera.setDisplayOrientation(result);
         return result/90;
     }
    
    }
    

    But when you run the application, no image is being showed in my device. Only a white screen. Note that, as I mentioned the camera is working in an activity not containing fragments.

    So, why the main activity is shown with a white screen?

    PS: Here you can download my code and test it.