Jeśli aplikacja używa oryginalnego pakietu Camera
klasa („Aparat1”), która jest wycofana od
Android 5.0 (poziom API 21),
zalecamy przejście na nowoczesny interfejs API aparatu w Androidzie. Oferty Androida
CameraX (ustandaryzowany, solidny aparat Jetpack);
API) i Camera2 (niskopoziomowy interfejs API). W przypadku atrybutu
w większości przypadków zalecamy przeniesienie aplikacji na Aparat X. Przyczyna jest następująca:
- Łatwość obsługi: Aparat X obsługuje szczegóły, dzięki czemu skup się na tworzeniu pracy kamery od podstaw, a nie na do wyróżnienia Twojej aplikacji.
- Aparat X obsługuje fragmentację za Ciebie: Aparat X ogranicza długoterminowe koszty konserwacji oraz kod dostosowany do konkretnego urządzenia, w związku z wyższą jakością użytkownikom. Aby dowiedzieć się więcej na ten temat, sprawdź Lepsza zgodność urządzeń z Aparatem X .
- Zaawansowane funkcje: Aparat X został zaprojektowany tak, aby umożliwiać zaawansowane którą łatwo włączyć w swojej aplikacji. Na przykład można łatwo stosowanie efektu bokeh, retuszu twarzy, HDR (High Dynamic Range) i rozjaśniania przy słabym oświetleniu trybu nocnego na zdjęciach za pomocą Rozszerzenia CameraX.
- Możliwość aktualizacji: Android udostępnia nowe funkcje i poprawki błędów w aplikacji CameraX. przez cały rok. Dzięki przejściu na Aparat X Twoja aplikacja otrzymuje najnowszą wersję Androida technologia aparatu w każdej wersji CameraX, a nie tylko na rocznych wersji Androida.
W tym przewodniku omawiamy typowe scenariusze korzystania z aplikacji aparatu. Każdy obejmuje implementację Aparatu1 i AparatuX porównanie.
W przypadku migracji czasami potrzebujesz
elastycznej elastyczności,
z dotychczasową bazą kodu. Cały kod aparatu CameraX w tym przewodniku zawiera
CameraController
to świetne rozwiązanie, jeśli zależy Ci na najprostszym sposobie korzystania z aplikacji CameraX
CameraProvider
implementacji – to świetne rozwiązanie, jeśli potrzebujesz większej elastyczności. Aby łatwiej wybrać
i odpowiadają następującemu zaletyowi:
Kontroler aparatu |
Dostawca kamery |
| Wymaga niewielkiego kodu konfiguracyjnego | Większa kontrola |
| Umożliwienie mu obsługiwania większej liczby procesów konfiguracji funkcje takie jak ustawianie ostrości przez dotknięcie i powiększanie przez ściągnięcie palców, działają automatycznie. |
Konfiguracją zajmuje się deweloper aplikacji, więc
aby dostosować konfigurację, np. włączyć obrót obrazu wyjściowego
lub ustaw format obrazu wyjściowego w ImageAnalysis
|
Wymaganie PreviewView do podglądu z aparatu pozwala na użycie tego parametru
CameraX, oferując płynną, kompleksową integrację, tak jak w przypadku naszego pakietu ML Kit.
integracja, która może mapować współrzędne wyników modelu ML (takie jak twarz
ramki ograniczające) bezpośrednio na współrzędne podglądu
|
Możliwość użycia niestandardowej „płaszczyzny” do podglądu z kamery pozwala: większą elastyczność, na przykład przez wykorzystanie istniejącego kodu „Surface”, może stanowić źródło danych do innych części aplikacji |
Jeśli podczas migracji napotkasz problemy, skontaktuj się z nami na Grupa dyskusyjna CameraX.
Przed migracją
Porównywanie użycia CameraX z Aparatem 1
Choć kod może wyglądać inaczej, podstawowe pojęcia stosowane w Aparacie 1 i
Aparaty CameraX są bardzo podobne do siebie. Aparat X
przedstawia typowe funkcje aparatu w przypadkach użycia,
W rezultacie wiele zadań deweloperskich w Aparacie1 zostało cofniętych.
obsługiwane automatycznie przez aparatX. Istnieją cztery
UseCase w AparacieX, które możesz
do różnych zadań związanych z aparatem: Preview,
ImageCapture,
VideoCapture
ImageAnalysis.
Przykładem obsługi przez deweloperów informacji niskopoziomowych w AparacieX jest
ViewPort, który jest wspólny dla
aktywnych UseCase. Dzięki temu wszyscy użytkownicy UseCase będą widzieć dokładnie te same piksele.
W Aparacie 1 musisz samodzielnie zarządzać tymi szczegółami, biorąc pod uwagę zmienność
format obrazu na różnych urządzeniach czujnikami i ekranami aparatu, trudno jest
sprawdź, czy podgląd pasuje do zdjęć i filmów.
Inny przykład: AparatX automatycznie obsługuje wywołania zwrotne Lifecycle
Lifecycle instancję pozytywnie. Oznacza to, że Aparat X obsługuje
połączenia z kamerą przez cały czas
Cykl aktywności na Androidzie,
w tym w takich przypadkach: zamknięcie aparatu, gdy aplikacja zostanie
tle; usuwanie podglądu z aparatu, gdy ekran nie jest już potrzebny
jego wyświetlanie; i wstrzymywanie podglądu z kamery, gdy pojawi się inna aktywność
pierwszeństwo pierwszego planu, np. przychodzące rozmowy wideo.
Aparat CameraX obsługuje też obracanie i skalowanie bez konieczności pisania dodatkowego kodu.
z Twojej strony. W przypadku Activity z odblokowaną orientacją,
Konfiguracja UseCase jest wykonywana przy każdym obróceniu urządzenia, ponieważ podczas jego zniszczenia
i odtwarza Activity przy zmianie orientacji. Dzięki temu uzyskasz dostęp do
UseCases ustawiają rotację docelową tak, aby pasowała do orientacji wyświetlacza o
za każdym razem.
Więcej informacji o obrotach w Aparacie X
Zanim przejdziemy do szczegółów, obejrzyjmy ogólne spojrzenie na aparat X.
UseCase oraz jaki może być ich związek z aplikacją Camera1. (Pojęcia związane z AparatemX są już dostępne
niebieski i Aparat 1
koncepcje są w
zielony).
Aparat X |
|||
| Konfiguracja kontrolera CameraController / obiektu CameraProvider | |||
| ↓ | ↓ | ↓ | ↓ |
| Podgląd | Robienie zdjęć | Przechwytywanie wideo | Analiza obrazu |
| ⁞ | ⁞ | ⁞ | ⁞ |
| Zarządzaj powierzchnią podglądu i ustaw ją w Aparacie | Ustawianie funkcji PictureCallback i wywoływania TakePicture() w aparacie | Zarządzanie konfiguracją aparatu i MediaRecorder w określonej kolejności | Kod analizy niestandardowej utworzony na podstawie platformy do wyświetlania podglądu |
| ↑ | ↑ | ↑ | ↑ |
| Kod związany z urządzeniem | |||
| ↑ | |||
| Zarządzanie obrotem i skalowaniem urządzeń | |||
| ↑ | |||
| Zarządzanie sesją z kamery (wybór kamery, zarządzanie cyklem życia) | |||
Aparat 1 |
|||
Zgodność i wydajność w AparacieX
CameraX obsługuje urządzenia obsługujące Androida 5.0 (poziom interfejsu API 21) lub nowszym. Ten to ponad 98% istniejących urządzeń z Androidem. Aparat CameraX został stworzony, aby automatycznie rozróżniają urządzenia, co zmniejsza potrzebę stosowania rozwiązań kod w aplikacji. Ponadto testujemy ponad 150 urządzeń fizycznych z Androidem. wersji 5.0 w naszym Laboratorium CameraX. Ty mogą przejrzeć pełną listę urządzeń znajdujących się obecnie w Laboratorium.
Aparat X używa Executor do
i zwiększyć możliwości aparatu. Dostępne opcje
skonfiguruj własnego wykonawcy w aplikacji CameraX
jeśli aplikacja ma określone wymagania dotyczące wątków. Jeśli zasada jest nieskonfigurowana, AparatX tworzy
i wykorzystuje zoptymalizowany domyślny domyślny Executor. Wiele interfejsów API platformy
dla którego stworzono Aparat X, wymaga zablokowania komunikacji międzyprocesowej (IPC) z
sprzęt, którego reakcja może czasem trwać setki milisekund. Do tego celu
aparat X wywołuje te interfejsy API tylko z wątków w tle, dzięki czemu
wątek główny nie jest zablokowany, a interfejs pozostaje płynny.
Więcej informacji o wątkach
Jeśli rynkiem docelowym aplikacji są mniej zaawansowane urządzenia, Aparat X oferuje
pozwala skrócić czas konfiguracji
ograniczenia aparatu. Ponieważ
Podłączenie do komponentów sprzętowych może zająć
W przypadku urządzeń niskiej jakości można określić zestaw kamer
do Twoich potrzeb. CameraX łączy się z nimi tylko podczas konfiguracji. Na przykład, jeśli
Aplikacja używa tylko tylnych aparatów, może ustawić tę konfigurację
za pomocą kodu DEFAULT_BACK_CAMERA, a następnie AparatX pozwala uniknąć inicjowania przedniego aparatu
kamery, by zmniejszyć opóźnienie.
Pojęcia związane z programowaniem na Androida
W tym przewodniku zakładamy, że masz ogólną wiedzę o programowaniu na Androida. Rozwój sytuacji podstawowe informacje, oto kilka koncepcji, które warto zrozumieć przed przejdź do kodu poniżej:
- View Binding generuje klasę powiązania dla:
plików układu XML, dzięki czemu można
odwoływać się do swoich widoków w sekcji Aktywności,
tak jak w kilku poniższych fragmentach kodu. Istnieją pewne
różnice między powiązaniem widoku a
findViewById()(wcześniej był to poprzedni sposób odwoływania się do widoków), ale w poniższym kodzie powinno być możliwe zastąp wiersze powiązania widoku podobnym wywołaniemfindViewById(). - Korutyny asynchroniczne to projekt równoczesności.
dodany w Kotlin 1.3 ten wzorzec może być używany do obsługi metod CameraX,
zwróci wartość
ListenableFuture. To łatwiejsze dzięki Jetpackowi Biblioteka Równoczesna od wersji 1.1.0. Aby dodać współrzędną asynchroniczną do aplikacji:- Dodaj
implementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")do pliku Gradle. - Umieść każdy kod CameraX, który zwraca
ListenableFuture, wlaunch. zablokuj lub funkcji zawieszania. - Dodaj
await()wywołanie funkcji, które zwracaListenableFuture. - Aby lepiej zrozumieć, jak działają współorganizacje, przeczytaj Przewodnik po rozpoczynaniu współudziału.
- Dodaj
Przenoszenie typowych scenariuszy
W tej sekcji wyjaśniono, jak przenieść typowe scenariusze z Aparatu 1 do Aparatu X.
Każdy scenariusz dotyczy implementacji Aparatu1, CameraProvider
oraz implementacja CameraController w AparacieX.
Wybieranie kamery
Jedną z pierwszych rzeczy, które warto zrobić w aplikacji aparatu, jest jak wybrać różne aparaty.
Aparat 1
W Aparacie 1 możesz
Camera.open() bez parametrów
aby otworzyć pierwszy tylny aparat. Możesz też podać liczbę całkowitą identyfikatora
aparat, który chcesz uruchomić. Oto przykład, jak mogłoby to wyglądać:
// Camera1: select a camera from id. // Note: opening the camera is a non-trivial task, and it shouldn't be // called from the main thread, unlike CameraX calls, which can be // on the main thread since CameraX kicks off background threads // internally as needed. private fun safeCameraOpen(id: Int): Boolean { return try { releaseCameraAndPreview() camera = Camera.open(id) true } catch (e: Exception) { Log.e(TAG, "failed to open camera", e) false } } private fun releaseCameraAndPreview() { preview?.setCamera(null) camera?.release() camera = null }
Aparat X: kontroler aparatu
W Aparacie X wybór kamery jest obsługiwany przez klasę CameraSelector. Aparat X
to łatwy przypadek używania domyślnego aparatu. Możesz określić, czy
wybierz domyślny aparat przedni lub tylny. Ponadto
Obiekt CameraControl w AparacieX pozwala łatwo
ustaw poziom powiększenia aplikacji, więc jeśli
Twoja aplikacja działa na urządzeniu obsługującym
kamer logicznych, a potem
do odpowiedniego obiektywu.
Oto kod CameraX do używania domyślnego tylnego aparatu
CameraController:
// CameraX: select a camera with CameraController var cameraController = LifecycleCameraController(baseContext) val selector = CameraSelector.Builder() .requireLensFacing(CameraSelector.LENS_FACING_BACK).build() cameraController.cameraSelector = selector
CameraX: CameraProvider,
Oto przykład wyboru domyślnego przedniego aparatu za pomocą funkcji CameraProvider
(przedniego lub tylnego aparatu można używać z tymi funkcjami: CameraController lub
CameraProvider):
// CameraX: select a camera with CameraProvider. // Use await() within a suspend function to get CameraProvider instance. // For more details on await(), see the "Android development concepts" // section above. private suspend fun startCamera() { val cameraProvider = ProcessCameraProvider.getInstance(this).await() // Set up UseCases (more on UseCases in later scenarios) var useCases:Array= ... // Set the cameraSelector to use the default front-facing (selfie) // camera. val cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA try { // Unbind UseCases before rebinding. cameraProvider.unbindAll() // Bind UseCases to camera. This function returns a camera // object which can be used to perform operations like zoom, // flash, and focus. var camera = cameraProvider.bindToLifecycle( this, cameraSelector, useCases) } catch(exc: Exception) { Log.e(TAG, "UseCase binding failed", exc) } }) ... // Call startCamera in the setup flow of your app, such as in onViewCreated. override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) ... lifecycleScope.launch { startCamera() } }
Jeśli chcesz mieć kontrolę nad wyborem kamery, możesz to zrobić na stronie
CameraX, jeśli korzystasz z aplikacji CameraProvider, nawiązując połączenie
getAvailableCameraInfos(),
który daje obiekt CameraInfo do sprawdzania określonych właściwości aparatu, takich jak
isFocusMeteringSupported()
Następnie możesz go przekonwertować na format CameraSelector, aby używać go jak w przykładzie powyżej.
za pomocą metody CameraInfo.getCameraSelector().
Więcej informacji o każdej kamerze możesz uzyskać, używając
Camera2CameraInfo
zajęcia. Zadzwoń do nas
getCameraCharacteristic()
i przyznać klucz
do odpowiednich danych. Zajrzyj do
CameraCharacteristics
, gdzie znajduje się lista wszystkich kluczy, o które możesz wysyłać zapytania.
Oto przykład niestandardowej funkcji checkFocalLength(), z której można
zdefiniuj siebie:
// CameraX: get a cameraSelector for first camera that matches the criteria // defined in checkFocalLength(). val cameraInfo = cameraProvider.getAvailableCameraInfos() .first { cameraInfo -> val focalLengths = Camera2CameraInfo.from(cameraInfo) .getCameraCharacteristic( CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS ) return checkFocalLength(focalLengths) } val cameraSelector = cameraInfo.getCameraSelector()
Wyświetlanie podglądu
Większość aplikacji do obsługi kamery na niektórych urządzeniach musi pokazywać obraz z kamery . W przypadku aplikacji Camera1 trzeba prawidłowo zarządzać wywołaniami zwrotnymi w cyklu życia musisz też ustawić obrót i skalowanie podglądu.
Dodatkowo w Aparacie1 musisz zdecydować, czy chcesz użyć
TextureView lub
SurfaceView.
Obie opcje wiążą się z pewnymi wadami. W obu przypadkach Aparat1 wymaga
poprawnie obsługiwać obracanie i skalowanie. Urządzenie PreviewView w aparacie X, z drugiej strony
ma bazowe implementacje zarówno dla TextureView, jak i SurfaceView.
CameraX decyduje, która implementacja jest najlepsza, na podstawie takich czynników jak:
typu urządzenia i wersji Androida, na której działa Twoja aplikacja. Jeśli któryś
jest zgodna, możesz zadeklarować swoje preferencje
PreviewView.ImplementationMode
Opcja COMPATIBLE używa TextureView dla podglądu, a metoda
Wartość PERFORMANCE używa SurfaceView (jeśli to możliwe).
Aparat 1
Aby wyświetlić podgląd, musisz utworzyć własne zajęcia w: Preview z atrybutem
implementacji
android.view.SurfaceHolder.Callback.
który służy do przekazywania danych obrazu ze sprzętu aparatu do
aplikacji. Przed rozpoczęciem wyświetlania podglądu obrazu na żywo komponent Preview
klasa musi być przekazywana do obiektu Camera.
// Camera1: set up a camera preview. class Preview( context: Context, private val camera: Camera ) : SurfaceView(context), SurfaceHolder.Callback { private val holder: SurfaceHolder = holder.apply { addCallback(this@Preview) setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) } override fun surfaceCreated(holder: SurfaceHolder) { // The Surface has been created, now tell the camera // where to draw the preview. camera.apply { try { setPreviewDisplay(holder) startPreview() } catch (e: IOException) { Log.d(TAG, "error setting camera preview", e) } } } override fun surfaceDestroyed(holder: SurfaceHolder) { // Take care of releasing the Camera preview in your activity. } override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) { // If your preview can change or rotate, take care of those // events here. Make sure to stop the preview before resizing // or reformatting it. if (holder.surface == null) { return // The preview surface does not exist. } // Stop preview before making changes. try { camera.stopPreview() } catch (e: Exception) { // Tried to stop a non-existent preview; nothing to do. } // Set preview size and make any resize, rotate or // reformatting changes here. // Start preview with new settings. camera.apply { try { setPreviewDisplay(holder) startPreview() } catch (e: Exception) { Log.d(TAG, "error starting camera preview", e) } } } } class CameraActivity : AppCompatActivity() { private lateinit var viewBinding: ActivityMainBinding private var camera: Camera? = null private var preview: Preview? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewBinding = ActivityMainBinding.inflate(layoutInflater) setContentView(viewBinding.root) // Create an instance of Camera. camera = getCameraInstance() preview = camera?.let { // Create the Preview view. Preview(this, it) } // Set the Preview view as the content of the activity. val cameraPreview: FrameLayout = viewBinding.cameraPreview cameraPreview.addView(preview) } }
Aparat X: kontroler aparatu
W Aparacie X możliwości zarządzania są znacznie mniejsze. Jeśli używasz tagu
CameraController, musisz też użyć PreviewView. Oznacza to, że
Preview UseCase jest domniemana, dlatego konfiguracja jest znacznie mniej pracochłonna:
// CameraX: set up a camera preview with a CameraController. class MainActivity : AppCompatActivity() { private lateinit var viewBinding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewBinding = ActivityMainBinding.inflate(layoutInflater) setContentView(viewBinding.root) // Create the CameraController and set it on the previewView. var cameraController = LifecycleCameraController(baseContext) cameraController.bindToLifecycle(this) val previewView: PreviewView = viewBinding.cameraPreview previewView.controller = cameraController } }
CameraX: CameraProvider,
Dzięki CameraProvider w aparacie X nie musisz używać PreviewView, ale
nadal znacznie upraszcza konfigurację podglądu w porównaniu do Aparatu1. Demonstracja
W tym przykładzie użyto PreviewView, ale możesz wpisać
SurfaceProvider do setSurfaceProvider() w przypadku bardziej złożonych zadań
do Twoich potrzeb.
W tym przypadku element UseCase Preview nie jest sugerowany, jak ma to miejsce w przypadku CameraController,
dlatego musisz to skonfigurować:
// CameraX: set up a camera preview with a CameraProvider. // Use await() within a suspend function to get CameraProvider instance. // For more details on await(), see the "Android development concepts" // section above. private suspend fun startCamera() { val cameraProvider = ProcessCameraProvider.getInstance(this).await() // Create Preview UseCase. val preview = Preview.Builder() .build() .also { it.setSurfaceProvider( viewBinding.viewFinder.surfaceProvider ) } // Select default back camera. val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA try { // Unbind UseCases before rebinding. cameraProvider.unbindAll() // Bind UseCases to camera. This function returns a camera // object which can be used to perform operations like zoom, // flash, and focus. var camera = cameraProvider.bindToLifecycle( this, cameraSelector, useCases) } catch(exc: Exception) { Log.e(TAG, "UseCase binding failed", exc) } }) ... // Call startCamera() in the setup flow of your app, such as in onViewCreated. override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) ... lifecycleScope.launch { startCamera() } }
Dotknij, aby ustawić ostrość
Gdy podgląd z aparatu wyświetla się na ekranie, zwykle wystarczy ustawić ostrość. gdy użytkownik kliknie podgląd.
Aparat 1
Aby zastosować funkcję ostrości przez dotknięcie w Aparacie 1, musisz obliczyć optymalną ostrość
Area, aby wskazać, na którym miejscu Camera ma próbować się skupić. To urządzenie Area jest
przekazano do: setFocusAreas(). Musisz też ustawić zgodny tryb ostrości na
Camera Obszar ostrości działa tylko wtedy, gdy bieżący tryb ostrości jest
FOCUS_MODE_AUTO, FOCUS_MODE_MACRO, FOCUS_MODE_CONTINUOUS_VIDEO lub
FOCUS_MODE_CONTINUOUS_PICTURE
Każdy element Area to prostokąt o określonej wadze. Waga to wartość pomiędzy
1 i 1000. W przypadku skonfigurowania kilku wartości ten parametr określa priorytety Areas. Ten
W przykładzie użyto tylko jednej wartości Area, więc wartość wagi nie ma znaczenia. Współrzędne
prostokąta w zakresie od -1000 do 1000. Lewy górny punkt to (-1000, -1000).
Prawy dolny punkt to (1000, 1000). Kierunek zależy od czujnika
czyli to, co widzi czujnik. Parametr nie ma wpływu na kierunek
lub odbicia lustrzanego Camera.setDisplayOrientation(), więc konieczne jest
przekształcenie współrzędnych zdarzenia dotknięcia na współrzędne czujnika.
// Camera1: implement tap-to-focus. class TapToFocusHandler : Camera.AutoFocusCallback { private fun handleFocus(event: MotionEvent) { val camera = camera ?: return val parameters = try { camera.getParameters() } catch (e: RuntimeException) { return } // Cancel previous auto-focus function, if one was in progress. camera.cancelAutoFocus() // Create focus Area. val rect = calculateFocusAreaCoordinates(event.x, event.y) val weight = 1 // This value's not important since there's only 1 Area. val focusArea = Camera.Area(rect, weight) // Set the focus parameters. parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO) parameters.setFocusAreas(listOf(focusArea)) // Set the parameters back on the camera and initiate auto-focus. camera.setParameters(parameters) camera.autoFocus(this) } private fun calculateFocusAreaCoordinates(x: Int, y: Int) { // Define the size of the Area to be returned. This value // should be optimized for your app. val focusAreaSize = 100 // You must define functions to rotate and scale the x and y values to // be values between 0 and 1, where (0, 0) is the upper left-hand side // of the preview, and (1, 1) is the lower right-hand side. val normalizedX = (rotateAndScaleX(x) - 0.5) * 2000 val normalizedY = (rotateAndScaleY(y) - 0.5) * 2000 // Calculate the values for left, top, right, and bottom of the Rect to // be returned. If the Rect would extend beyond the allowed values of // (-1000, -1000, 1000, 1000), then crop the values to fit inside of // that boundary. val left = max(normalizedX - (focusAreaSize / 2), -1000) val top = max(normalizedY - (focusAreaSize / 2), -1000) val right = min(left + focusAreaSize, 1000) val bottom = min(top + focusAreaSize, 1000) return Rect(left, top, left + focusAreaSize, top + focusAreaSize) } override fun onAutoFocus(focused: Boolean, camera: Camera) { if (!focused) { Log.d(TAG, "tap-to-focus failed") } } }
Aparat X: kontroler aparatu
CameraController nasłuchuje zdarzeń dotknięcia użytkowniczki PreviewView w celu obsłużenia
automatycznie ustawić ostrość przez dotknięcie. Możesz włączyć lub wyłączyć funkcję dotknij, aby ustawić ostrość, używając
setTapToFocusEnabled()
i sprawdź wartość za pomocą odpowiedniej metody getter
isTapToFocusEnabled()
getTapToFocusState()
zwraca obiekt LiveData
do śledzenia zmian stanu skupienia w CameraController.
// CameraX: track the state of tap-to-focus over the Lifecycle of a PreviewView, // with handlers you can define for focused, not focused, and failed states. val tapToFocusStateObserver = Observer{ state -> when (state) { CameraController.TAP_TO_FOCUS_NOT_STARTED -> Log.d(TAG, "tap-to-focus init") CameraController.TAP_TO_FOCUS_STARTED -> Log.d(TAG, "tap-to-focus started") CameraController.TAP_TO_FOCUS_FOCUSED -> Log.d(TAG, "tap-to-focus finished (focus successful)") CameraController.TAP_TO_FOCUS_NOT_FOCUSED -> Log.d(TAG, "tap-to-focus finished (focused unsuccessful)") CameraController.TAP_TO_FOCUS_FAILED -> Log.d(TAG, "tap-to-focus failed") } } cameraController.getTapToFocusState().observe(this, tapToFocusStateObserver)
CameraX: CameraProvider,
Aby ustawić ostrość przez dotknięcie, musisz użyć urządzenia CameraProvider
w pracy. W tym przykładzie zakładamy, że używasz właściwości PreviewView. Jeśli nie, musisz wykonać te czynności:
dostosować logikę do niestandardowego elementu Surface.
Wykonaj te czynności, jeśli używasz PreviewView:
- Skonfiguruj wykrywanie gestów do obsługi zdarzeń dotknięcia.
- W przypadku zdarzenia kliknięcia utwórz
MeteringPointw taki sposób:MeteringPointFactory.createPoint() - Za pomocą
MeteringPointutwórzFocusMeteringAction. - Za pomocą obiektu
CameraControlna urządzeniuCamera(zwrócono zbindToLifecycle()), wywołajstartFocusAndMetering(), podając wartośćFocusMeteringAction. - (Opcjonalnie) Odpowiedz na
FocusMeteringResult. - Skonfiguruj detektor gestów, aby reagował na zdarzenia dotyku w
PreviewView.setOnTouchListener()
// CameraX: implement tap-to-focus with CameraProvider. // Define a gesture detector to respond to tap events and call // startFocusAndMetering on CameraControl. If you want to use a // coroutine with await() to check the result of focusing, see the // "Android development concepts" section above. val gestureDetector = GestureDetectorCompat(context, object : SimpleOnGestureListener() { override fun onSingleTapUp(e: MotionEvent): Boolean { val previewView = previewView ?: return val camera = camera ?: return val meteringPointFactory = previewView.meteringPointFactory val focusPoint = meteringPointFactory.createPoint(e.x, e.y) val meteringAction = FocusMeteringAction .Builder(meteringPoint).build() lifecycleScope.launch { val focusResult = camera.cameraControl .startFocusAndMetering(meteringAction).await() if (!result.isFocusSuccessful()) { Log.d(TAG, "tap-to-focus failed") } } } } ) ... // Set the gestureDetector in a touch listener on the PreviewView. previewView.setOnTouchListener { _, event -> // See pinch-to-zooom scenario for scaleGestureDetector definition. var didConsume = scaleGestureDetector.onTouchEvent(event) if (!scaleGestureDetector.isInProgress) { didConsume = gestureDetector.onTouchEvent(event) } didConsume }
Ściąganie i powiększanie
Powiększanie i pomniejszanie podglądu to kolejne częste manipulacje podgląd z aparatu. Rosnąca liczba aparatów na urządzeniach sprawia, że użytkownicy obiektyw o najlepszej ogniskowej zostanie automatycznie wybrany w wyniku powiększenia.
Aparat 1
Istnieją dwa sposoby powiększania za pomocą Aparatu1. Metoda Camera.startSmoothZoom()
zmienia się od bieżącego poziomu powiększenia do zalecanego poziomu.
Metoda Camera.Parameters.setZoom() przechodzi bezpośrednio do uzyskanego poziomu powiększenia
cal Zanim użyjesz któregoś z nich, zadzwoń pod numer isSmoothZoomSupported() lub
isZoomSupported(), aby odpowiednio korzystać z powiązanych metod powiększenia.
dostępne w Aparacie.
Aby zaimplementować powiększenie przez ściągnięcie palców, w tym przykładzie użyto elementu setZoom(), ponieważ
detektor na powierzchni podglądu stale uruchamia zdarzenia, gdy ściągnięcie
więc automatycznie zmienia poziom powiększenia za każdym razem.
Klasa ZoomTouchListener jest zdefiniowana poniżej i powinna być ustawiona jako wywołanie zwrotne
do detektora dotykowego obszaru podglądu.
// Camera1: implement pinch-to-zoom. // Define a scale gesture detector to respond to pinch events and call // setZoom on Camera.Parameters. val scaleGestureDetector = ScaleGestureDetector(context, object : ScaleGestureDetector.OnScaleGestureListener { override fun onScale(detector: ScaleGestureDetector): Boolean { val camera = camera ?: return false val parameters = try { camera.parameters } catch (e: RuntimeException) { return false } // In case there is any focus happening, stop it. camera.cancelAutoFocus() // Set the zoom level on the Camera.Parameters, and set // the Parameters back onto the Camera. val currentZoom = parameters.zoom parameters.setZoom(detector.scaleFactor * currentZoom) camera.setParameters(parameters) return true } } ) // Define a View.OnTouchListener to attach to your preview view. class ZoomTouchListener : View.OnTouchListener { override fun onTouch(v: View, event: MotionEvent): Boolean = scaleGestureDetector.onTouchEvent(event) } // Set a ZoomTouchListener to handle touch events on your preview view // if zoom is supported by the current camera. if (camera.getParameters().isZoomSupported()) { view.setOnTouchListener(ZoomTouchListener()) }
Aparat X: kontroler aparatu
Podobnie jak używanie funkcji ostrości przez dotknięcie, CameraController słucha dotyku funkcji PreviewView
które obsługują automatyczne powiększanie przez ściąganie palców. Możesz włączać i wyłączać
ściągnij palce, aby powiększyć
setPinchToZoomEnabled()
i sprawdź wartość za pomocą odpowiedniej metody getter
isPinchToZoomEnabled()
getZoomState()
zwraca obiekt LiveData do śledzenia zmian w metodzie
ZoomState w
CameraController.
// CameraX: track the state of pinch-to-zoom over the Lifecycle of // a PreviewView, logging the linear zoom ratio. val pinchToZoomStateObserver = Observer{ state -> val zoomRatio = state.getZoomRatio() Log.d(TAG, "ptz-zoom-ratio $zoomRatio") } cameraController.getZoomState().observe(this, pinchToZoomStateObserver)
CameraX: CameraProvider,
Aby korzystać z powiększania przez ściąganie palców w CameraProvider, musisz odpowiednio skonfigurować tę funkcję. Jeśli
nie używasz PreviewView, dostosuj zasady, aby zastosować
niestandardowy element Surface.
Wykonaj te czynności, jeśli używasz PreviewView:
- Skonfiguruj detektor gestów skalowania do obsługi zdarzeń ściągnięcia.
- Pobierz
ZoomStatez obiektuCamera.CameraInfo, gdzieCameraw przypadku wywołania przez CiebiebindToLifecycle() - Jeśli
ZoomStatezawiera wartośćzoomRatio, zapisz ją jako bieżące powiększenie współczynnik proporcji. Jeśli na urządzeniuZoomStatenie ma parametruzoomRatio, użyj ustawienia domyślnego kamery współczynnik powiększenia (1,0). - Wybierz iloczyn obecnego powiększenia (
scaleFactor– określić nowy współczynnik powiększenia i przekazać je doCameraControl.setZoomRatio(). - Skonfiguruj detektor gestów, aby reagował na zdarzenia dotyku w
PreviewView.setOnTouchListener()
// CameraX: implement pinch-to-zoom with CameraProvider. // Define a scale gesture detector to respond to pinch events and call // setZoomRatio on CameraControl. val scaleGestureDetector = ScaleGestureDetector(context, object : SimpleOnGestureListener() { override fun onScale(detector: ScaleGestureDetector): Boolean { val camera = camera ?: return val zoomState = camera.cameraInfo.zoomState val currentZoomRatio: Float = zoomState.value?.zoomRatio ?: 1f camera.cameraControl.setZoomRatio( detector.scaleFactor * currentZoomRatio ) } } ) ... // Set the scaleGestureDetector in a touch listener on the PreviewView. previewView.setOnTouchListener { _, event -> var didConsume = scaleGestureDetector.onTouchEvent(event) if (!scaleGestureDetector.isInProgress) { // See pinch-to-zooom scenario for gestureDetector definition. didConsume = gestureDetector.onTouchEvent(event) } didConsume }
Robienie zdjęcia
W tej sekcji dowiesz się, jak włączyć robienie zdjęć, niezależnie od tego, czy musisz to zrobić na naciśnięcia przycisku migawki, upłynięcia czasu lub jakiegokolwiek innego zdarzenia wyboru.
Aparat 1
W Aparacie 1 najpierw definiujesz
Camera.PictureCallback
w razie potrzeby zarządzać danymi zdjęć. Oto prosty przykład
PictureCallback do obsługi danych obrazu JPEG:
// Camera1: define a Camera.PictureCallback to handle JPEG data. private val picture = Camera.PictureCallback { data, _ -> val pictureFile: File = getOutputMediaFile(MEDIA_TYPE_IMAGE) ?: run { Log.d(TAG, "error creating media file, check storage permissions") return@PictureCallback } try { val fos = FileOutputStream(pictureFile) fos.write(data) fos.close() } catch (e: FileNotFoundException) { Log.d(TAG, "file not found", e) } catch (e: IOException) { Log.d(TAG, "error accessing file", e) } }
Aby zrobić zdjęcie, wywołaj metodę takePicture()
w instancji Camera. Ta metoda takePicture() ma trzy różne
dla różnych typów danych. Pierwszy parametr to
ShutterCallback (niezdefiniowany w tym przykładzie). Drugi parametr to
dla PictureCallback do obsługi nieprzetworzonych (nieskompresowanych) danych kamery. Trzecia
jest parametrem użytym w tym przykładzie, ponieważ jest to PictureCallback do obsługi
Dane obrazu JPEG.
// Camera1: call takePicture on Camera instance, passing our PictureCallback. camera?.takePicture(null, null, picture)
Aparat X: kontroler aparatu
CameraController w Aparatze X zachowuje prostotę obsługi zdjęć w obiekcie Aparat 1
za pomocą własnej metody takePicture(). Tutaj zdefiniuj
w celu skonfigurowania wpisu MediaStore i zrobienia tam zdjęcia.
// CameraX: define a function that uses CameraController to take a photo. private val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS" private fun takePhoto() { // Create time stamped name and MediaStore entry. val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US) .format(System.currentTimeMillis()) val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, name) put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg") if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image") } } // Create output options object which contains file + metadata. val outputOptions = ImageCapture.OutputFileOptions .Builder(context.getContentResolver(), MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) .build() // Set up image capture listener, which is triggered after photo has // been taken. cameraController.takePicture( outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback { override fun onError(e: ImageCaptureException) { Log.e(TAG, "photo capture failed", e) } override fun onImageSaved( output: ImageCapture.OutputFileResults ) { val msg = "Photo capture succeeded: ${output.savedUri}" Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() Log.d(TAG, msg) } } ) }
CameraX: CameraProvider,
Robienie zdjęć za pomocą aplikacji CameraProvider działa prawie tak samo jak w przypadku aplikacji
CameraController, ale najpierw musisz utworzyć i powiązać ImageCapture
UseCase ma obiekt do wywołania takePicture():
// CameraX: create and bind an ImageCapture UseCase. // Make a reference to the ImageCapture UseCase at a scope that can be accessed // throughout the camera logic in your app. private var imageCapture: ImageCapture? = null ... // Create an ImageCapture instance (can be added with other // UseCase definitions). imageCapture = ImageCapture.Builder().build() ... // Bind UseCases to camera (adding imageCapture along with preview here, but // preview is not required to use imageCapture). This function returns a camera // object which can be used to perform operations like zoom, flash, and focus. var camera = cameraProvider.bindToLifecycle( this, cameraSelector, preview, imageCapture)
Jeśli chcesz zrobić zdjęcie, możesz zadzwonić pod numer
ImageCapture.takePicture() Zobacz kod CameraController w tej sekcji
aby zobaczyć pełny przykład funkcji takePhoto().
// CameraX: define a function that uses CameraController to take a photo. private fun takePhoto() { // Get a stable reference of the modifiable ImageCapture UseCase. val imageCapture = imageCapture ?: return ... // Call takePicture on imageCapture instance. imageCapture.takePicture( ... ) }
Nagrywanie filmu
Nagrywanie filmów jest znacznie bardziej skomplikowane niż w przykładach. jak dotąd. Każda część tego procesu musi być odpowiednio skonfigurowana, zwykle w określonym zamówieniu. Może być też konieczne sprawdzenie, czy obraz i dźwięk są synchronizować lub rozwiązywać dodatkowe niespójności na urządzeniach.
Jak się przekonasz, AparatX również sprawdza się w wielu kwestiach.
Aparat 1
Nagrywanie filmów za pomocą Aparatu1 wymaga starannego zarządzania tymi funkcjami: Camera i
MediaRecorder, a metody muszą
w określonej kolejności. Musisz przestrzegać tego zamówienia przez
należy zapewnić prawidłowe działanie aplikacji:
- Uruchom aparat.
- Przygotuj i uruchom podgląd (jeśli aplikacja wyświetla nagrywany film, co zwykle tak jest).
- Odblokuj aparat, aby mógł go używać do
MediaRecorder, dzwoniąc pod numerCamera.unlock(). - Skonfiguruj nagrywanie, wywołując te metody w aplikacji
MediaRecorder:- Połącz instancję
Cameraz usługąsetCamera(camera). - Będziesz dzwonić pod numer
setAudioSource(MediaRecorder.AudioSource.CAMCORDER). - Będziesz dzwonić pod numer
setVideoSource(MediaRecorder.VideoSource.CAMERA). - Zadzwoń pod numer
setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_1080P))aby ustawić jakość. ZobaczCamcorderProfilew przypadku całej wybierz opcje jakości. - Będziesz dzwonić pod numer
setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()). - Jeśli aplikacja ma podgląd filmu, zadzwoń
setPreviewDisplay(preview?.holder?.surface) - Będziesz dzwonić pod numer
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4). - Będziesz dzwonić pod numer
setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT). - Będziesz dzwonić pod numer
setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT). - Wywołaj
prepare(), aby dokończyć konfigurację urządzeniaMediaRecorder.
- Połącz instancję
- Aby rozpocząć nagrywanie, zadzwoń pod numer
MediaRecorder.start(). - Aby zatrzymać nagrywanie, wywołaj te metody. Jeszcze raz dokładnie w takiej kolejności:
- Będziesz dzwonić pod numer
MediaRecorder.stop(). - Opcjonalnie możesz usunąć bieżącą konfigurację
MediaRecorder, wywołującMediaRecorder.reset() - Będziesz dzwonić pod numer
MediaRecorder.release(). - Zablokuj kamerę, żeby można było z niej korzystać w przyszłych sesjach
MediaRecorderDzwonię pod numerCamera.lock().
- Będziesz dzwonić pod numer
- Aby wyłączyć podgląd, zadzwoń pod numer
Camera.stopPreview(). - Na koniec, aby zwolnić
Camera, tak aby mogły go używać inne procesy, wywołajCamera.release()
Oto wszystkie kroki:
// Camera1: set up a MediaRecorder and a function to start and stop video // recording. // Make a reference to the MediaRecorder at a scope that can be accessed // throughout the camera logic in your app. private var mediaRecorder: MediaRecorder? = null private var isRecording = false ... private fun prepareMediaRecorder(): Boolean { mediaRecorder = MediaRecorder() // Unlock and set camera to MediaRecorder. camera?.unlock() mediaRecorder?.run { setCamera(camera) // Set the audio and video sources. setAudioSource(MediaRecorder.AudioSource.CAMCORDER) setVideoSource(MediaRecorder.VideoSource.CAMERA) // Set a CamcorderProfile (requires API Level 8 or higher). setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)) // Set the output file. setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()) // Set the preview output. setPreviewDisplay(preview?.holder?.surface) setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT) setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT) // Prepare configured MediaRecorder. return try { prepare() true } catch (e: IllegalStateException) { Log.d(TAG, "preparing MediaRecorder failed", e) releaseMediaRecorder() false } catch (e: IOException) { Log.d(TAG, "setting MediaRecorder file failed", e) releaseMediaRecorder() false } } return false } private fun releaseMediaRecorder() { mediaRecorder?.reset() mediaRecorder?.release() mediaRecorder = null camera?.lock() } private fun startStopVideo() { if (isRecording) { // Stop recording and release camera. mediaRecorder?.stop() releaseMediaRecorder() camera?.lock() isRecording = false // This is a good place to inform user that video recording has stopped. } else { // Initialize video camera. if (prepareVideoRecorder()) { // Camera is available and unlocked, MediaRecorder is prepared, now // you can start recording. mediaRecorder?.start() isRecording = true // This is a good place to inform the user that recording has // started. } else { // Prepare didn't work, release the camera. releaseMediaRecorder() // Inform user here. } } }
Aparat X: kontroler aparatu
Dzięki CameraController w aparacie X możesz przełączać ImageCapture,
VideoCapture i ImageAnalysis UseCase niezależnie,
o ile lista przypadków użycia może być używana jednocześnie.
Elementy UseCase ImageCapture i ImageAnalysis są domyślnie włączone, co
Dlatego nie trzeba dzwonić pod numer setEnabledUseCases(), aby zrobić zdjęcie.
Aby używać urządzenia CameraController do nagrywania filmów, musisz najpierw użyć
setEnabledUseCases(), aby zezwolić na VideoCapture UseCase.
// CameraX: Enable VideoCapture UseCase on CameraController. cameraController.setEnabledUseCases(VIDEO_CAPTURE);
Aby zacząć nagrywać film, możesz zadzwonić do
CameraController.startRecording()
. Ta funkcja może zapisać nagrany film w elemencie File, jak widać
w poniższym przykładzie. Musisz też zdać Executor i zajęcia
który stosuje
OnVideoSavedCallback.
do obsługi udanych i błędnych wywołań zwrotnych. Kiedy powinno się zakończyć nagrywanie, wywołaj połączenie
CameraController.stopRecording()
Uwaga: jeśli używasz AparatuX 1.3.0-alfa02 lub nowszego,
Parametr AudioConfig
, która umożliwia włączenie lub wyłączenie nagrywania dźwięku w filmie. Aby włączyć
do nagrywania dźwięku, musisz mieć uprawnienia do korzystania z mikrofonu.
Dodatkowo w wersji 1.3.0-alfa02 usunęliśmy metodę stopRecording().
startRecording() zwraca obiekt Recording, którego można użyć do wstrzymywania,
wznawiając i zatrzymując nagrywanie.
// CameraX: implement video capture with CameraController. private val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS" // Define a VideoSaveCallback class for handling success and error states. class VideoSaveCallback : OnVideoSavedCallback { override fun onVideoSaved(outputFileResults: OutputFileResults) { val msg = "Video capture succeeded: ${outputFileResults.savedUri}" Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() Log.d(TAG, msg) } override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) { Log.d(TAG, "error saving video: $message", cause) } } private fun startStopVideo() { if (cameraController.isRecording()) { // Stop the current recording session. cameraController.stopRecording() return } // Define the File options for saving the video. val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US) .format(System.currentTimeMillis()) val outputFileOptions = OutputFileOptions .Builder(File(this.filesDir, name)) .build() // Call startRecording on the CameraController. cameraController.startRecording( outputFileOptions, ContextCompat.getMainExecutor(this), VideoSaveCallback() ) }
CameraX: CameraProvider,
Jeśli korzystasz z karty CameraProvider, musisz utworzyć VideoCapture
UseCase i przekazać obiekt Recorder. Na Recorder.Builder możesz:
określ jakość filmu i opcjonalnie
FallbackStrategy, czyli
zajmuje się przypadkiem, gdy urządzenie nie spełnia wymagań jakościowych. Potem
powiąż instancję VideoCapture z instancją CameraProvider ze swoją drugą
UseCase s
// CameraX: create and bind a VideoCapture UseCase with CameraProvider. // Make a reference to the VideoCapture UseCase and Recording at a // scope that can be accessed throughout the camera logic in your app. private lateinit var videoCapture: VideoCaptureprivate var recording: Recording? = null ... // Create a Recorder instance to set on a VideoCapture instance (can be // added with other UseCase definitions). val recorder = Recorder.Builder() .setQualitySelector(QualitySelector.from(Quality.FHD)) .build() videoCapture = VideoCapture.withOutput(recorder) ... // Bind UseCases to camera (adding videoCapture along with preview here, but // preview is not required to use videoCapture). This function returns a camera // object which can be used to perform operations like zoom, flash, and focus. var camera = cameraProvider.bindToLifecycle( this, cameraSelector, preview, videoCapture)
Obecnie aplikacja Recorder jest dostępna w aplikacji videoCapture.output
usłudze. Recorder może uruchamiać nagrania wideo zapisane w usłudze File,
ParcelFileDescriptor lub MediaStore. W tym przykładzie użyto elementu MediaStore.
Na urządzeniu Recorder możesz je wywołać na kilka sposobów, aby je przygotować. Zadzwoń do nas
prepareRecording(), aby ustawić opcje wyjścia MediaStore. Jeśli aplikacja ma
uprawnienia do korzystania z mikrofonu urządzenia, wywołaj też też withAudioEnabled().
Następnie wywołaj start(), by rozpocząć nagrywanie, przekazując kontekst i
Detektor zdarzeń Consumer<VideoRecordEvent> do obsługi zdarzeń nagrywania wideo. Jeśli
zwracany Recording może służyć do wstrzymania, wznowienia lub zatrzymania
nagrywanie.
// CameraX: implement video capture with CameraProvider. private val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS" private fun startStopVideo() { val videoCapture = this.videoCapture ?: return if (recording != null) { // Stop the current recording session. recording.stop() recording = null return } // Create and start a new recording session. val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US) .format(System.currentTimeMillis()) val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, name) put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4") if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/CameraX-Video") } } val mediaStoreOutputOptions = MediaStoreOutputOptions .Builder(contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI) .setContentValues(contentValues) .build() recording = videoCapture.output .prepareRecording(this, mediaStoreOutputOptions) .withAudioEnabled() .start(ContextCompat.getMainExecutor(this)) { recordEvent -> when(recordEvent) { is VideoRecordEvent.Start -> { viewBinding.videoCaptureButton.apply { text = getString(R.string.stop_capture) isEnabled = true } } is VideoRecordEvent.Finalize -> { if (!recordEvent.hasError()) { val msg = "Video capture succeeded: " + "${recordEvent.outputResults.outputUri}" Toast.makeText( baseContext, msg, Toast.LENGTH_SHORT ).show() Log.d(TAG, msg) } else { recording?.close() recording = null Log.e(TAG, "video capture ends with error", recordEvent.error) } viewBinding.videoCaptureButton.apply { text = getString(R.string.start_capture) isEnabled = true } } } } }
Dodatkowe materiały
Udostępniamy wiele kompletnych aplikacji CameraX Repozytorium GitHub z przykładami kamer. Dzięki tym przykładom można zrozumieć, jak scenariusze przedstawione w tym przewodniku pasują do pełnego kontekstu: Aplikacja na Androida.
Potrzebujesz dodatkowej pomocy przy migracji do AparatuX lub masz pytania w sprawie pakietu interfejsów API aparatu Android, skontaktuj się z nami na Dyskusja na temat CameraX Grupa.