在這一節課,我們會討論如何通過使用framework的APIs來直接控制相機的硬件。直接控制設備的相機,相比起拍照與錄像來說,要複雜一些。然而,如果你想要創建一個專業的特殊的相機程序,這節課會演示這部分內容。
Open the Camera Object [打開相機對象]
獲取到 Camera
對象是直接控制Camera的第一步。正如Android自帶的相機程序一樣,推薦訪問Camera的方式是在onCreate方法裏面另起一個Thread來打開Camera。這個方法可以避免因爲打開工作比較費時而引起ANR。在一個更加基礎的實現方法裏面,打開Camera的動作被延遲到onResume()方法裏面去執行,這樣使得代碼能夠更好的重用,並且保持控制流程不會複雜化。[原文是:In
a more basic implementation, opening the camera can be deferred to the onResume()
method
to facilitate code reuse and keep the flow of control simple.]
在camera正在被另外一個程序使用的時候去執行 Camera.open()
會拋出一個exception,所以需要捕獲起來。
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可以支持多個cameras。如果你使用
open()
,你會獲取到最後的一個camera。
Create the Camera Preview [創建相機預覽界面]
拍照通常需要提供一個預覽界面來顯示待拍的事物。和拍照類似,你需要使用一個SurfaceView
來展現錄製的畫面。
- Preview Class
android.view.SurfaceHolder.Callback
接口,這個接口用來傳遞從camera硬件獲取的數據到程序。
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); } ... }這個Preview類必須在查看圖片之前傳遞給
Camera
對象。正如下面描述的:
- Set and Start the Preview
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(); } }
Modify Camera Settings [修改相機設置]
相機設置可以改變拍照的方式,從縮放級別到曝光補償(exposure compensation)。下面的例子僅僅演示了改變預覽大小的設置,更多設置請參考Camera的源代碼。
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(); }
Set the Preview Orientation [設置預覽方向]
大多數相機程序會鎖定預覽爲橫屏的,因爲那是人拍照的自然方式。設置裏面並沒有阻止你去拍豎屏的照片,這些信息會被記錄在EXIF裏面。setCameraDisplayOrientation()
方法可以使得你改變預覽的方向,並且不會影響到圖片被記錄的效果。然而,在Android
API level 14之前,你必須在改變方向之前,先停止你的預覽,然後才能去重啓它。
Take a Picture [拍一張圖片]
只要預覽開始之後,可以使用 Camera.takePicture()
方法來拍下一張圖片。你可以創建Camera.PictureCallback
與 Camera.ShutterCallback
對象並傳遞他們到Camera.takePicture()
中。
如果你想要做連拍的動作,你可以創建一個Camera.PreviewCallback
並實現onPreviewFrame()
.你還可以選擇幾個預覽幀來進行拍照,或是建立一個延遲拍照的動作。
Restart the Preview [重啓預覽]
在圖片被獲取後,你必須在用戶拍下一張圖片之前重啓預覽。在下面的示例中,通過重載shutter button來實現重啓。
@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(); }
Stop the Preview and Release the Camera [停止預覽並釋放相機]
當你的程序在使用Camera之後,有必要做清理的動作。特別是,你必須釋放 Camera
對象,不然會引起其他app
crash。
那麼何時應該停止預覽並釋放相機呢? 在預覽的surface被摧毀之後,可以做停止預覽與釋放相機的動作。如下所示:
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,歡迎交流!
轉載請註明出自:http://blog.csdn.net/kesenhoo,謝謝!