片段中的 Android 相机预览
Posted
技术标签:
【中文标题】片段中的 Android 相机预览【英文标题】:Android Camera preview in a fragment 【发布时间】:2014-10-25 14:49:52 【问题描述】:到目前为止,我有一个完整的工作代码,可以插入摄像头以查看前置摄像头的预览。
我现在要做的是让相机在Fragment
内工作。
完整代码:
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;
但是当您运行应用程序时,我的设备中没有显示任何图像。只有一个白屏。请注意,正如我提到的,相机正在一个不包含片段的活动中工作。
那么,为什么主要活动显示为白屏?
PS:Here你可以下载我的代码测试一下。
【问题讨论】:
你能把你的代码贴在这里吗?您的链接已失效且无法正常工作。 【参考方案1】:首先 - 使用 FrameLayout
进行相机预览。
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mainLayout"
android:layout_
android:layout_ />
第二个 - 无需打开相机两次。你的surfaceCreated
方法。
@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);
第三个 - 无需释放相机两次。您是在 Fragment 中完成的,只需将其从 surfaceDestroyed
中删除即可。
@Override
public void surfaceDestroyed(SurfaceHolder holder)
if (mCamera != null)
// mCamera.stopPreview();
// mCamera.release();
在你的片段中。
@Override
public void onPause()
super.onPause();
if (mCamera != null)
mCamera.stopPreview();
mCamera.release();
你会在我看到的片段中看到你的相机预览。祝你好运!
【讨论】:
【参考方案2】:您似乎在CameraExtractionFragment
和CameraExtraction
中有属性cameraId
,但它仅在CameraExtractionFragment
中分配了一个值。
您应该删除CameraExtraction.cameraId
并改用CameraExtractionFragment.cameraId
。
【讨论】:
以上是关于片段中的 Android 相机预览的主要内容,如果未能解决你的问题,请参考以下文章
如何设置 Open GLES2.0 与 Android 相机配合使用?