在这节课程中,我们讨论如何使用框架的API来直接控制照相机。
直接控制一个设备的照相机需要比从已经存在的照相机应用程序中请求图片或者音频需要更多的代码。然而,如果你想构建一个指定的照相机应用程序,或者在你的应用程序中完全集成,这节课程向你展示如何实现。
打开照相机对象
————————————————————————————————————————————————————————————
获取Camera对象的一个实例,是直接控制相机过程的第一步。正如Android自己的相机应用程序,访问照相机要求的方式是,在onCreate()方法中启动一个单独的线程中打开Camera。这种方式是一个好的注意,因为它需要一段时间,并且在UI线程中陷入困境。 在一个更基础的实现中,打开照相机可以被延期到onResume()方法,促进代码重用并保持简单的流控制。
如果照相机已经被其它的应用程序使用,调用Camera.open()方法抛出一个异常,搜易我们在一个try块中包裹它。
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 Level 9开始,照相机框架支持多个照相机。如果你使用遗留额API并不使用参数调用onpen()方法,你第一个后方照相机。
创建照相机预览
————————————————————————————————————————————————————————————
你的用户在点击快门之前查看它们的题材的一个预览,是照相的通常要求。为了实现它,你能使用一个SurfaceView来绘制照相机传感器抓取的预览。
预览类
为了开始显示一个预览,你需要预览类。预览要求一个android.view.SurfaceHolder.Callbak接口的实现,它被用户从照相机硬件往你的应用程序传递图片数据。
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对象,如下面的章节显示。
设置和启动预览
一个照相机实例和它的相关的预览必须在一个指定的顺序被创建,照相机对象在先。在下面的代码块,初始化照相机的过程被封装,以至于Camera.startPreview()方法在setCamera()方法中被调用,无论用户什么时候改变相机。预览也必须在预览类的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头中被记录。setCameraDisplayOrientaion()方法让你改变预览的显示,而不影响记录的图片。然而,在Android早期API level 14的时候,你必须在改变方向之前停止你的预览,然后重启它。
拍照
————————————————————————————————————————————————————————————
一旦预览启动,使用Camera.takePicture()方法来拍一张照片。你能创建Camera.PictureCallback和Camera.ShutterCallbak对象,并将它们传递到Camera.tackPicture()方法。
如果你想持续不断的抓取照片,你可以创建一个Camera.PreviewCallback,它实现了onPreviewFrame()方法。在两者之间,你能捕获仅仅被选中的预览框架,或者在调用tackPicture()方法中设置一个延迟动作。
重启预览
————————————————————————————————————————————————————————————
在拍玩一张照片之后,你必须在用户拍其它照片的之前重启预览。在这个例子中,通过覆盖快门按钮执行重启。
@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();
}
停止预览和释放照相机
————————————————————————————————————————————————————————————
一旦你的应用程序正在使用照相机,是时候清理。特别,你必须释放Camera对象,或者你可能会是其它应用程序崩溃,包括你自己的应用程序的新的实例。
当你应该停止预览和释放照相机的时候?那么,你的预览界面被销毁是一个好的暗示,是停止预览和释放照相机的时候,正如在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;
}
}