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
MeteringPoint
w taki sposób:MeteringPointFactory.createPoint()
- Za pomocą
MeteringPoint
utwórzFocusMeteringAction
. - Za pomocą obiektu
CameraControl
na 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
ZoomState
z obiektuCamera.CameraInfo
, gdzieCamera
w przypadku wywołania przez CiebiebindToLifecycle()
- Jeśli
ZoomState
zawiera wartośćzoomRatio
, zapisz ją jako bieżące powiększenie współczynnik proporcji. Jeśli na urządzeniuZoomState
nie 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ę
Camera
z 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ść. ZobaczCamcorderProfile
w 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
MediaRecorder
Dzwonię 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.