Kamera steuern

In dieser Lektion erfahren Sie, wie Sie die Kamerahardware direkt über die Framework-APIs steuern können.

Hinweis:Diese Seite bezieht sich auf die Klasse Camera, die eingestellt wurde. Wir empfehlen die Verwendung von KameraX oder für bestimmte Anwendungsfälle Kamera2. CameraX und Camera2 unterstützen Android 5.0 (API-Level 21) und höher.

Die direkte Steuerung der Kamera eines Geräts erfordert wesentlich mehr Code als das Anfordern von Bildern oder Videos von vorhandenen Kamera-Apps. Wenn Sie jedoch eine spezielle Kameraanwendung oder etwas vollständig in Ihre App-UI integriertes erstellen möchten, erfahren Sie in dieser Lektion, wie das geht.

Weitere Informationen finden Sie in den folgenden Ressourcen:

Kameraobjekt öffnen

Der erste Schritt bei der direkten Steuerung der Kamera ist das Abrufen einer Instanz des Camera-Objekts. Wie bei der Android-Kamera-App wird für den Zugriff auf die Kamera empfohlen, Camera in einem separaten Thread zu öffnen, der über onCreate() gestartet wird. Dieser Ansatz ist sinnvoll, da er eine Weile dauern kann und den UI-Thread hängen kann. In einer einfacheren Implementierung kann das Öffnen der Kamera auf die Methode onResume() übertragen werden, um die Wiederverwendung von Code zu erleichtern und den Ablauf der Steuerung einfach zu halten.

Durch das Aufrufen von Camera.open() wird eine Ausnahme ausgegeben, wenn die Kamera bereits von einer anderen Anwendung verwendet wird. Daher wird sie in einen try-Block eingebunden.

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 Rückkamera.

Kameravorschau erstellen

Für die Aufnahme von Fotos benötigen Nutzer in der Regel eine Vorschau des Motivs, bevor sie auf den Auslöser klicken. Dazu können Sie SurfaceView verwenden, um eine Vorschau davon zu erstellen, was der Kamerasensor erfasst.

Kursvorschau

Wenn Sie eine Vorschau anzeigen lassen möchten, benötigen Sie den Vorschaukurs. Für die Vorabversion 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 Live-Bildvorschau gestartet werden kann, wie im nächsten Abschnitt gezeigt.

Vorschau festlegen und starten

Eine Kamerainstanz und die zugehörige Vorschau müssen in einer bestimmten Reihenfolge erstellt werden, wobei das Kameraobjekt an erster Stelle steht. Im folgenden Snippet wird der Prozess der Kamerainitialisierung so gekapselt, dass jedes Mal, wenn der Nutzer die Kamera ändert, Camera.startPreview() von der Methode setCamera() aufgerufen wird. Die Vorschau muss auch über die Callback-Methode surfaceChanged() 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

Die Kameraeinstellungen ändern die Art und Weise, wie die Kamera Bilder aufnimmt, von der Zoomstufe bis zur Belichtungskorrektur. In diesem Beispiel wird nur die Größe der Vorschau geändert. Viele weitere Informationen finden Sie im Quellcode der Kameraanwendung.

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 die Anzeige im Querformat gesperrt, da dies die natürliche Ausrichtung des Kamerasensors ist. Diese Einstellung verhindert nicht, dass Fotos im Hochformat aufgenommen werden, da die Ausrichtung des Geräts im EXIF-Header aufgezeichnet wird. Mit der Methode setCameraDisplayOrientation() können Sie ändern, wie die Vorschau angezeigt wird, ohne dass sich dies auf die Aufzeichnung des Bilds auswirkt. In Android vor API-Level 14 musst du die Vorschau jedoch beenden, bevor du die Ausrichtung änderst. Anschließend musst du die Vorschau neu starten.

Foto aufnehmen

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

Wenn Sie kontinuierlich Bilder erfassen möchten, können Sie eine Camera.PreviewCallback erstellen, die onPreviewFrame() implementiert. Für etwas dazwischen können Sie nur ausgewählte Vorschauframes erfassen oder eine verzögerte Aktion einrichten, um takePicture() aufzurufen.

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 erfolgt der Neustart durch Überlastung des Auslösers.

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 loslassen

Nachdem die App die Kamera nicht mehr verwendet hat, ist es Zeit zum Aufräumen. Insbesondere müssen Sie das Camera-Objekt freigeben. Andernfalls riskieren Sie, dass andere Anwendungen, einschließlich neuer Instanzen Ihrer eigenen Anwendung, abstürzen.

Wann solltest du die Vorschau beenden und die Kamera loslassen? Das Zerstören der Vorschauoberfläche ist ein ziemlich guter Hinweis darauf, dass es Zeit ist, die Vorschau zu beenden und die Kamera freizugeben, wie in diesen Methoden aus der Preview-Klasse 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;
    }
}

Zu Beginn der Lektion war dieses Verfahren auch Teil der setCamera()-Methode. Das Initialisieren einer Kamera beginnt also immer mit dem Beenden der Vorschau.