这一节里面我讲述一下如何利用framework的API直接控制相机
直接去控制相机,相比于利用相机拍照或者录像,需要更多的代码。如果你想创建一个专业的相机APP或者是在你的UI里面完全的集成相机,这节里面会教你如何实现它。
创建一个相机对象
首先要获取到一个相机的实例。如android自身的相机APP里面一样,在一个独立的线程里面用打开相机的方法来得到这个Camera的实例,一般在onCreate函数里面进行。这种方式是防止这个动作会产生耗时,导致UI线程假死。在更多的实现方法中,打开相机的动作可以在onResume里面实现,方法代码的重利用和相机的一系列控制动作。
调用Camera.open()的时候,如果相机在被另外一个程序利用,会抛出异常,所以要捕获这个异常:
private boolean safeCameraOpen(int id) {
boolean qOpened = false;
try {
releaseCameraAndPreview();
mCamera = Camera.open(id);
qOpened = (mCamera != null);
} catch (Exception e) {
Log.e(getString(R.string.app_name), "failed to open Camera");
e.printStackTrace();
}
return qOpened;
}
private void releaseCameraAndPreview() {
mPreview.setCamera(null);
if (mCamera != null) {
mCamera.release();
mCamera = null;
}
}
在API 9之后,支持多相机。如果你使用的是之前的API,直接用没有参数的open()函数,你会得到一个后置的Camera对象。
创建相机预览视图
要拍摄一张照片之前,我们需要看到照片当前的预览图,在我们按下拍照按钮之前。可以利用SurfaceView来绘制当前相机传感器捕捉到的画面。
1.Preview类
要显示一个预览视图,那就需要一个对应的Preview类。这个类需要实现一个android.view.SurfaceHolder.Callback接口,用来专递相机捕获的图像数据给应用程序。
class Preview extends ViewGroup implements SurfaceHolder.Callback {
SurfaceView mSurfaceView;
SurfaceHolder mHolder;
Preview(Context context) {
super(context);
mSurfaceView = new SurfaceView(context);
addView(mSurfaceView);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = mSurfaceView.getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
...
}
在播放实时的画面之前,必须给这个类传递一个Camera对象,下面会讲到
2.设置和启动Preview
Camera对象和相关联的Preview必须按照指定的规则来创建,Camera是第一个被创建出来的。
下面的代码片段中,相机的初始化过程是被封装的,这样当用户改变相机的时候,setCamera()里面调用了Camera.startPreview()方法。preview也必须在它的类方法
surfaceChanged()
里面重新启动。
public void setCamera(Camera camera) {
if (mCamera == camera) { return; }
stopPreviewAndFreeCamera();
mCamera = camera;
if (mCamera != null) {
List<Size> localSizes = mCamera.getParameters().getSupportedPreviewSizes();
mSupportedPreviewSizes = localSizes;
requestLayout();
try {
mCamera.setPreviewDisplay(mHolder);
} catch (IOException e) {
e.printStackTrace();
}
/*
Important: Call startPreview() to start updating the preview surface. Preview must
be started before you can take a picture.
*/
mCamera.startPreview();
}
}
修改相机设置
相机的设置决定了相机捕获图像的方法,如聚焦,曝光灯。下面的例子仅仅改变了预览的大小。可以查看相机的源码来获取更多的设置方法。
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// Now that the size is known, set up the camera parameters and begin
// the preview.
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
mCamera.setParameters(parameters);
/*
Important: Call startPreview() to start updating the preview surface. Preview must be
started before you can take a picture.
*/
mCamera.startPreview();
}
设置预览角度
许多的相机应用都默认的是风景模式,因为这个是相机传感器默认的显示方向。但是这并不妨碍你拍摄肖像模型的照片,因为这个角度是被记录在EXIF的头部。setCameraDisplayOrientation()方法可以让你修改照片的显示方向,但是并不影响照片的拍摄。在API 14和之前的API上,在改变方向之前, 必须停止preview,修改完成之后,在重新启动。
捕获一张照片
在preview启动之后,利用
Camera.takePicture()方法拍摄一张照片。我们可以创建
Camera.PictureCallback
and Camera.ShutterCallback对象并传递他们给
Camera.takePicture()
.
如果你想连续不断的抓去照片,创建一个
Camera.PreviewCallback,实现
onPreviewFrame()方法。介于2者之间,你也可以仅仅捕获选中的几个预览图像,或者是设置一个延迟操作来调用
takePicture()
.
重新启动Preview
在拍摄一张照片之后,你必须重新启动preview来拍摄另外一张照片。下面的例子,重新是通过重载快门按钮来实现的。
@Override
public void onClick(View v) {
switch(mPreviewState) {
case K_STATE_FROZEN:
mCamera.startPreview();
mPreviewState = K_STATE_PREVIEW;
break;
default:
mCamera.takePicture( null, rawCallback, null);
mPreviewState = K_STATE_BUSY;
} // switch
shutterBtnConfig();
}
停止Preview和是否Camera对象
一般来说,当你的应用拍照完成后,要是否camera对象,不然的话,会导致其他的APP在用相机的时候产生崩溃,或者是你自己的APP在重新创建camera的实例的时候迆会崩溃。
那么什么时候释放相机呢?你的surfaceview被释放时一个很好的暗示信息,这个时候就应该去释放相机,像Preview类里面一样。
public void surfaceDestroyed(SurfaceHolder holder) {
// Surface will be destroyed when we return, so stop the preview.
if (mCamera != null) {
/*
Call stopPreview() to stop updating the preview surface.
*/
mCamera.stopPreview();
}
}
/**
* When this function returns, mCamera will be null.
*/
private void stopPreviewAndFreeCamera() {
if (mCamera != null) {
/*
Call stopPreview() to stop updating the preview surface.
*/
mCamera.stopPreview();
/*
Important: Call release() to release the camera for use by other applications.
Applications should release the camera immediately in onPause() (and re-open() it in
onResume()).
*/
mCamera.release();
mCamera = null;
}
}
在前面的内容中,这个处理过程也是
setCamera()方法的一部分,所以初始化一个camera经常伴随着停止一个preview