【Android Training - Multimedia】捕獲照片 [Lesson 3 - 直接操控相機]


在這一節課,我們會討論如何通過使用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
爲了顯示一個預覽界面,你需要一個Preview類。這個類需要實現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實例與它相關的preview必須以一種指定的順序來創建,首先是創建Camera對象。在下面的示例中,初始化camera的動作被封裝起來,這樣,無論用戶想對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();
    }
}

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,謝謝!




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章