本篇总结了利用Camera API在Android5.0版本以下开发相机:
本文参考文章http://https://yq.aliyun.com/articles/26706#
相机开发首先需要在Manifanst.xml文件总添加相机权限,以及自动对焦功能,Android6.0以上的需要动态分配权限;
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="true"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
相机预览界面一般使用SurfaceView存放,可以自定义一个继承SurfaceView控件作为相机控件,并实现SurfaceHolder.Callback接口,通过SurfaceView状态控制相机状态;
SurfaceHolder.Callback会在SurfaceView创建后分别回调surfaceCreated(),surfaceChanged,surfaceDestroyed三个方法;相机将在surfaceCreated回调的时候打开,并设置相机预览显示在SurfaceView中,开启相机代码如下:
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (mCamera == null) {
mCamera = Camera.open();
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
}
}
在开启相机预览前,为了使预览界面长宽比跟手机设备屏幕比相同,需要先设置相机各种设置参数,比如设置相机分辨率,图片分辨率等等。还可以设置照片质量,相机是否自动对焦等等;设置相机参数代码如下:
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.i(TAG, "surfaceChanged");
setCameraParams(mCamera, mScreenWidth, mScreenHeight);
mCamera.startPreview();
}
/**
* 设置相机参数
* @param camera 相机
* @param width 目标宽度
* @param height 目标高度
*/
private void setCameraParams(Camera camera, int width, int height) {
Log.i(TAG,"setCameraParams width="+width+" height="+height);
//返回当前相机设置参数
Camera.Parameters parameters = camera.getParameters();
//获取当前相机支持相片大小,并以队列形式返回
List<Camera.Size> pictureSizeList = parameters.getSupportedPictureSizes();
/**从列表中选取合适的分辨率*/
Camera.Size picSize = getProperSize(pictureSizeList, ((float) height / width));
if (null == picSize) {
picSize = parameters.getPictureSize();
}
Log.i(TAG, "picSize.width=" + picSize.width + " picSize.height=" + picSize.height);
float w = picSize.width;
float h = picSize.height;
//设置相片宽度和高度
parameters.setPictureSize(picSize.width,picSize.height);
this.setLayoutParams(new FrameLayout.LayoutParams((int) (height*(h/w)), height));
//获取当前相机支持预览界面大小,并以队列形式返回
List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes();
Camera.Size preSize = getProperSize(previewSizeList, ((float) height) / width);
if (null != preSize) {
Log.i(TAG, "preSize.width=" + preSize.width + " preSize.height=" + preSize.height);
parameters.setPreviewSize(preSize.width, preSize.height);
}
//设置相机拍摄图片质量,数值范围1~100,数值越大图片质量越高
parameters.setJpegQuality(100);
//如果相机支持自动对焦,则开启
if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
parameters.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
}
mCamera.cancelAutoFocus();//自动对焦。
// 设置PreviewDisplay的方向,将捕获的画面旋转多少度显示
mCamera.setDisplayOrientation(90);
mCamera.setParameters(parameters);
}
/**
* 从列表中选取合适的分辨率
* 默认w:h = 4:3
* <p>注意:这里的w对应屏幕的height, h对应屏幕的width<p/>
*/
private Camera.Size getProperSize(List<Camera.Size> pictureSizeList, float screenRatio) {
Log.i(TAG, "screenRatio=" + screenRatio);
Camera.Size result = null;
for (Camera.Size size : pictureSizeList) {
float currentRatio = ((float) size.width) / size.height;
if (currentRatio - screenRatio == 0) {
result = size;
break;
}
}
if (null == result) {
for (Camera.Size size : pictureSizeList) {
float curRatio = ((float) size.width) / size.height;
if (curRatio == 4f / 3) {// 默认w:h = 4:3
result = size;
break;
}
}
}
return result;
}
相机拍照则调用camera.takePicture(ShutterCallback shutter, PictureCallback raw,PictureCallback postview, PictureCallback jpeg),其中postview为传null,源码如下:
/**
* Triggers an asynchronous image capture. The camera service will initiate
* a series of callbacks to the application as the image capture progresses.
* The shutter callback occurs after the image is captured. This can be used
* to trigger a sound to let the user know that image has been captured. The
* raw callback occurs when the raw image data is available (NOTE: the data
* will be null if there is no raw image callback buffer available or the
* raw image callback buffer is not large enough to hold the raw image).
* The postview callback occurs when a scaled, fully processed postview
* image is available (NOTE: not all hardware supports this). The jpeg
* callback occurs when the compressed image is available. If the
* application does not need a particular callback, a null can be passed
* instead of a callback method.
*
* <p>This method is only valid when preview is active (after
* {@link #startPreview()}). Preview will be stopped after the image is
* taken; callers must call {@link #startPreview()} again if they want to
* re-start preview or take more pictures. This should not be called between
* {@link android.media.MediaRecorder#start()} and
* {@link android.media.MediaRecorder#stop()}.
*
* <p>After calling this method, you must not call {@link #startPreview()}
* or take another picture until the JPEG callback has returned.
*
* @param shutter the callback for image capture moment, or null
* @param raw the callback for raw (uncompressed) image data, or null
* @param postview callback with postview image data, may be null
* @param jpeg the callback for JPEG image data, or null
*/
public final void takePicture(ShutterCallback shutter, PictureCallback raw,
PictureCallback postview, PictureCallback jpeg) {
//源码具体实现
}
ShutterCallback是监听快门按下的回调。后面三个PictureCallback接口参数,分别对应三份图像数据,分别是原始图像、缩放和压缩图像和JPG图像,图像数据可以在PictureCallback接口的void onPictureTaken(byte[] data, Camera camera)中获得,三份数据相应的三个回调正好按照参数顺序调用,通常我们只关心JPG图像数据,此时前面两个PictureCallback接口参数可以直接传null。
每次调用takePicture方法拍照后,摄像头会停止预览,假如需要继续拍照,则需要在上面的PictureCallback的onPictureTaken函数末尾,再调Camera::startPreview()。
最后,如果需要关闭相机,或者关闭当前拍照页面,需要先释放相机资源,代码如下:
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.stopPreview();//停止预览
mCamera.release();//释放相机资源
mCamera = null;
holder = null;
}
Android5.0以下版本开发相机,具体源码可参考:Android5.0以下版本