本节课主要讲述怎样通过调用系统的framework APIs来控制设备的摄像头操作。
相比调用系统内置的其他相机应用来拍照和摄像,自己编写代码来直接控制相机操作需要更多的工作,然而,如果您要设计一款专业的拍照应用,或者相机功能与您的UI界面进行深度整合,那么您可以从本节课找到所需的知识。
开启相机
自己编写代码控制相机的第一步就是获得一个Camera 实例对象,与Android系统内置的其他相机应用类似,推荐的方法是在onCreate()方法中启动一个新的线程来开启相机,因为这个过程可能比较耗时,新启一个线程来处理可以有效避免UI主线程的停顿,另一种方式是将开启相机的过程推迟到 onResume() 方法中,这样便于代码的重用和简化流程控制。
如果此时有其他应用正在使用相机,那么当您的程序调用方法Camera.open() 的时候会出现异常,需要进行捕获( try block)。
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 开始,camera framework 支持用户程序控制多个相机,如果您在调用方法open() 的时候没有传递任何参数,那么系统会默认启动Androd设备的第一个后置摄像头。
创建相机预览画面
按下快门键拍照以前,用户希望看到当前相机的预览画面,要实现此功能,那么您可以使用SurfaceView,通过获取实时的相机捕获的数据并展示到界面上。
自定义预览界面类型(Preview Class)
要创建一个预览画面,需要自定义一个画面预览类型(preview class)。您需要实现 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 实例对象,稍后会有介绍。
设置和开启预览(Set and Start the Preview)
创建Camera实例和创建与其绑定的预览界面实例必须按照一个特定的顺序,且创建Camera实例必须是第一步。下面的实例代码中,将开启相机的预览操作封装在一个函数中,这样可以保证不论用户什么时候只要调用过 setCamera()方法更新了相机参数配置,用来开启预览功能的Camera.startPreview()方法就会被调用,同样在画面大小变更的回调函数 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(); } }
配置相机参数
在控制相机拍照的过程中可以修改相机的配置参数,比如放大倍率,曝光补偿等等,示例代码展示了修改相机的预览画面大小,如果想了解更多的配置参数请参阅Camera APP的源代码。
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 header中。方法setCameraDisplayOrientation() 可以用来改变预览画面的方向而不影响最终照片的拍摄方向。需要注意的是在API level 14之前,您需要首先停止预览画面,切换横竖屏模式,再重新启动预览画面。
拍照
在预览的过程中,调用方法 Camera.takePicture() 来拍摄照片,可以创建Camera.PictureCallback 和 Camera.ShutterCallback 实例对象作为参数传递给方法 Camera.takePicture() 中。想要实现连续拍照,需要实现接口Camera.PreviewCallback 中的onPreviewFrame()函数,在实现代码中,您可以捕获当前选定的预览帧,或者设定一个延迟时间来调用拍照函数 takePicture()。
重新启动拍照预览
拍摄了一张照片以后,您必须重新启动画面预览才可以继续拍摄下一张照片,示例代码展示了用户按下快门键拍照后重新启动预览。
@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 实例对象,否则可能会破坏其他应用程序的执行,包括您自己应用的重新启动。
那么哪个地方是最合适停止预览与释放Camera资源的呢?预览画面销毁的回调函数中是个不错的选择。需要参照前面实现的自定义预览类型( Preview class)。
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 实例之前总是首先停止预览。
参考文摘:
http://developer.android.com/training/camera/cameradirect.html
又来了,春节都过去了…..无聊啊,进来看看文章(一淘网:www.cnoz.org)
恭祝博主新年快乐,以后会常来看看的,(一淘比价网:www.cnoz.org)
带上诚挚的祝福,怀着思念的心情,走在团圆的日子,送去岁末的祝愿,愿一家团圆,愿岁岁平安,愿身体健康,愿万事吉祥。愿你除夕快乐!
博主新年快乐哦!