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.