sterować kamerą,

W tej lekcji powiemy, jak sterować bezpośrednio kamerą za pomocą interfejsów API.

Uwaga: ta strona dotyczy klasy Camera, która została wycofana. Zalecamy korzystanie z aplikacji CameraX lub (w określonych przypadkach) Camera2. Zarówno aplikacja CameraX, jak i Aparat 2 obsługują Androida 5.0 (poziom interfejsu API 21) i nowsze wersje.

Bezpośrednie sterowanie aparatem urządzenia wymaga znacznie więcej kodu niż wysyłanie żądań zdjęć lub filmów z istniejących aplikacji fotograficznych. Z tej lekcji dowiesz się, jak utworzyć specjalistyczną aplikację aparatu lub w pełni zintegrowane z nią interfejs.

Zapoznaj się z tymi powiązanymi materiałami:

Otwórz obiekt aparatu

Uzyskanie instancji obiektu Camera to pierwszy etap bezpośredniego sterowania kamerą. Podobnie jak w przypadku aplikacji Aparat na Androidzie, zalecanym sposobem uzyskania dostępu do aparatu jest otwarcie aplikacji Camera w osobnym wątku, który został uruchomiony z przeglądarki onCreate(). To dobry pomysł, ponieważ może to trochę potrwać i spowodować, że wątek UI może być zakłócony. W prostszej implementacji uruchamianie kamery można odłożyć na użycie metody onResume(), co ułatwia ponowne użycie kodu i ułatwia przepływ kontroli.

Wywołanie Camera.open() stwarza wyjątek, jeśli kamera jest już używana przez inną aplikację, dlatego pakujemy ją w blok 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;
    }
}

Od poziomu 9 interfejsu API platforma aparatów obsługuje wiele kamer. Jeśli używasz starszej wersji interfejsu API i wywołujesz funkcję open() bez argumentu, otrzymasz pierwszy tylny aparat.

Utwórz podgląd z aparatu

Zazwyczaj użytkownik musi zobaczyć podgląd obiektu, zanim kliknie migawkę. Aby to zrobić, możesz użyć elementu SurfaceView, aby wyświetlić podgląd tego, co rejestruje czujnik.

Wyświetl podgląd zajęć

Aby zacząć wyświetlać podgląd, potrzebujesz klasy podglądu. Podgląd wymaga implementacji interfejsu android.view.SurfaceHolder.Callback, który służy do przesyłania danych obrazu ze sprzętu aparatu do aplikacji.

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

Klasa podglądu musi zostać przekazana do obiektu Camera przed uruchomieniem podglądu obrazu na żywo, jak pokazano w następnej sekcji.

Ustawianie i uruchamianie podglądu

Instancja kamery i powiązany z nią podgląd należy utworzyć w określonej kolejności – obiekt kamery musi być widoczny jako pierwszy. W poniższym fragmencie kodu proces inicjowania kamery jest zawarty w taki sposób, że funkcja Camera.startPreview() jest wywoływana przez metodę setCamera() za każdym razem, gdy użytkownik zmieni kamerę. Należy też ponownie uruchomić podgląd w metodzie wywołania zwrotnego klasy podglądu 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();
    }
}

Modyfikuj ustawienia aparatu

Ustawienia aparatu zmieniają sposób robienia zdjęć – od powiększenia po kompensację ekspozycji. W tym przykładzie zmienia się tylko rozmiar podglądu. Więcej informacji znajdziesz w kodzie źródłowym aplikacji Aparat.

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

Ustawianie orientacji podglądu

Większość aplikacji do obsługi kamer blokuje ekran w trybie poziomym, ponieważ tak jest w naturalnej orientacji czujnika aparatu. To ustawienie nie uniemożliwia robienia zdjęć w trybie portretowym, ponieważ orientacja urządzenia jest rejestrowana w nagłówku EXIF. Metoda setCameraDisplayOrientation() umożliwia zmianę sposobu wyświetlania podglądu bez wpływu na sposób rejestrowania obrazu. Jednak na urządzeniach z Androidem przed zmianą poziomu interfejsu API na poziom 14 musisz zatrzymać podgląd przed zmianą orientacji, a następnie ponownie uruchomić podgląd.

Zrób zdjęcie

Aby zrobić zdjęcie po uruchomieniu podglądu, użyj metody Camera.takePicture(). Możesz tworzyć obiekty Camera.PictureCallback oraz Camera.ShutterCallback i przekazywać je do Camera.takePicture().

Jeśli chcesz pobierać obrazy w trybie ciągłym, możesz utworzyć obiekt Camera.PreviewCallback z implementacją onPreviewFrame(). Możesz przechwycić tylko wybrane klatki podglądu lub skonfigurować opóźnione działanie funkcji takePicture().

Uruchom ponownie podgląd

Po zrobieniu zdjęcia musisz ponownie uruchomić podgląd, zanim użytkownik będzie mógł zrobić kolejne. W tym przykładzie ponowne uruchomienie następuje przez przeciążenie przycisku migawki.

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

Zatrzymaj podgląd i puść aparat

Gdy aplikacja skończy korzystać z aparatu, trzeba ją wyczyścić. W szczególności musisz zwolnić obiekt Camera. W przeciwnym razie ryzykujesz awarię innych aplikacji, w tym nowych instancji Twojej aplikacji.

Kiedy należy zatrzymać podgląd i zwolnić aparat? Zniszczenie powierzchni podglądu to dobra wskazówka, że nadszedł czas, aby zatrzymać podgląd i zwolnić kamerę, jak pokazano w tych metodach w klasie 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;
    }
}

Na początku lekcji ta procedura była również częścią metody setCamera(), więc uruchomienie kamery zawsze zaczyna się od zatrzymania podglądu.