最近项目中一直在调用系统相机拍照,而产品总是要求拍照页面向iOS看齐,真是很头疼啊。但问题还是存在,有必要搞一个自定义的啦。在网上搜索了一段时间后,发现还真不是那么好实现的,实践之后,总结一些要点如下:
1.camera的parameters设置总是会提示出setParamters failed的异常
2.预览和图片的尺寸设置
3.预览图片变形
4.自动聚焦
5.保存的图片旋转角度
以上是我遇到的难点,并将之一一解决后的总结。
第1个问题,setPreviewFrameRate和setPreviewSize不能放在一起使用,否则set的时候必然报错,这个也是在查看了源代码后发现的。
第2个问题,每个手机都有自己支持的尺寸集合,并不是设置一个固定的宽高就可以的,所以要想设置尺寸,一定是先获取手机支持的尺寸,而且排序也不一样,有的从大到小,有的从小到大。没办法,开源嘛。
第3个问题就是surfaceview的尺寸大小,设置的不对的话,会导致预览变形,这个要和预览尺寸配合着设置
第4个问题比较容易解决一点,网上有很多种设置方式,但是最好用的就是使用重力加速度类型的传感器来实现,这个效果是最好的,在配合上触屏操作时也加上自动聚焦功能,就能保证自动聚焦这个功能完美的实现了。
第5个问题就是拍照会转动手机在各种角度拍照,那么获取的图片显示的时候有要求的需要根据角度重新生成bitmap,然后再显示啦。这个使用Matrix类就能实现,但是难点在于如何获取到正确的旋转角度。通常都会使用getWindowManager().getDefaultDisplay().getRotation();这个方法,但是当我将这个相机Activity设置成portrait时,这个方法是无效的,因此必须使用OrientationEventListener这个监听器,其实这个也是利用传感器实现的方向监听。
好了,以上总是我遇到的一些问题,以及一些总结,下面直接贴出代码,就不上传工程啦。所有的代码都写在一个activity里面了,还没有做架构抽象,感兴趣的小伙伴可以抽象一下,反馈给我,呵呵。
CameraManager.java
import android.content.Context; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; import android.hardware.Camera; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; import android.view.SurfaceHolder; import android.widget.Toast; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * Created by zouguibao on 2017/11/23. */ public class CameraManger { private static CameraManger instance; private Camera camera; private MediaPlayer mShootSound; public static final int TYPE_PREVIEW = 0; public static final int TYPE_PICTURE = 1; public static final int ALLOW_PIC_LEN = 2000; //最大允许的照片尺寸的长度 宽或者高 private int cameraPosition; private CameraManger() { } public static CameraManger getInstance() { if (instance == null) { instance = new CameraManger(); } return instance; } /** * 打开摄像头 * * @param holder * @param autoFocusCallback * @param degree */ public void openCamera(SurfaceHolder holder, Camera.AutoFocusCallback autoFocusCallback, int degree) { try { //初始化摄像头 cameraPosition = Camera.CameraInfo.CAMERA_FACING_BACK; // 打开摄像头 camera = Camera.open(cameraPosition); // 设置用于显示拍照影像的SurfaceHolder对象 camera.setPreviewDisplay(holder); camera.setDisplayOrientation(degree); camera.autoFocus(autoFocusCallback); } catch (Exception e) { // e.printStackTrace(); camera.release(); camera = null; } } /** * 设置参数 */ public void setCameraParameters(int screenWidth, int screenHeight) { try { if (camera != null) { Camera.Parameters parameters = camera.getParameters();//获取各项参数 Camera.Size previewSize = findFitPreResolution(parameters); parameters.setPreviewSize(previewSize.width, previewSize.height);// 设置预览大小 parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); parameters.setPictureFormat(PixelFormat.JPEG);//设置图片格式 //不能与setPreviewSize一起使用,否则setParamters会报错 // parameters.setPreviewFrameRate(5);//设置每秒显示4帧 parameters.setJpegQuality(80);// 设置照片质量 Camera.Size pictureSize = null; if (equalRate(screenWidth, screenHeight, 1.33f)) { pictureSize = findFitPicResolution(parameters, (float) 4 / 3); } else { pictureSize = findFitPicResolution(parameters, (float) 16 / 9); } parameters.setPictureSize(pictureSize.width, pictureSize.height);// 设置保存的图片尺寸 camera.setParameters(parameters); camera.startPreview(); } } catch (Exception e) { e.printStackTrace(); } } private boolean equalRate(int width, int height, float rate) { float r = (float) width / (float) height; if (Math.abs(r - rate) <= 0.2) { return true; } else { return false; } } /** * 摄像头切换 * * @param holder * @param autoFocusCallback * @param degree */ public void turnCamera(SurfaceHolder holder, Camera.AutoFocusCallback autoFocusCallback, int degree, int screenWidth, int screenHeight) { //切换前后摄像头 //现在是后置,变更为前置 if (camera != null && cameraPosition == Camera.CameraInfo.CAMERA_FACING_BACK) { camera.stopPreview();//停掉原来摄像头的预览 camera.release();//释放资源 camera = null;//取消原来摄像头 camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);//打开当前选中的摄像头 try { camera.setPreviewDisplay(holder);//通过surfaceview显示取景画面 camera.setDisplayOrientation(degree); camera.autoFocus(autoFocusCallback); setCameraParameters(screenWidth, screenHeight); } catch (IOException e) { e.printStackTrace(); } camera.startPreview();//开始预览 cameraPosition = Camera.CameraInfo.CAMERA_FACING_FRONT; DataUtils.isBackCamera = false; } else if (cameraPosition == Camera.CameraInfo.CAMERA_FACING_FRONT) { //代表摄像头的方位,CAMERA_FACING_FRONT前置 // CAMERA_FACING_BACK后置 //现在是前置, 变更为后置 camera.stopPreview();//停掉原来摄像头的预览 camera.release();//释放资源 camera = null;//取消原来摄像头 camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);//打开当前选中的摄像头 try { camera.setPreviewDisplay(holder);//通过surfaceview显示取景画面 camera.setDisplayOrientation(degree); camera.autoFocus(autoFocusCallback); setCameraParameters(screenWidth, screenHeight); } catch (IOException e) { e.printStackTrace(); } camera.startPreview();//开始预览 cameraPosition = Camera.CameraInfo.CAMERA_FACING_BACK; DataUtils.isBackCamera = true; } } public void setCameraZoom(int scale) { Camera.Parameters parameters = camera.getParameters(); int zoom = parameters.getZoom() + scale; if (zoom < 0) zoom = 0; if (zoom > parameters.getMaxZoom()) zoom = parameters.getMaxZoom(); parameters.setZoom(zoom); camera.setParameters(parameters); } public boolean setCameraFocusAreas(Point point) { if (camera == null) { return false; } Camera.Parameters parameters = null; try { parameters = camera.getParameters(); } catch (Exception e) { e.printStackTrace(); return false; } //不支持设置自定义聚焦,则使用自动聚焦,返回 if (parameters.getMaxNumFocusAreas() <= 0) { return false; } Listareas = new ArrayList (); int left = point.x - 300; int top = point.y - 300; int right = point.x + 300; int bottom = point.y + 300; left = left < -1000 ? -1000 : left; top = top < -1000 ? -1000 : top; right = right > 1000 ? 1000 : right; bottom = bottom > 1000 ? 1000 : bottom; areas.add(new Camera.Area(