控制相機

在本課程中,我們將討論如何直接使用 架構 API

注意:本頁面所述是指已淘汰的相機類別。建議使用 CameraX,或者,在特定情況使用 Camera2。CameraX 和 Camera2 均支援 Android 5.0 (API 級別 21) 以上版本。

比起要求圖片或影片,直接控制裝置相機的程式碼要多上許多 從現有相機應用程式中移除不過,如果您想要建構專用的相機應用程式 或完全整合到您的應用程式 UI 中,本課程將說明如何進行這些操作。

請參閱下列相關資源:

開啟相機物件

如要取得 Camera 物件的執行個體, 直接控制相機的程序如同 Android 本身「相機」應用程式, 如要存取相機,建議的做法是在另一個執行緒上開啟 Camera 還有從 onCreate() 啟動的 Pod這種做法 因為可能需要一些時間才能完成,而且可能會使 UI 執行緒停滯。在基本的導入方式中 開啟相機可延遲給 onResume() 方法,以便重複使用程式碼,並保持 控制簡單

呼叫 Camera.open() 會擲回 因為其他應用程式正在使用攝影機,所以我們會將其包裝 在 try 區塊中。

Kotlin

private fun safeCameraOpen(id: Int): Boolean {
    return try {
        releaseCameraAndPreview()
        mCamera = Camera.open(id)
        true
    } catch (e: Exception) {
        Log.e(getString(R.string.app_name), "failed to open Camera")
        e.printStackTrace()
        false
    }
}

private fun releaseCameraAndPreview() {
    preview?.setCamera(null)
    mCamera?.also { camera ->
        camera.release()
        mCamera = null
    }
}

Java

private boolean safeCameraOpen(int id) {
    boolean qOpened = false;

    try {
        releaseCameraAndPreview();
        camera = Camera.open(id);
        qOpened = (camera != null);
    } catch (Exception e) {
        Log.e(getString(R.string.app_name), "failed to open Camera");
        e.printStackTrace();
    }

    return qOpened;
}

private void releaseCameraAndPreview() {
    preview.setCamera(null);
    if (camera != null) {
        camera.release();
        camera = null;
    }
}

自 API 級別 9 開始,相機架構可支援多部相機。如果您使用 舊版 API 並呼叫 open(),而沒有 引數時,你就會得到第一個後置鏡頭。

建立相機預覽

拍照時,使用者通常需要看到拍攝主體的預覽畫面,才會點選 快門。方法是使用 SurfaceView 繪製 相機感應器正在偵測。

預覽類別

如要開始顯示預覽畫面,必須具備預覽類別。 預覽需要實作 android.view.SurfaceHolder.Callback 介面,用來傳遞圖片 將相機硬體資料傳送至應用程式

Kotlin

class Preview(
        context: Context,
        val surfaceView: SurfaceView = SurfaceView(context)
) : ViewGroup(context), SurfaceHolder.Callback {

    var mHolder: SurfaceHolder = surfaceView.holder.apply {
        addCallback(this@Preview)
        setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)
    }
    ...
}

Java

class Preview extends ViewGroup implements SurfaceHolder.Callback {

    SurfaceView surfaceView;
    SurfaceHolder holder;

