Kamera steuern

In dieser Lektion wird erläutert, wie Sie die Kamerahardware direkt über die Framework-APIs steuern.

Hinweis:Auf dieser Seite wird auf die Klasse Camera verwiesen, die eingestellt wurde. Wir empfehlen die Verwendung von CameraX oder, für bestimmte Anwendungsfälle, Camera2. Sowohl CameraX als auch Camera2 unterstützen Android 5.0 (API-Level 21) und höher.

Die direkte Steuerung einer Gerätekamera erfordert viel mehr Code als das Anfordern von Bildern oder Videos von vorhandenen Kameraanwendungen. Wenn Sie jedoch eine spezielle Kameraanwendung oder etwas vollständig in Ihre App-Benutzeroberfläche integriertes erstellen möchten, erfahren Sie in dieser Lektion, wie das geht.

Weitere Informationen finden Sie in den folgenden Ressourcen:

Kameraobjekt öffnen

Eine Instanz des Camera-Objekts zu erhalten, ist der erste Schritt, um die Kamera direkt zu steuern. Wie bei der Kamera-App von Android wird empfohlen, auf die Kamera zuzugreifen, indem Sie Camera in einem separaten Thread öffnen, der von onCreate() aus gestartet wird. Dieser Ansatz ist sinnvoll, da er einige Zeit in Anspruch nehmen und den UI-Thread verlangsamen kann. Bei einer einfacheren Implementierung kann das Öffnen der Kamera auf die onResume()-Methode verschoben werden, um die Wiederverwendung von Code zu erleichtern und den Kontrollfluss einfach zu halten.

Beim Aufrufen von Camera.open() wird eine Ausnahme ausgelöst, wenn die Kamera bereits von einer anderen Anwendung verwendet wird. Daher wird der Aufruf in einen try-Block eingeschlossen.

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;
    }
}

Seit API-Level 9 unterstützt das Kamera-Framework mehrere Kameras. Wenn Sie die Legacy-API verwenden und open() ohne Argument aufrufen, erhalten Sie die erste nach hinten gerichtete Kamera.

Kameravorschau erstellen

Normalerweise müssen Nutzer eine Vorschau des Motivs sehen, bevor sie auf den Auslöser klicken. Dazu können Sie ein SurfaceView verwenden, um Vorschauen der vom Kamerasensor erfassten Inhalte zu erstellen.

Vorschauklasse

Um eine Vorschau anzuzeigen, benötigen Sie eine Vorschauklasse. Für die Vorschau ist eine Implementierung der android.view.SurfaceHolder.Callback-Schnittstelle erforderlich, über die Bilddaten von der Kamerahardware an die Anwendung übergeben werden.

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);
    }
...
}

Die Vorschauklasse muss an das Camera-Objekt übergeben werden, bevor die Livebildvorschau gestartet werden kann, wie im nächsten Abschnitt gezeigt.

Vorschau einrichten und starten

Eine Kamera-Instanz und die zugehörige Vorschau müssen in einer bestimmten Reihenfolge erstellt werden, wobei das Kameraobjekt zuerst erstellt werden muss. Im folgenden Snippet wird die Initialisierung der Kamera gekapselt, sodass Camera.startPreview() von der Methode setCamera() aufgerufen wird, wenn der Nutzer die Kamera ändert. Die Vorschau muss auch in der surfaceChanged()-Callback-Methode der Vorschauklasse neu gestartet werden.

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();
    }
}

Kameraeinstellungen ändern

Mit den Kameraeinstellungen können Sie die Art und Weise ändern, wie die Kamera Bilder aufnimmt, vom Zoomfaktor bis zur Belichtungskorrektur. In diesem Beispiel wird nur die Vorschaubildgröße geändert. Im Quellcode der Kamera-App finden Sie viele weitere Beispiele.

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();
}

Vorschauausrichtung festlegen

Bei den meisten Kameraanwendungen wird das Display im Querformat gesperrt, da dies die natürliche Ausrichtung des Kamerasensors ist. Diese Einstellung verhindert nicht, dass Sie Fotos im Hochformat aufnehmen, da die Ausrichtung des Geräts im EXIF-Header aufgezeichnet wird. Mit der Methode setCameraDisplayOrientation() können Sie die Darstellung der Vorschau ändern, ohne die Aufzeichnung des Bildes zu beeinflussen. Unter Android vor API-Level 14 müssen Sie die Vorschau jedoch beenden, bevor Sie die Ausrichtung ändern, und sie dann neu starten.

Foto aufnehmen

Mit der Methode Camera.takePicture() können Sie ein Bild aufnehmen, sobald die Vorschau gestartet wurde. Sie können Camera.PictureCallback- und Camera.ShutterCallback-Objekte erstellen und an Camera.takePicture() übergeben.

Wenn Sie Bilder kontinuierlich abrufen möchten, können Sie eine Camera.PreviewCallback erstellen, die onPreviewFrame() implementiert. Wenn Sie etwas dazwischen möchten, können Sie nur ausgewählte Vorschaubilder erfassen oder eine verzögerte Aktion zum Aufrufen von takePicture() einrichten.

Vorschau neu starten

Nachdem ein Bild aufgenommen wurde, müssen Sie die Vorschau neu starten, bevor der Nutzer ein weiteres Bild aufnehmen kann. In diesem Beispiel wird der Neustart durch Überlastung des Auslösers durchgeführt.

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();
}

Vorschau beenden und Kamera freigeben

Wenn Ihre Anwendung die Kamera nicht mehr verwendet, ist es an der Zeit, die Ressourcen freizugeben. Insbesondere müssen Sie das Camera-Objekt freigeben, da sonst andere Anwendungen abstürzen könnten, einschließlich neuer Instanzen Ihrer eigenen Anwendung.

Wann sollten Sie die Vorschau beenden und die Kamera loslassen? Wenn die Vorschauoberfläche zerstört wird, ist das ein ziemlich guter Hinweis darauf, dass es an der Zeit ist, die Vorschau zu beenden und die Kamera freizugeben, wie in diesen Methoden der Klasse Preview gezeigt.

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;
    }
}

Weiter oben in der Lektion war diese Vorgehensweise auch Teil der Methode setCamera(). Das Initialisieren einer Kamera beginnt also immer mit dem Beenden der Vorschau.