Android Camera preview in a fragment
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!
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, 2022Comments
-
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.