    Preview(Context context) {
        super(context);

        surfaceView = new SurfaceView(context);
        addView(surfaceView);

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        holder = surfaceView.getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
...
}

預覽類別必須在直播期間傳遞至 Camera 物件 即可開始預覽圖片,如下一節所示。

設定並開始預覽

您必須在特定物件中建立相機執行個體及其相關預覽 將相機物件放在最前面在下方程式碼片段中, 會封裝相機的初始化程序,讓模型呼叫 Camera.startPreview() setCamera() 方法,每當使用者變更 相機上另外也必須在預覽類別 surfaceChanged() 回呼方法中重新啟動預覽。

Kotlin

fun setCamera(camera: Camera?) {
    if (mCamera == camera) {
        return
    }

    stopPreviewAndFreeCamera()

    mCamera = camera

    mCamera?.apply {
        mSupportedPreviewSizes = parameters.supportedPreviewSizes
        requestLayout()

        try {
            setPreviewDisplay(holder)
        } catch (e: IOException) {
            e.printStackTrace()
        }

        // Important: Call startPreview() to start updating the preview
        // surface. Preview must be started before you can take a picture.
        startPreview()
    }
}

Java

public void setCamera(Camera camera) {
    if (mCamera == camera) { return; }

    stopPreviewAndFreeCamera();

    mCamera = camera;

    if (mCamera != null) {
        List<Size> localSizes = mCamera.getParameters().getSupportedPreviewSizes();
        supportedPreviewSizes = localSizes;
        requestLayout();

        try {
            mCamera.setPreviewDisplay(holder);
        } 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();
    }
}

修改相機設定

相機設定會改變相機從變焦拍照的方式 曝光補償等級這個範例只會變更預覽大小。 查看相機應用程式的原始碼

Kotlin

override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) {
    mCamera?.apply {
        // Now that the size is known, set up the camera parameters and begin
        // the preview.
        parameters?.also { params ->
            params.setPreviewSize(previewSize.width, previewSize.height)
            requestLayout()
            parameters = params
        }

        // Important: Call startPreview() to start updating the preview surface.
        // Preview must be started before you can take a picture.
        startPreview()
    }
}

Java

@Override
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(previewSize.width, previewSize.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() 方法可讓您 但不影響圖片錄製方式。不過,在 Android 之前 至 API 級別 14,則必須先停止預覽,才能變更方向並重新啟動。

拍攝相片

使用「Camera.takePicture()」 方法,開始預覽後拍照。您可以建立 Camera.PictureCallbackCamera.ShutterCallback 物件,並將其傳遞至 Camera.takePicture()

如要持續擷取圖片,可以建立實作 onPreviewFrame()Camera.PreviewCallback。適用對象 也可以只擷取選定的預覽畫面 呼叫 takePicture()

重新開始預覽

使用者拍照後,必須重新啟動預覽。 可以拍攝另一張相片此範例是透過超載而重新啟動 快門按鈕

Kotlin

fun onClick(v: View) {
    previewState = if (previewState == K_STATE_FROZEN) {
        camera?.startPreview()
        K_STATE_PREVIEW
    } else {
        camera?.takePicture(null, rawCallback, null)
        K_STATE_BUSY
    }
    shutterBtnConfig()
}

Java

@Override
public void onClick(View v) {
    switch(previewState) {
    case K_STATE_FROZEN:
        camera.startPreview();
        previewState = K_STATE_PREVIEW;
        break;

    default:
        camera.takePicture( null, rawCallback, null);
        previewState = K_STATE_BUSY;
    } // switch
    shutterBtnConfig();
}

停止預覽並釋出相機

應用程式完成相機拍攝後,可開始進行清除。於 您必須釋出 Camera 物件,否則, 包括您應用程式的新執行個體

何時應停止預覽並釋出相機?嗯,擁有 預覽表面被破壞時,最好能 預覽並釋出相機,如 Preview 類別中的這些方法所示。

Kotlin

override fun surfaceDestroyed(holder: SurfaceHolder) {
    // Surface will be destroyed when we return, so stop the preview.
    // Call stopPreview() to stop updating the preview surface.
    mCamera?.stopPreview()
}

/**
 * When this function returns, mCamera will be null.
 */
private fun stopPreviewAndFreeCamera() {
    mCamera?.apply {
        // Call stopPreview() to stop updating the preview surface.
        stopPreview()

        // Important: Call release() to release the camera for use by other
        // applications. Applications should release the camera immediately
        // during onPause() and re-open() it during onResume()).
        release()

        mCamera = null
    }
}

Java

@Override
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
        // during onPause() and re-open() it during onResume()).
        mCamera.release();

        mCamera = null;
    }
}

如本課程之前所述,這個程序也是 setCamera() 方法的一部分,因此初始化相機一律從停止 預覽。