public class FaceCameraHelper { private static final String TAG = "FaceCameraHelper"; public static final int SUPPORT_DEFAULT_WIDTH = 1920; public static final int SUPPORT_DEFAULT_HEIGHT = 1080; private Camera mCamera; private int mCameraId; private SurfaceHolder mSurfaceHolder; private SurfaceView mSurfaceView; private Activity mContext; private Point mPreviewViewSize; private Camera.Size mSize; private CameraListener mCameraListener; private int mRotateOrientation; private FaceCameraHelper(Builder builder) { mCameraId = builder.cameraId; mSurfaceView = builder.surfaceView; mContext = builder.activity; mPreviewViewSize = builder.previewViewSize; mCameraListener = builder.cameraListener; init(); } public interface CameraListener { void previewData(byte[] data, Camera camera); } public void takePicture(Camera.PictureCallback pictureCallback) { if (mCamera != null && pictureCallback != null) { mCamera.takePicture(null, null, pictureCallback); } } public void startPreview() { if (mCamera != null) { mCamera.startPreview(); } } public void stopPreview() { if (mCamera != null) { mCamera.stopPreview(); } } public static class Builder { //当前使用前/后置摄像头 默认前置 private static final int CURRENT_FACING = Camera.CameraInfo.CAMERA_FACING_FRONT; private int cameraId = CURRENT_FACING; private Activity activity; private SurfaceView surfaceView; private Point previewViewSize; private CameraListener cameraListener; public Builder setCameraListener(CameraListener cameraListener) { this.cameraListener = cameraListener; return this; } public Builder setPreviewViewSize(Point previewViewSize) { this.previewViewSize = previewViewSize; return this; } /** * 摄像头 * * @param cameraId 0:后置 1:前置 * @return 当前对象 */ public Builder setCameraId(int cameraId) { this.cameraId = cameraId; return this; } public Builder setActivity(Activity activity) { this.activity = activity; return this; } public Builder setSurfaceView(SurfaceView surfaceView) { this.surfaceView = surfaceView; return this; } public FaceCameraHelper build() { return new FaceCameraHelper(this); } } private void init() { Log.d(TAG, "init: "); mSurfaceHolder = mSurfaceView.getHolder(); mSurfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { Log.d(TAG, "surfaceCreated: "); // if (mCamera != null) { // try { // mCamera.setPreviewDisplay(holder); // } catch (IOException e) { // e.printStackTrace(); // } // } openCamera(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.d(TAG, "surfaceChanged: "); // if (mSurfaceHolder.getSurface() == null) { // // preview surface does not exist // Log.e(TAG, "mHolder.getSurface() == null"); // return; // } // try { // mCamera.stopPreview(); // } catch (Exception e) { // // ignore: tried to stop a non-existent preview // Log.e(TAG, "Error stopping camera preview: " + e.getMessage()); // } // try { // mCamera.setPreviewDisplay(mSurfaceHolder); // mCamera.startPreview(); // } catch (Exception e) { // // ignore: tried to stop a non-existent preview // Log.d(TAG, "Error starting camera preview: " + e.getMessage()); // } } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.d(TAG, "surfaceDestroyed: "); closeCamera(); } }); } public synchronized void openCamera() { Log.d(TAG, "openCamera: "); if (mCamera == null) { mCamera = Camera.open(mCameraId); } mRotateOrientation = getRotateResult(); mCamera.setDisplayOrientation(mRotateOrientation); Camera.Parameters parameters = mCamera.getParameters(); mSize = parameters.getPreviewSize(); List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes(); if (supportedPreviewSizes != null && supportedPreviewSizes.size() > 0) { mSize = getBestSupportedSize(supportedPreviewSizes, mPreviewViewSize); } parameters.setPreviewSize(mSize.width, mSize.height); parameters.setPictureSize(mSize.width, mSize.height);//需要设置这个参数,不然拍照时有些手机(vivo x21)手机输出的图片非常模糊 //对焦模式设置 List<String> supportedFocusModes = parameters.getSupportedFocusModes(); if (supportedFocusModes != null && supportedFocusModes.size() > 0) { if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); } else if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); } else if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) { parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); } } try { mCamera.setParameters(parameters); } catch (Exception e) { e.printStackTrace(); resolveSamePhoneException(parameters); } try { mCamera.setPreviewDisplay(mSurfaceHolder); } catch (IOException e) { e.printStackTrace(); } mCamera.setPreviewCallback(mPreviewCallback); mCamera.startPreview(); } //个别机型(小米6x)在SupportPreviewSizes里汇报了支持某种预览尺寸,但实际是不支持的,设置进去就会抛出RuntimeException. private void resolveSamePhoneException(Camera.Parameters parameters) { try { //遇到上面所说的情况,只能设置一个最小的预览尺寸 parameters.setPreviewSize(SUPPORT_DEFAULT_WIDTH, SUPPORT_DEFAULT_HEIGHT); mCamera.setParameters(parameters); } catch (Exception e1) { //到这里还有问题,就是拍照尺寸的锅了,同样只能设置一个最小的拍照尺寸 e1.printStackTrace(); try { parameters.setPictureSize(SUPPORT_DEFAULT_WIDTH, SUPPORT_DEFAULT_HEIGHT); mCamera.setParameters(parameters); } catch (Exception ignored) { } } } public int getRotateOrientation() { return mRotateOrientation; } private Camera.PreviewCallback mPreviewCallback = new Camera.PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) { if (mCameraListener != null) { mCameraListener.previewData(data, camera); } } }; public synchronized void closeCamera() { Log.d(TAG, "closeCamera: "); if (mCamera != null) { mCamera.setPreviewCallback(null); mCamera.stopPreview(); mCamera.release(); mCamera = null; } } private Camera.Size getBestSupportedSize(List<Camera.Size> sizes, Point previewViewSize) { if (sizes == null || sizes.size() == 0) { return mCamera.getParameters().getPreviewSize(); } Camera.Size[] tempSizes = sizes.toArray(new Camera.Size[0]); Arrays.sort(tempSizes, new Comparator<Camera.Size>() { @Override public int compare(Camera.Size o1, Camera.Size o2) { if (o1.width > o2.width) { return -1; } else if (o1.width == o2.width) { return o1.height > o2.height ? -1 : 1; } else { return 1; } } }); sizes = Arrays.asList(tempSizes); Camera.Size bestSize = sizes.get(0); float previewViewRatio; if (previewViewSize != null) { previewViewRatio = (float) previewViewSize.x / (float) previewViewSize.y; } else { previewViewRatio = (float) bestSize.width / (float) bestSize.height; } if (previewViewRatio > 1) { previewViewRatio = 1 / previewViewRatio; } boolean isNormalRotate = (0 % 180 == 0); for (Camera.Size s : sizes) { if (mPreviewViewSize != null && mPreviewViewSize.x == s.width && mPreviewViewSize.y == s.height) { return s; } if (isNormalRotate) { if (Math.abs((s.height / (float) s.width) - previewViewRatio) < Math.abs(bestSize.height / (float) bestSize.width - previewViewRatio)) { bestSize = s; } } else { if (Math.abs((s.width / (float) s.height) - previewViewRatio) < Math.abs(bestSize.width / (float) bestSize.height - previewViewRatio)) { bestSize = s; } } } return bestSize; } private int getRotateResult() { Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(mCameraId, info); int rotation = mContext.getWindowManager().getDefaultDisplay().getRotation(); 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; } else { result = (info.orientation - degrees + 360) % 360; } return result; } }
使用:
FaceCameraHelper mFaceCameraHelper = new FaceCameraHelper.Builder() .setCameraId(1)//1:表示前置摄像头 .setPreviewViewSize(new Point(mSurfaceView.getMeasuredWidth(), mSurfaceView.getMeasuredHeight())) .setActivity(this) .setSurfaceView(mSurfaceView) .setCameraListener(null)//数据回调 .build();