W tej lekcji omawiamy sterowanie sprzętem kamery bezpośrednio za pomocą interfejsów API frameworku.
Uwaga: ta strona dotyczy klasy Camera, która została wycofana. Zalecamy korzystanie z CameraX lub, w przypadku określonych zastosowań, z Camera2. Zarówno CameraX, jak i Camera2 obsługują Androida 5.0 (poziom interfejsu API 21) i nowsze.
Bezpośrednie sterowanie aparatem urządzenia wymaga znacznie więcej kodu niż żądanie zdjęć lub filmów z dotychczasowych aplikacji do obsługi aparatu. Jeśli jednak chcesz utworzyć specjalistyczną aplikację do obsługi kamery lub coś, co jest w pełni zintegrowane z interfejsem aplikacji, ta lekcja pokaże Ci, jak to zrobić.
Zapoznaj się z tymi materiałami:
Otwieranie obiektu aparatu
Pobieranie instancji obiektu Camera jest pierwszym krokiem w procesie bezpośredniego sterowania kamerą. Podobnie jak w przypadku aplikacji Aparat na Androidzie, zalecany sposób uzyskania dostępu do aparatu to otwarcie Camera na osobnym wątku, który jest uruchamiany z poziomu onCreate(). Takie podejście jest dobrym pomysłem, ponieważ może zająć trochę czasu i może spowolnić wątek interfejsu. W prostszym wdrożeniu otwarcie aparatu można odłożyć do metody onResume(), aby ułatwić ponowne użycie kodu i utrzymać prostotę przepływu sterowania.
Wywołanie funkcji Camera.open() powoduje wyjątek, jeśli aparat jest już używany przez inną aplikację, więc otaczamy go blokiem 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 API 9 framework aparatu obsługuje wiele aparatów. Jeśli używasz starszego interfejsu API i wywołujesz open() bez argumentu, otrzymasz pierwszą tylną kamerę.
Tworzenie podglądu z aparatu
Zdjęcie wymaga zwykle, aby użytkownicy zobaczyli podgląd obiektu przed naciśnięciem migawki. W tym celu możesz użyć SurfaceView, aby wyświetlić podgląd tego, co rejestruje czujnik aparatu.
Klasa podglądu
Aby wyświetlić podgląd, musisz mieć zajęcia z podglądem. Podgląd wymaga implementacji interfejsu android.view.SurfaceHolder.Callback, który służy do przekazywania danych obrazu z 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, zanim można rozpocząć podgląd obrazu na żywo, jak pokazano w następnej sekcji.
Konfigurowanie i uruchamianie podglądu
Przykład kamery i powiązany z nim podgląd muszą zostać utworzone w określonej kolejności, przy czym obiekt kamery musi być utworzony jako pierwszy. W poniżej zamieszczonym fragmencie kodu proces inicjowania aparatu jest opakowany, tak aby metoda Camera.startPreview() wywoływała metodę setCamera(), gdy tylko użytkownik wprowadzi zmiany w aparacie. Podgląd musi też zostać ponownie uruchomiony w metodzie wywołania surfaceChanged() klasy preview.
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(); } }
Modyfikowanie ustawień aparatu
Ustawienia aparatu zmieniają sposób, w jaki aparat robi zdjęcia, od poziomu zoomu do kompensacji ekspozycji. W tym przykładzie zmienia się tylko rozmiar podglądu. W źródle aplikacji Aparat znajdziesz więcej przykładów.
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 aparatu blokuje wyświetlacz w orientacji poziomej, ponieważ jest to naturalna orientacja czujnika aparatu. To ustawienie nie uniemożliwia robienia zdjęć w układzie pionowym, ponieważ orientacja urządzenia jest zapisywana w nagłówku EXIF. Metoda setCameraDisplayOrientation() pozwala zmienić sposób wyświetlania podglądu bez wpływu na sposób rejestrowania obrazu. Na urządzeniach z Androidem w wersji starszej niż poziom interfejsu API 14 przed zmianą orientacji musisz jednak zatrzymać podgląd, a potem go ponownie uruchomić.
Zrób zdjęcie
Po rozpoczęciu podglądu użyj metody Camera.takePicture(), aby zrobić zdjęcie. Możesz tworzyć obiekty Camera.PictureCallback i Camera.ShutterCallback oraz przekazywać je do funkcji Camera.takePicture().
Jeśli chcesz ciągle pobierać obrazy, możesz utworzyć Camera.PreviewCallback, który implementuje onPreviewFrame(). Jeśli chcesz uzyskać coś pośredniego, możesz rejestrować tylko wybrane klatki podglądu lub skonfigurować opóźnione wywołanie takePicture().
Ponownie uruchom podgląd
Po zrobieniu zdjęcia musisz ponownie uruchomić podgląd, zanim użytkownik będzie mógł zrobić kolejne zdjęcie. W tym przykładzie ponowne uruchomienie jest wykonywane 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(); }
Zatrzymanie podglądu i zwolnienie kamery
Gdy aplikacja skończy korzystać z kamery, nadszedł czas na uporządkowanie. W szczególności musisz zwolnić obiekt Camera, w przeciwnym razie ryzykujesz zawieszenie innych aplikacji, w tym nowych instancji własnej aplikacji.
Kiedy należy zatrzymać podgląd i zwolnić kamerę? No cóż, zniszczenie powierzchni podglądu to dobry znak, że czas zatrzymać podgląd i zwolnić kamerę, tak jak w metodach klasy 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; } }
Wcześniej w lekcji ta procedura była również częścią metody setCamera(), więc inicjowanie aparatu zawsze zaczyna się od zatrzymania podglądu.