Platforma Androida obsługuje różne aparaty i funkcje aparatu dostępne na urządzeniach, co umożliwia robienie zdjęć i nagrywanie filmów w aplikacjach. W tym dokumencie omawiamy szybkie i proste podejście do rejestrowania obrazów i filmów oraz zaawansowane podejście do tworzenia niestandardowych funkcji aparatu dla użytkowników.
Uwaga:
ta strona zawiera opis klasy
Camera
, która nie jest już używana. Zalecamy używanie biblioteki Jetpack CameraX lub, w określonych przypadkach, klasy camera2
. Zarówno CameraX, jak i Camera2 działają na Androidzie 5.0 (poziom interfejsu API 21) i nowszym.
Zapoznaj się z tymi materiałami:
co należy wziąć pod uwagę
Zanim włączysz w aplikacji możliwość korzystania z aparatów na urządzeniach z Androidem, zastanów się nad kilkoma kwestiami dotyczącymi tego, jak aplikacja ma korzystać z tego sprzętu.
- Wymagania dotyczące aparatu – czy korzystanie z aparatu jest tak ważne dla Twojej aplikacji, że nie chcesz, aby była ona instalowana na urządzeniach, które nie mają aparatu? Jeśli tak jest, zadeklaruj wymaganie dotyczące kamery w manifeście.
- Szybkie zdjęcie lub dostosowany aparat – jak aplikacja będzie korzystać z aparatu? Czy chcesz tylko robić szybkie zdjęcia lub nagrywać krótkie klipy wideo, czy Twoja aplikacja będzie oferować nowy sposób korzystania z aparatów? Jeśli chcesz szybko zrobić zdjęcie lub nagrać klip, rozważ użycie istniejących aplikacji aparatu. Jeśli chcesz opracować niestandardową funkcję aparatu, zapoznaj się z sekcją Tworzenie aplikacji aparatu.
- Wymagania dotyczące usług działających na pierwszym planie – kiedy aplikacja wchodzi w interakcję z kamerą? Na Androidzie 9 (poziom interfejsu API 28) i nowszych aplikacje działające w tle nie mają dostępu do kamery. Dlatego należy używać aparatu, gdy aplikacja jest na pierwszym planie lub w ramach usługi działającej na pierwszym planie.
- Pamięć – czy obrazy lub filmy generowane przez aplikację mają być widoczne tylko dla niej, czy też mają być udostępniane, aby inne aplikacje, takie jak Galeria lub inne aplikacje multimedialne i społecznościowe, mogły z nich korzystać? Czy chcesz, aby zdjęcia i filmy były dostępne nawet po odinstalowaniu aplikacji? Więcej informacji o tym, jak wdrożyć te opcje, znajdziesz w sekcji Zapisywanie plików multimedialnych.
Podstawy
Platforma Android obsługuje robienie zdjęć i nagrywanie filmów za pomocą interfejsu API android.hardware.camera2
lub aparatu Intent
. Oto odpowiednie klasy:
android.hardware.camera2
- Ten pakiet to główny interfejs API do sterowania aparatami na urządzeniach. Możesz go używać do robienia zdjęć lub nagrywania filmów podczas tworzenia aplikacji aparatu.
Camera
- Ta klasa to starszy, wycofany interfejs API do sterowania aparatami na urządzeniach.
SurfaceView
- Ta klasa służy do wyświetlania użytkownikowi podglądu na żywo z kamery.
MediaRecorder
- Ta klasa służy do nagrywania filmów z kamery.
Intent
- Typ działania intencji
MediaStore.ACTION_IMAGE_CAPTURE
lubMediaStore.ACTION_VIDEO_CAPTURE
może służyć do rejestrowania obrazów lub filmów bez bezpośredniego użycia obiektuCamera
.
Deklaracje w pliku manifestu
Zanim zaczniesz tworzyć aplikację za pomocą interfejsu Camera API, upewnij się, że plik manifestu zawiera odpowiednie deklaracje, które umożliwiają korzystanie ze sprzętu kamery i innych powiązanych funkcji.
- Uprawnienia dostępu do aparatu – aplikacja musi prosić o uprawnienia do korzystania z aparatu urządzenia.
<uses-permission android:name="android.permission.CAMERA" />
Uwaga: jeśli używasz aparatu wywołując istniejącą aplikację aparatu, Twoja aplikacja nie musi prosić o to uprawnienie.
- Funkcje aparatu – aplikacja musi też deklarować korzystanie z funkcji aparatu, np.:
<uses-feature android:name="android.hardware.camera" />
Listę funkcji aparatu znajdziesz w dokumentacji funkcji.
Dodanie funkcji aparatu do pliku manifestu powoduje, że Google Play uniemożliwia zainstalowanie aplikacji na urządzeniach, które nie mają aparatu lub nie obsługują określonych przez Ciebie funkcji aparatu. Więcej informacji o korzystaniu z filtrowania na podstawie funkcji w Google Play znajdziesz w artykule Google Play i filtrowanie na podstawie funkcji.
Jeśli aplikacja może używać aparatu lub funkcji aparatu do prawidłowego działania, ale nie wymaga tego, musisz określić to w pliku manifestu, dodając atrybut
android:required
i ustawiając go nafalse
:<uses-feature android:name="android.hardware.camera" android:required="false" />
- Uprawnienie do przechowywania danych – aplikacja może zapisywać obrazy lub filmy w pamięci zewnętrznej urządzenia (na karcie SD), jeśli jest przeznaczona na Androida 10 (API na poziomie 29) lub starszego i zawiera w manifeście te informacje:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- Uprawnienia do nagrywania dźwięku – aby nagrywać dźwięk podczas rejestrowania obrazu, aplikacja musi poprosić o uprawnienia do nagrywania dźwięku.
<uses-permission android:name="android.permission.RECORD_AUDIO" />
-
Dostęp do lokalizacji – jeśli Twoja aplikacja taguje obrazy informacjami o lokalizacji GPS, musisz poprosić o
ACCESS_FINE_LOCATION
dostęp. Pamiętaj, że jeśli Twoja aplikacja jest kierowana na Androida 5.0 (API na poziomie 21) lub nowszego, musisz też zadeklarować, że korzysta z GPS-u urządzenia:<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> ... <!-- Needed only if your app targets Android 5.0 (API level 21) or higher. --> <uses-feature android:name="android.hardware.location.gps" />
Więcej informacji o uzyskiwaniu lokalizacji użytkownika znajdziesz w artykule Strategie dotyczące lokalizacji.
Korzystanie z dotychczasowych aplikacji do obsługi aparatu
Szybkim sposobem na włączenie robienia zdjęć lub nagrywania filmów w aplikacji bez konieczności pisania dużej ilości dodatkowego kodu jest użycie Intent
do wywołania istniejącej aplikacji aparatu na Androida.
Szczegóły znajdziesz w lekcjach szkoleniowych Proste robienie zdjęć i Proste nagrywanie filmów.
Tworzenie aplikacji aparatu
Niektórzy deweloperzy mogą wymagać interfejsu użytkownika aparatu dostosowanego do wyglądu ich aplikacji lub zapewniającego specjalne funkcje. Napisanie własnego kodu do robienia zdjęć może zapewnić użytkownikom lepsze wrażenia.
Uwaga: ten przewodnik dotyczy starszego, wycofanego interfejsu API Camera
. W przypadku nowych lub zaawansowanych aplikacji aparatu zalecamy nowszy android.hardware.camera2
interfejs API.
Ogólne kroki tworzenia niestandardowego interfejsu aparatu dla aplikacji są następujące:
- Wykrywanie aparatu i uzyskiwanie do niego dostępu – tworzenie kodu, który sprawdza, czy istnieją aparaty, i wysyła prośbę o dostęp do nich.
- Utwórz klasę podglądu – utwórz klasę podglądu kamery, która rozszerza klasę
SurfaceView
i implementuje interfejsSurfaceHolder
. Ta klasa wyświetla podgląd obrazu na żywo z kamery. - Utwórz układ podglądu – gdy masz już klasę podglądu z kamery, utwórz układ widoku, który zawiera podgląd i wybrane przez Ciebie elementy interfejsu.
- Konfigurowanie odbiorników do przechwytywania – połącz odbiorniki z elementami sterującymi interfejsu, aby rozpocząć przechwytywanie obrazu lub filmu w odpowiedzi na działania użytkownika, takie jak naciśnięcie przycisku.
- Przechwytywanie i zapisywanie plików – skonfiguruj kod do przechwytywania zdjęć lub filmów i zapisywania danych wyjściowych.
- Zwolnij aparat – po użyciu aparatu aplikacja musi go prawidłowo zwolnić, aby mogły z niego korzystać inne aplikacje.
Sprzęt aparatu jest zasobem współdzielonym, którym należy starannie zarządzać, aby aplikacja nie wchodziła w konflikt z innymi aplikacjami, które również mogą chcieć go używać. W kolejnych sekcjach omówimy wykrywanie sprzętu aparatu, żądanie dostępu do aparatu, robienie zdjęć i nagrywanie filmów oraz zwalnianie aparatu, gdy aplikacja przestanie go używać.
Ostrzeżenie: pamiętaj, aby zwolnić obiekt Camera
, wywołując Camera.release()
, gdy aplikacja przestanie go używać. Jeśli aplikacja nie zwalnia prawidłowo kamery, wszystkie kolejne próby uzyskania do niej dostępu, w tym próby podejmowane przez samą aplikację, zakończą się niepowodzeniem i mogą spowodować zamknięcie aplikacji lub innych aplikacji.
Wykrywanie sprzętu kamery
Jeśli aplikacja nie wymaga konkretnie kamery, używając deklaracji w pliku manifestu, w czasie działania aplikacji sprawdź, czy kamera jest dostępna. Aby to sprawdzić, użyj metody PackageManager.hasSystemFeature()
, jak pokazano w poniższym przykładowym kodzie:
Kotlin
/** Check if this device has a camera */ private fun checkCameraHardware(context: Context): Boolean { if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)) { // this device has a camera return true } else { // no camera on this device return false } }
Java
/** Check if this device has a camera */ private boolean checkCameraHardware(Context context) { if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){ // this device has a camera return true; } else { // no camera on this device return false; } }
Urządzenia z Androidem mogą mieć kilka aparatów, np. tylny aparat do robienia zdjęć i przedni aparat do rozmów wideo. Android 2.3 (API na poziomie 9) i nowszy umożliwia sprawdzenie liczby aparatów dostępnych na urządzeniu za pomocą metody Camera.getNumberOfCameras()
.
Dostęp do kamer
Jeśli stwierdzisz, że urządzenie, na którym działa Twoja aplikacja, ma aparat, musisz poprosić o dostęp do niego, uzyskując instancję Camera
(chyba że używasz intencji, aby uzyskać dostęp do aparatu).
Aby uzyskać dostęp do głównego aparatu, użyj metody Camera.open()
i upewnij się, że przechwycisz wszystkie wyjątki, jak pokazano w poniższym kodzie:
Kotlin
/** A safe way to get an instance of the Camera object. */ fun getCameraInstance(): Camera? { return try { Camera.open() // attempt to get a Camera instance } catch (e: Exception) { // Camera is not available (in use or does not exist) null // returns null if camera is unavailable } }
Java
/** A safe way to get an instance of the Camera object. */ public static Camera getCameraInstance(){ Camera c = null; try { c = Camera.open(); // attempt to get a Camera instance } catch (Exception e){ // Camera is not available (in use or does not exist) } return c; // returns null if camera is unavailable }
Ostrzeżenie: podczas korzystania z funkcji Camera.open()
zawsze sprawdzaj, czy nie ma wyjątków. Jeśli nie sprawdzisz, czy występują wyjątki, gdy kamera jest używana lub nie istnieje, system zamknie Twoją aplikację.
Na urządzeniach z Androidem 2.3 (poziom interfejsu API 9) lub nowszym możesz uzyskać dostęp do konkretnych aparatów za pomocą funkcji Camera.open(int)
. Powyższy przykładowy kod uzyska dostęp do pierwszego tylnego aparatu na urządzeniu z więcej niż jednym aparatem.
Sprawdzanie funkcji aparatu
Po uzyskaniu dostępu do kamery możesz uzyskać więcej informacji o jej możliwościach, korzystając z metody Camera.getParameters()
i sprawdzając zwrócony obiekt Camera.Parameters
pod kątem obsługiwanych funkcji. Jeśli używasz interfejsu API na poziomie 9 lub wyższym, użyj Camera.getCameraInfo()
, aby określić, czy aparat znajduje się z przodu czy z tyłu urządzenia, oraz orientację obrazu.
Tworzenie zajęć w wersji przedpremierowej
Aby użytkownicy mogli skutecznie robić zdjęcia lub nagrywać filmy, muszą widzieć, co widzi aparat urządzenia. Klasa podglądu z kamery to SurfaceView
, która może wyświetlać obraz na żywo
z kamery, dzięki czemu użytkownicy mogą kadrować i robić zdjęcia lub nagrywać filmy.
Poniższy przykładowy kod pokazuje, jak utworzyć podstawową klasę podglądu z kamery, którą można uwzględnić w układzie View
. Ta klasa implementuje interfejs SurfaceHolder.Callback
, aby rejestrować zdarzenia wywołania zwrotnego związane z tworzeniem i usuwaniem widoku, które są potrzebne do przypisania danych wejściowych podglądu z kamery.
Kotlin
/** A basic Camera preview class */ class CameraPreview( context: Context, private val mCamera: Camera ) : SurfaceView(context), SurfaceHolder.Callback { private val mHolder: SurfaceHolder = holder.apply { // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. addCallback(this@CameraPreview) // deprecated setting, but required on Android versions prior to 3.0 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. mCamera.apply { try { setPreviewDisplay(holder) startPreview() } catch (e: IOException) { Log.d(TAG, "Error setting camera preview: ${e.message}") } } } override fun surfaceDestroyed(holder: SurfaceHolder) { // empty. 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 (mHolder.surface == null) { // preview surface does not exist return } // stop preview before making changes try { mCamera.stopPreview() } catch (e: Exception) { // ignore: tried to stop a non-existent preview } // set preview size and make any resize, rotate or // reformatting changes here // start preview with new settings mCamera.apply { try { setPreviewDisplay(mHolder) startPreview() } catch (e: Exception) { Log.d(TAG, "Error starting camera preview: ${e.message}") } } } }
Java
/** A basic Camera preview class */ public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder mHolder; private Camera mCamera; public CameraPreview(Context context, Camera camera) { super(context); mCamera = camera; // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. mHolder = getHolder(); mHolder.addCallback(this); // deprecated setting, but required on Android versions prior to 3.0 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void surfaceCreated(SurfaceHolder holder) { // The Surface has been created, now tell the camera where to draw the preview. try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } public void surfaceDestroyed(SurfaceHolder holder) { // empty. Take care of releasing the Camera preview in your activity. } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // 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 (mHolder.getSurface() == null){ // preview surface does not exist return; } // stop preview before making changes try { mCamera.stopPreview(); } catch (Exception e){ // ignore: tried to stop a non-existent preview } // set preview size and make any resize, rotate or // reformatting changes here // start preview with new settings try { mCamera.setPreviewDisplay(mHolder); mCamera.startPreview(); } catch (Exception e){ Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } }
Jeśli chcesz ustawić określony rozmiar podglądu z kamery, zrób to w surfaceChanged()
zgodnie z informacjami w komentarzach powyżej. Podczas ustawiania rozmiaru podglądu musisz używać wartości z getSupportedPreviewSizes()
.
Nie ustawiaj dowolnych wartości w metodzie setPreviewSize()
.
Uwaga:
Wraz z wprowadzeniem funkcji
wielu okien w Androidzie 7.0 (interfejs API na poziomie 24) i nowszych nie możesz już zakładać, że współczynnik proporcji podglądu jest taki sam jak w przypadku aktywności, nawet po wywołaniu funkcji setDisplayOrientation()
.
W zależności od rozmiaru okna i formatu obrazu może być konieczne dopasowanie podglądu z szerokiego kąta do układu w orientacji pionowej lub odwrotnie, przy użyciu układu letterbox.
Umieszczanie podglądu w układzie
Klasa podglądu z kamery, taka jak w przykładzie z poprzedniej sekcji, musi być umieszczona w układzie aktywności wraz z innymi elementami interfejsu użytkownika służącymi do robienia zdjęć lub nagrywania filmów. W tej sekcji dowiesz się, jak utworzyć podstawowy układ i aktywność podglądu.
Poniższy kod układu zapewnia bardzo podstawowy widok, który można wykorzystać do wyświetlania podglądu z kamery. W tym przykładzie element FrameLayout
ma być kontenerem klasy podglądu z kamery. Ten typ układu jest używany, aby można było nakładać dodatkowe informacje o zdjęciu lub elementy sterujące na obrazy podglądu z kamery na żywo.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" > <FrameLayout android:id="@+id/camera_preview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1" /> <Button android:id="@+id/button_capture" android:text="Capture" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> </LinearLayout>
Na większości urządzeń domyślna orientacja podglądu z kamery to orientacja pozioma. Ten przykładowy układ określa układ poziomy, a kod poniżej ustala orientację aplikacji na poziomą. Aby uprościć renderowanie podglądu z kamery, zmień orientację aktywności podglądu aplikacji na poziomą, dodając do pliku manifestu ten kod:
<activity android:name=".CameraActivity" android:label="@string/app_name" android:screenOrientation="landscape"> <!-- configure this activity to use landscape orientation --> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
Uwaga: podgląd z kamery nie musi być wyświetlany w orientacji poziomej.
Od Androida 2.2 (poziom API 8) możesz używać metody setDisplayOrientation()
, aby ustawić obrót obrazu podglądu. Aby zmienić orientację podglądu, gdy użytkownik zmieni orientację telefonu, w metodzie surfaceChanged()
klasy podglądu najpierw zatrzymaj podgląd za pomocą Camera.stopPreview()
, zmień orientację, a potem ponownie uruchom podgląd za pomocą Camera.startPreview()
.
W aktywności widoku kamery dodaj klasę podglądu do elementu FrameLayout
pokazanego w przykładzie powyżej. Aktywność kamery musi też zapewniać jej zwalnianie, gdy jest wstrzymana lub wyłączona. Przykład poniżej pokazuje, jak zmodyfikować działanie aparatu, aby dołączyć klasę podglądu pokazaną w sekcji Tworzenie klasy podglądu.
Kotlin
class CameraActivity : Activity() { private var mCamera: Camera? = null private var mPreview: CameraPreview? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Create an instance of Camera mCamera = getCameraInstance() mPreview = mCamera?.let { // Create our Preview view CameraPreview(this, it) } // Set the Preview view as the content of our activity. mPreview?.also { val preview: FrameLayout = findViewById(R.id.camera_preview) preview.addView(it) } } }
Java
public class CameraActivity extends Activity { private Camera mCamera; private CameraPreview mPreview; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // Create an instance of Camera mCamera = getCameraInstance(); // Create our Preview view and set it as the content of our activity. mPreview = new CameraPreview(this, mCamera); FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview); preview.addView(mPreview); } }
Uwaga: metoda getCameraInstance()
w przykładzie powyżej odnosi się do przykładowej metody pokazanej w artykule Dostęp do kamer.
Robienie zdjęć
Po utworzeniu klasy podglądu i układu widoku, w którym będzie ona wyświetlana, możesz zacząć rejestrować obrazy za pomocą aplikacji. W kodzie aplikacji musisz skonfigurować detektory elementów sterujących interfejsu użytkownika, aby reagowały na działania użytkownika, np. robienie zdjęć.
Aby odzyskać zdjęcie, użyj metody Camera.takePicture()
. Ta metoda przyjmuje 3 parametry, które otrzymują dane z kamery.
Aby otrzymywać dane w formacie JPEG, musisz wdrożyć interfejs Camera.PictureCallback
do odbierania danych obrazu i zapisywania ich w pliku. Poniższy kod przedstawia podstawową implementację interfejsu Camera.PictureCallback
, która umożliwia zapisanie obrazu otrzymanego z kamery.
Kotlin
private val mPicture = 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.message}") } catch (e: IOException) { Log.d(TAG, "Error accessing file: ${e.message}") } }
Java
private PictureCallback mPicture = new PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE); if (pictureFile == null){ Log.d(TAG, "Error creating media file, check storage permissions"); return; } try { FileOutputStream fos = new FileOutputStream(pictureFile); fos.write(data); fos.close(); } catch (FileNotFoundException e) { Log.d(TAG, "File not found: " + e.getMessage()); } catch (IOException e) { Log.d(TAG, "Error accessing file: " + e.getMessage()); } } };
Wywołaj metodę Camera.takePicture()
, aby uruchomić rejestrowanie obrazu. Poniższy przykładowy kod pokazuje, jak wywołać tę metodę z poziomu przycisku View.OnClickListener
.
Kotlin
val captureButton: Button = findViewById(R.id.button_capture) captureButton.setOnClickListener { // get an image from the camera mCamera?.takePicture(null, null, picture) }
Java
// Add a listener to the Capture button Button captureButton = (Button) findViewById(R.id.button_capture); captureButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { // get an image from the camera mCamera.takePicture(null, null, picture); } } );
Uwaga: mPicture
w przykładzie poniżej odnosi się do przykładowego kodu powyżej.
Ostrzeżenie: pamiętaj, aby zwolnić obiekt Camera
, wywołując Camera.release()
, gdy aplikacja przestanie go używać. Informacje o tym, jak zwolnić kamerę, znajdziesz w sekcji Zwalnianie kamery.
Nagrywanie filmów
Nagrywanie wideo za pomocą struktury Androida wymaga starannego zarządzania obiektem Camera
i koordynacji z klasą MediaRecorder
. Podczas nagrywania wideo za pomocą aplikacji Camera
musisz zarządzać połączeniami Camera.lock()
i Camera.unlock()
, aby umożliwić aplikacji MediaRecorder
dostęp do sprzętu kamery, a także połączeniami Camera.open()
i Camera.release()
.
Uwaga: począwszy od Androida 4.0 (interfejs API na poziomie 14), wywołania Camera.lock()
i Camera.unlock()
są zarządzane automatycznie.
W przeciwieństwie do robienia zdjęć za pomocą aparatu urządzenia nagrywanie filmów wymaga bardzo konkretnej kolejności wywoływania funkcji. Aby przygotować się do nagrywania filmu za pomocą aplikacji i zarejestrować go, musisz wykonać czynności w określonej kolejności, jak opisano poniżej.
- Otwórz aparat – użyj
Camera.open()
, aby uzyskać instancję obiektu aparatu. - Połącz podgląd – przygotuj podgląd obrazu z kamery na żywo, podłączając
SurfaceView
do kamery za pomocąCamera.setPreviewDisplay()
. - Rozpocznij podgląd – zadzwoń pod numer
Camera.startPreview()
, aby rozpocząć wyświetlanie obrazu na żywo z kamery. - Rozpocznij nagrywanie filmu – aby nagrać film, musisz wykonać te czynności w odpowiedniej kolejności:
- Odblokuj aparat – odblokuj aparat, aby
MediaRecorder
mógł go używać, dzwoniąc pod numerCamera.unlock()
. - Skonfiguruj MediaRecorder – wywołaj te
MediaRecorder
metody w tej kolejności. Więcej informacji znajdziesz wMediaRecorder
dokumentacji referencyjnej.setCamera()
– ustaw aparat, który ma być używany do nagrywania filmów, użyj bieżącej instancji aplikacjiCamera
.setAudioSource()
– ustaw źródło dźwięku, użyjMediaRecorder.AudioSource.CAMCORDER
.setVideoSource()
– ustaw źródło filmu, użyjMediaRecorder.VideoSource.CAMERA
.- Ustaw format wyjściowy i kodowanie filmu. W przypadku Androida 2.2 (poziom interfejsu API 8) i nowszych wersji użyj metody
MediaRecorder.setProfile
i uzyskaj instancję profilu za pomocąCamcorderProfile.get()
. W przypadku wersji Androida starszych niż 2.2 musisz ustawić format wyjściowy wideo i parametry kodowania:setOutputFormat()
– ustaw format wyjściowy, określ ustawienie domyślne lubMediaRecorder.OutputFormat.MPEG_4
.setAudioEncoder()
– ustaw typ kodowania dźwięku, określ domyślne ustawienie lubMediaRecorder.AudioEncoder.AMR_NB
.setVideoEncoder()
– ustaw typ kodowania wideo, określ ustawienie domyślne lubMediaRecorder.VideoEncoder.MPEG_4_SP
.
setOutputFile()
- Ustaw plik wyjściowy, użyjgetOutputMediaFile(MEDIA_TYPE_VIDEO).toString()
z przykładu w sekcji Zapisywanie plików multimedialnych.setPreviewDisplay()
– określ element układu podgląduSurfaceView
dla aplikacji. Użyj tego samego obiektu, który został określony w przypadku Connect Preview (Podgląd połączenia).
Uwaga: musisz wywołać te metody konfiguracji
MediaRecorder
w tej kolejności. W przeciwnym razie w aplikacji wystąpią błędy i nagrywanie się nie powiedzie. - Przygotuj MediaRecorder – przygotuj
MediaRecorder
za pomocą podanych ustawień konfiguracji, wywołującMediaRecorder.prepare()
. - Start MediaRecorder – rozpocznij nagrywanie filmu, wywołując
MediaRecorder.start()
.
- Odblokuj aparat – odblokuj aparat, aby
- Zatrzymaj nagrywanie filmu – aby pomyślnie zakończyć nagrywanie filmu, wywołaj w odpowiedniej kolejności te metody:
- Stop MediaRecorder – zatrzymuje nagrywanie filmu przez wywołanie
MediaRecorder.stop()
. - Reset MediaRecorder – opcjonalnie usuń ustawienia konfiguracji z rejestratora, wywołując
MediaRecorder.reset()
. - Release MediaRecorder – zwolnij
MediaRecorder
, wywołującMediaRecorder.release()
. - Zablokuj kamerę – zablokuj kamerę, aby przyszłe sesje
MediaRecorder
mogły jej używać, wywołującCamera.lock()
. Począwszy od Androida 4.0 (interfejs API na poziomie 14), to wywołanie nie jest wymagane, chyba że wywołanieMediaRecorder.prepare()
się nie powiedzie.
- Stop MediaRecorder – zatrzymuje nagrywanie filmu przez wywołanie
- Zatrzymaj podgląd – gdy skończysz korzystać z kamery, zatrzymaj podgląd, klikając
Camera.stopPreview()
. - Zwolnij kamerę – zwolnij kamerę, aby inne aplikacje mogły jej używać, wywołując
Camera.release()
.
Uwaga: można użyć MediaRecorder
bez wcześniejszego tworzenia podglądu z kamery i pominąć kilka pierwszych kroków tego procesu. Jednak użytkownicy zwykle wolą zobaczyć podgląd przed rozpoczęciem nagrywania, dlatego ten proces nie jest tu omawiany.
Wskazówka: jeśli aplikacja jest zwykle używana do nagrywania filmów, przed rozpoczęciem podglądu ustaw wartość parametru
setRecordingHint(boolean)
na true
. To ustawienie może skrócić czas potrzebny na rozpoczęcie nagrywania.
Konfigurowanie MediaRecorder
Podczas nagrywania wideo za pomocą klasy MediaRecorder
musisz wykonać czynności konfiguracyjne w określonej kolejności, a następnie wywołać metodę MediaRecorder.prepare()
, aby sprawdzić i wdrożyć konfigurację. Poniższy przykładowy kod pokazuje, jak prawidłowo skonfigurować i przygotować klasę MediaRecorder
do nagrywania filmów.
Kotlin
private fun prepareVideoRecorder(): Boolean { mediaRecorder = MediaRecorder() mCamera?.let { camera -> // Step 1: Unlock and set camera to MediaRecorder camera?.unlock() mediaRecorder?.run { setCamera(camera) // Step 2: Set sources setAudioSource(MediaRecorder.AudioSource.CAMCORDER) setVideoSource(MediaRecorder.VideoSource.CAMERA) // Step 3: Set a CamcorderProfile (requires API Level 8 or higher) setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)) // Step 4: Set output file setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()) // Step 5: Set the preview output setPreviewDisplay(mPreview?.holder?.surface) setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT) setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT) // Step 6: Prepare configured MediaRecorder return try { prepare() true } catch (e: IllegalStateException) { Log.d(TAG, "IllegalStateException preparing MediaRecorder: ${e.message}") releaseMediaRecorder() false } catch (e: IOException) { Log.d(TAG, "IOException preparing MediaRecorder: ${e.message}") releaseMediaRecorder() false } } } return false }
Java
private boolean prepareVideoRecorder(){ mCamera = getCameraInstance(); mediaRecorder = new MediaRecorder(); // Step 1: Unlock and set camera to MediaRecorder mCamera.unlock(); mediaRecorder.setCamera(mCamera); // Step 2: Set sources mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); // Step 3: Set a CamcorderProfile (requires API Level 8 or higher) mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)); // Step 4: Set output file mediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()); // Step 5: Set the preview output mediaRecorder.setPreviewDisplay(mPreview.getHolder().getSurface()); // Step 6: Prepare configured MediaRecorder try { mediaRecorder.prepare(); } catch (IllegalStateException e) { Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage()); releaseMediaRecorder(); return false; } catch (IOException e) { Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage()); releaseMediaRecorder(); return false; } return true; }
W przypadku Androida w wersji starszej niż 2.2 (poziom interfejsu API 8) musisz ustawić parametry formatu wyjściowego i formatu kodowania bezpośrednio, zamiast używać CamcorderProfile
. To podejście zostało zaprezentowane w tym kodzie:
Kotlin
// Step 3: Set output format and encoding (for versions prior to API Level 8) mediaRecorder?.apply { setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT) setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT) }
Java
// Step 3: Set output format and encoding (for versions prior to API Level 8) mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
Te parametry nagrywania filmów w przypadku MediaRecorder
mają ustawienia domyślne, ale możesz je dostosować do swoich potrzeb:
setVideoEncodingBitRate()
setVideoSize()
setVideoFrameRate()
setAudioEncodingBitRate()
setAudioChannels()
setAudioSamplingRate()
Uruchamianie i zatrzymywanie MediaRecorder
Rozpoczynając i zatrzymując nagrywanie wideo za pomocą klasy MediaRecorder
, musisz postępować zgodnie z poniższą kolejnością.
- Odblokowywanie kamery za pomocą
Camera.unlock()
- Skonfiguruj
MediaRecorder
zgodnie z przykładem kodu powyżej. - Rozpocznij nagrywanie za pomocą
MediaRecorder.start()
- Nagraj film
- Zatrzymywanie nagrywania za pomocą
MediaRecorder.stop()
- Zwolnij rejestrator multimediów za pomocą
MediaRecorder.release()
- Zablokuj kamerę za pomocą
Camera.lock()
Poniższy przykładowy kod pokazuje, jak podłączyć przycisk, aby prawidłowo rozpoczynać i zatrzymywać nagrywanie wideo za pomocą aparatu i klasy MediaRecorder
.
Uwaga: podczas nagrywania filmu nie zwalniaj przycisku aparatu, bo w przeciwnym razie podgląd zostanie zatrzymany.
Kotlin
var isRecording = false val captureButton: Button = findViewById(R.id.button_capture) captureButton.setOnClickListener { if (isRecording) { // stop recording and release camera mediaRecorder?.stop() // stop the recording releaseMediaRecorder() // release the MediaRecorder object mCamera?.lock() // take camera access back from MediaRecorder // inform the user that recording has stopped setCaptureButtonText("Capture") isRecording = false } else { // initialize video camera if (prepareVideoRecorder()) { // Camera is available and unlocked, MediaRecorder is prepared, // now you can start recording mediaRecorder?.start() // inform the user that recording has started setCaptureButtonText("Stop") isRecording = true } else { // prepare didn't work, release the camera releaseMediaRecorder() // inform user } } }
Java
private boolean isRecording = false; // Add a listener to the Capture button Button captureButton = (Button) findViewById(id.button_capture); captureButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { if (isRecording) { // stop recording and release camera mediaRecorder.stop(); // stop the recording releaseMediaRecorder(); // release the MediaRecorder object mCamera.lock(); // take camera access back from MediaRecorder // inform the user that recording has stopped setCaptureButtonText("Capture"); isRecording = false; } else { // initialize video camera if (prepareVideoRecorder()) { // Camera is available and unlocked, MediaRecorder is prepared, // now you can start recording mediaRecorder.start(); // inform the user that recording has started setCaptureButtonText("Stop"); isRecording = true; } else { // prepare didn't work, release the camera releaseMediaRecorder(); // inform user } } } } );
Uwaga: w powyższym przykładzie metoda prepareVideoRecorder()
odnosi się do przykładowego kodu pokazanego w artykule Konfigurowanie MediaRecorder. Ta metoda zajmuje się blokowaniem kamery, konfigurowaniem i przygotowywaniem instancji MediaRecorder
.
Zwalnianie kamery
Kamery to zasoby współdzielone przez aplikacje na urządzeniu. Aplikacja może korzystać z aparatu po uzyskaniu instancji Camera
. Musisz zachować szczególną ostrożność, aby zwolnić obiekt aparatu, gdy aplikacja przestanie go używać, i jak najszybciej po wstrzymaniu aplikacji (Activity.onPause()
). Jeśli aplikacja nie zwolni prawidłowo aparatu, wszystkie kolejne próby uzyskania do niego dostępu, w tym próby podejmowane przez samą aplikację, zakończą się niepowodzeniem i mogą spowodować zamknięcie aplikacji lub innych aplikacji.
Aby zwolnić instancję obiektu Camera
, użyj metody Camera.release()
, jak pokazano w przykładzie kodu poniżej.
Kotlin
class CameraActivity : Activity() { private var mCamera: Camera? private var preview: SurfaceView? private var mediaRecorder: MediaRecorder? override fun onPause() { super.onPause() releaseMediaRecorder() // if you are using MediaRecorder, release it first releaseCamera() // release the camera immediately on pause event } private fun releaseMediaRecorder() { mediaRecorder?.reset() // clear recorder configuration mediaRecorder?.release() // release the recorder object mediaRecorder = null mCamera?.lock() // lock camera for later use } private fun releaseCamera() { mCamera?.release() // release the camera for other applications mCamera = null } }
Java
public class CameraActivity extends Activity { private Camera mCamera; private SurfaceView preview; private MediaRecorder mediaRecorder; ... @Override protected void onPause() { super.onPause(); releaseMediaRecorder(); // if you are using MediaRecorder, release it first releaseCamera(); // release the camera immediately on pause event } private void releaseMediaRecorder(){ if (mediaRecorder != null) { mediaRecorder.reset(); // clear recorder configuration mediaRecorder.release(); // release the recorder object mediaRecorder = null; mCamera.lock(); // lock camera for later use } } private void releaseCamera(){ if (mCamera != null){ mCamera.release(); // release the camera for other applications mCamera = null; } } }
Ostrzeżenie: jeśli aplikacja nie zwalnia prawidłowo kamery, wszystkie kolejne próby uzyskania do niej dostępu, w tym próby podejmowane przez samą aplikację, zakończą się niepowodzeniem i mogą spowodować zamknięcie aplikacji lub innych aplikacji.
Zapisywanie plików multimedialnych
Pliki multimedialne utworzone przez użytkowników, takie jak zdjęcia i filmy, powinny być zapisywane w katalogu pamięci zewnętrznej urządzenia (karta SD), aby oszczędzać miejsce w systemie i umożliwiać użytkownikom dostęp do tych plików bez urządzenia. Na urządzeniu można zapisać pliki multimedialne w wielu lokalizacjach katalogów, ale jako deweloper powinieneś wziąć pod uwagę tylko 2 standardowe lokalizacje:
Environment.getExternalStoragePublicDirectory
(Environment.DIRECTORY_PICTURES
) – ta metoda zwraca standardową, udostępnioną i zalecaną lokalizację do zapisywania zdjęć i filmów. Ten katalog jest udostępniony (publiczny), więc inne aplikacje mogą łatwo znajdować, odczytywać, zmieniać i usuwać pliki zapisane w tej lokalizacji. Jeśli użytkownik odinstaluje aplikację, pliki multimedialne zapisane w tej lokalizacji nie zostaną usunięte. Aby uniknąć zakłóceń w działaniu istniejących zdjęć i filmów użytkowników, utwórz w tym katalogu podkatalog na pliki multimedialne aplikacji, jak pokazano w poniższym przykładzie kodu. Ta metoda jest dostępna w Androidzie 2.2 (poziom API 8). Aby uzyskać informacje o równoważnych wywołaniach w starszych wersjach interfejsu API, zobacz Zapisywanie plików udostępnionych.Context.getExternalFilesDir
(Environment.DIRECTORY_PICTURES
) – ta metoda zwraca standardową lokalizację do zapisywania zdjęć i filmów powiązanych z aplikacją. Jeśli aplikacja zostanie odinstalowana, wszystkie pliki zapisane w tej lokalizacji zostaną usunięte. W przypadku plików w tej lokalizacji nie są egzekwowane zabezpieczenia, a inne aplikacje mogą je odczytywać, zmieniać i usuwać.
Poniższy przykładowy kod pokazuje, jak utworzyć lokalizację File
lub Uri
dla pliku multimedialnego, którego można użyć podczas wywoływania aparatu urządzenia za pomocą Intent
lub w ramach tworzenia aplikacji aparatu.
Kotlin
val MEDIA_TYPE_IMAGE = 1 val MEDIA_TYPE_VIDEO = 2 /** Create a file Uri for saving an image or video */ private fun getOutputMediaFileUri(type: Int): Uri { return Uri.fromFile(getOutputMediaFile(type)) } /** Create a File for saving an image or video */ private fun getOutputMediaFile(type: Int): File? { // To be safe, you should check that the SDCard is mounted // using Environment.getExternalStorageState() before doing this. val mediaStorageDir = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyCameraApp" ) // This location works best if you want the created images to be shared // between applications and persist after your app has been uninstalled. // Create the storage directory if it does not exist mediaStorageDir.apply { if (!exists()) { if (!mkdirs()) { Log.d("MyCameraApp", "failed to create directory") return null } } } // Create a media file name val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date()) return when (type) { MEDIA_TYPE_IMAGE -> { File("${mediaStorageDir.path}${File.separator}IMG_$timeStamp.jpg") } MEDIA_TYPE_VIDEO -> { File("${mediaStorageDir.path}${File.separator}VID_$timeStamp.mp4") } else -> null } }
Java
public static final int MEDIA_TYPE_IMAGE = 1; public static final int MEDIA_TYPE_VIDEO = 2; /** Create a file Uri for saving an image or video */ private static Uri getOutputMediaFileUri(int type){ return Uri.fromFile(getOutputMediaFile(type)); } /** Create a File for saving an image or video */ private static File getOutputMediaFile(int type){ // To be safe, you should check that the SDCard is mounted // using Environment.getExternalStorageState() before doing this. File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), "MyCameraApp"); // This location works best if you want the created images to be shared // between applications and persist after your app has been uninstalled. // Create the storage directory if it does not exist if (! mediaStorageDir.exists()){ if (! mediaStorageDir.mkdirs()){ Log.d("MyCameraApp", "failed to create directory"); return null; } } // Create a media file name String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); File mediaFile; if (type == MEDIA_TYPE_IMAGE){ mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_"+ timeStamp + ".jpg"); } else if(type == MEDIA_TYPE_VIDEO) { mediaFile = new File(mediaStorageDir.getPath() + File.separator + "VID_"+ timeStamp + ".mp4"); } else { return null; } return mediaFile; }
Uwaga: Environment.getExternalStoragePublicDirectory()
jest dostępny w Androidzie 2.2 (poziom API 8) i nowszych wersjach. Jeśli kierujesz reklamy na urządzenia ze starszymi wersjami Androida, użyj Environment.getExternalStorageDirectory()
zamiast tego. Więcej informacji znajdziesz w artykule Zapisywanie udostępnionych plików.
Aby identyfikator URI obsługiwał profile służbowe, najpierw
przekonwertuj identyfikator URI pliku na identyfikator URI treści. Następnie dodaj identyfikator URI treści do
EXTRA_OUTPUT
elementu Intent
.
Więcej informacji o zapisywaniu plików na urządzeniu z Androidem znajdziesz w artykule Przechowywanie danych.
Funkcje aparatu
Android obsługuje wiele funkcji aparatu, którymi możesz sterować za pomocą aplikacji aparatu, takich jak format obrazu, tryb lampy błyskowej, ustawienia ostrości i wiele innych. W tej sekcji znajdziesz listę typowych funkcji aparatu i krótki opis sposobu ich używania. Większość funkcji aparatu jest dostępna i można je ustawiać za pomocą obiektu Camera.Parameters
. Istnieje jednak kilka ważnych funkcji, które wymagają czegoś więcej niż tylko prostych ustawień w Camera.Parameters
. Funkcje te są opisane w sekcjach poniżej:
Ogólne informacje o korzystaniu z funkcji sterowanych za pomocą ikony Camera.Parameters
znajdziesz w sekcji Korzystanie z funkcji aparatu. Szczegółowe informacje o korzystaniu z funkcji kontrolowanych przez obiekt parametrów aparatu znajdziesz w dokumentacji referencyjnej interfejsu API, do której prowadzą linki na liście funkcji poniżej.
Tabela 1. Typowe funkcje aparatu posortowane według poziomu interfejsu API Androida, w którym zostały wprowadzone.
Funkcja | Poziom API | Opis |
---|---|---|
Wykrywanie twarzy | 14 | wykrywać twarze na zdjęciu i używać ich do ustawiania ostrości, pomiaru światła i balansu bieli; |
Obszary pomiaru | 14 | Określ co najmniej 1 obszar na obrazie, na podstawie którego ma zostać obliczony balans bieli. |
Obszary specjalizacji | 14 | Ustawianie jednego lub większej liczby obszarów na obrazie, które mają być używane do ustawiania ostrości |
White Balance Lock |
14 | Włączanie i wyłączanie automatycznej korekty balansu bieli |
Exposure Lock |
14 | Włączanie i wyłączanie automatycznej korekty ekspozycji |
Video Snapshot |
14 | Robienie zdjęć podczas nagrywania filmu (zrzut klatki) |
Film poklatkowy | 11 | Nagrywanie klatek z ustawionymi opóźnieniami w celu nagrania filmu poklatkowego |
Multiple Cameras |
9 | Obsługa więcej niż jednego aparatu na urządzeniu, w tym przedniego i tylnego |
Focus Distance |
9 | Podaje odległości między kamerą a obiektami, które wydają się być w ognisku. |
Zoom |
8 | Ustawianie powiększenia obrazu |
Exposure
Compensation |
8 | Zwiększanie lub zmniejszanie poziomu ekspozycji na światło |
GPS Data |
5 | Dołączanie lub pomijanie danych o lokalizacji geograficznej obrazu |
White Balance |
5 | Ustaw tryb balansu bieli, który wpływa na wartości kolorów na zarejestrowanym obrazie. |
Focus Mode |
5 | Ustaw sposób, w jaki aparat ustawia ostrość na obiekcie, np. automatyczny, stały, makro lub nieskończoność. |
Scene Mode |
5 | Stosowanie gotowych trybów do określonych sytuacji fotograficznych, takich jak noc, plaża, śnieg czy światło świec. |
JPEG Quality |
5 | Ustaw poziom kompresji obrazu JPEG, który zwiększa lub zmniejsza jakość i rozmiar pliku wyjściowego obrazu. |
Flash Mode |
5 | Włączanie i wyłączanie lampy błyskowej oraz korzystanie z ustawienia automatycznego |
Color Effects |
5 | Zastosuj do zrobionego zdjęcia efekt kolorystyczny, np. czarno-biały, sepia lub negatyw. |
Anti-Banding |
5 | Zmniejsza efekt pasmowania w gradientach kolorów spowodowany kompresją JPEG. |
Picture Format |
1 | Określanie formatu pliku obrazu |
Picture Size |
1 | Określ wymiary zapisywanego obrazu w pikselach. |
Uwaga: te funkcje nie są obsługiwane na wszystkich urządzeniach ze względu na różnice w sprzęcie i implementacji oprogramowania. Informacje o sprawdzaniu dostępności funkcji na urządzeniu, na którym działa aplikacja, znajdziesz w artykule Sprawdzanie dostępności funkcji.
Sprawdzanie dostępności funkcji
Pierwszą rzeczą, którą należy zrozumieć, gdy chcesz korzystać z funkcji aparatu na urządzeniach z Androidem, jest to, że nie wszystkie funkcje aparatu są obsługiwane na wszystkich urządzeniach. Ponadto urządzenia obsługujące daną funkcję mogą obsługiwać ją na różnych poziomach lub z różnymi opcjami. Dlatego podczas tworzenia aplikacji aparatu musisz zdecydować, które funkcje aparatu chcesz obsługiwać i w jakim zakresie. Po podjęciu tej decyzji zaplanuj dodanie do aplikacji aparatu kodu, który sprawdzi, czy sprzęt urządzenia obsługuje te funkcje, i w przypadku niedostępności funkcji będzie działać bez zakłóceń.
Dostępność funkcji aparatu możesz sprawdzić, uzyskując instancję obiektu parametrów aparatu i sprawdzając odpowiednie metody. Poniższy przykładowy kod pokazuje, jak uzyskać obiekt Camera.Parameters
i sprawdzić, czy aparat obsługuje funkcję autofokusa:
Kotlin
val params: Camera.Parameters? = camera?.parameters val focusModes: List<String>? = params?.supportedFocusModes if (focusModes?.contains(Camera.Parameters.FOCUS_MODE_AUTO) == true) { // Autofocus mode is supported }
Java
// get Camera parameters Camera.Parameters params = camera.getParameters(); List<String> focusModes = params.getSupportedFocusModes(); if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) { // Autofocus mode is supported }
Możesz użyć techniki pokazanej powyżej w przypadku większości funkcji aparatu. Obiekt Camera.Parameters
udostępnia metodę getSupported...()
, is...Supported()
lub getMax...()
, która pozwala określić, czy funkcja jest obsługiwana (i w jakim zakresie).
Jeśli aplikacja wymaga określonych funkcji aparatu do prawidłowego działania, możesz je określić, dodając odpowiednie elementy do pliku manifestu aplikacji. Gdy zadeklarujesz używanie określonych funkcji aparatu, takich jak lampa błyskowa i automatyczne ustawianie ostrości, Google Play uniemożliwi zainstalowanie aplikacji na urządzeniach, które nie obsługują tych funkcji. Listę funkcji aparatu, które można zadeklarować w pliku manifestu aplikacji, znajdziesz w sekcji Funkcje w tym dokumencie.
Korzystanie z funkcji aparatu
Większość funkcji aparatu jest aktywowana i kontrolowana za pomocą obiektu Camera.Parameters
. Ten obiekt uzyskujesz, najpierw pobierając instancję obiektu Camera
, wywołując metodę getParameters()
, zmieniając zwrócony obiekt parametru, a następnie ustawiając go z powrotem w obiekcie kamery, jak pokazano w tym przykładowym kodzie:
Kotlin
val params: Camera.Parameters? = camera?.parameters params?.focusMode = Camera.Parameters.FOCUS_MODE_AUTO camera?.parameters = params
Java
// get Camera parameters Camera.Parameters params = camera.getParameters(); // set the focus mode params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); // set Camera parameters camera.setParameters(params);
Ta technika działa w przypadku niemal wszystkich funkcji aparatu, a większość parametrów można zmienić w dowolnym momencie po uzyskaniu instancji obiektu Camera
. Zmiany parametrów są zwykle od razu widoczne dla użytkownika w podglądzie z kamery w aplikacji.
Po stronie oprogramowania zmiany parametrów mogą zacząć obowiązywać dopiero po kilku klatkach, ponieważ sprzęt aparatu przetwarza nowe instrukcje, a następnie wysyła zaktualizowane dane obrazu.
Ważne: niektórych funkcji aparatu nie można dowolnie zmieniać. W szczególności zmiana rozmiaru lub orientacji podglądu z kamery wymaga najpierw zatrzymania podglądu, zmiany rozmiaru podglądu, a następnie ponownego uruchomienia podglądu. Od Androida 4.0 (API na poziomie 14) orientację podglądu można zmieniać bez ponownego uruchamiania podglądu.
Wdrożenie innych funkcji aparatu wymaga więcej kodu, m.in.:
- Pomiar i obszary ostrości
- Wykrywanie twarzy
- Film poklatkowy
W sekcjach poniżej znajdziesz krótkie omówienie sposobu wdrażania tych funkcji.
Pomiar i obszary ostrości
W niektórych sytuacjach fotograficznych automatyczne ustawianie ostrości i pomiar światła mogą nie dać oczekiwanych rezultatów. Od Androida 4.0 (API w wersji 14) aplikacja aparatu może udostępniać dodatkowe elementy sterujące, które pozwalają aplikacji lub użytkownikom określać obszary obrazu, które mają być używane do określania ustawień ostrości lub poziomu oświetlenia, i przekazywać te wartości do sprzętu aparatu w celu wykorzystania ich podczas rejestrowania zdjęć lub filmów.
Obszary pomiaru i ostrości działają bardzo podobnie do innych funkcji aparatu, ponieważ możesz nimi sterować za pomocą metod w obiekcie Camera.Parameters
. Poniższy kod pokazuje, jak ustawić 2 obszary pomiaru światła dla instancji Camera
:
Kotlin
// Create an instance of Camera camera = getCameraInstance() // set Camera parameters val params: Camera.Parameters? = camera?.parameters params?.apply { if (maxNumMeteringAreas > 0) { // check that metering areas are supported meteringAreas = ArrayList<Camera.Area>().apply { val areaRect1 = Rect(-100, -100, 100, 100) // specify an area in center of image add(Camera.Area(areaRect1, 600)) // set weight to 60% val areaRect2 = Rect(800, -1000, 1000, -800) // specify an area in upper right of image add(Camera.Area(areaRect2, 400)) // set weight to 40% } } camera?.parameters = this }
Java
// Create an instance of Camera camera = getCameraInstance(); // set Camera parameters Camera.Parameters params = camera.getParameters(); if (params.getMaxNumMeteringAreas() > 0){ // check that metering areas are supported List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>(); Rect areaRect1 = new Rect(-100, -100, 100, 100); // specify an area in center of image meteringAreas.add(new Camera.Area(areaRect1, 600)); // set weight to 60% Rect areaRect2 = new Rect(800, -1000, 1000, -800); // specify an area in upper right of image meteringAreas.add(new Camera.Area(areaRect2, 400)); // set weight to 40% params.setMeteringAreas(meteringAreas); } camera.setParameters(params);
Obiekt Camera.Area
zawiera 2 parametry danych: obiekt Rect
do określania obszaru w polu widzenia kamery oraz wartość wagi, która informuje kamerę, jakie znaczenie należy przypisać temu obszarowi w pomiarze światła lub obliczeniach ostrości.
Pole Rect
w Camera.Area
obiekcie opisuje prostokątny kształt odwzorowany na siatce o wymiarach 2000 x 2000 jednostek. Współrzędne -1000, -1000
reprezentują lewy górny róg obrazu z kamery, a współrzędne 1000, 1000 reprezentują
prawy dolny róg obrazu z kamery, jak pokazano na ilustracji poniżej.
Rysunek 1. Czerwone linie ilustrują układ współrzędnych służący do określania wartości a
Camera.Area
w podglądzie z kamery. Niebieskie pole pokazuje lokalizację i kształt obszaru kamery o wartościach Rect
333,333,667,667.
Granice tego układu współrzędnych zawsze odpowiadają zewnętrznej krawędzi obrazu widocznego w podglądzie z aparatu i nie zmniejszają się ani nie powiększają wraz z poziomem powiększenia. Podobnie obracanie podglądu obrazu za pomocą Camera.setDisplayOrientation()
nie powoduje ponownego mapowania układu współrzędnych.
Wykrywanie twarzy
W przypadku zdjęć, na których znajdują się ludzie, twarze są zwykle najważniejszą częścią obrazu i powinny być używane do określania ostrości i balansu bieli podczas robienia zdjęcia. Platforma Androida 4.0 (poziom interfejsu API 14) udostępnia interfejsy API do identyfikowania twarzy i obliczania ustawień obrazu za pomocą technologii rozpoznawania twarzy.
Uwaga: gdy funkcja wykrywania twarzy jest włączona, parametry
setWhiteBalance(String)
,
setFocusAreas(List<Camera.Area>)
i
setMeteringAreas(List<Camera.Area>)
nie działają.
Korzystanie z funkcji wykrywania twarzy w aplikacji aparatu wymaga wykonania kilku ogólnych czynności:
- Sprawdź, czy urządzenie obsługuje wykrywanie twarzy.
- Tworzenie odbiornika wykrywania twarzy
- Dodaj do obiektu kamery odbiornik wykrywania twarzy
- Rozpocznij wykrywanie twarzy po wyświetleniu podglądu (i po każdym ponownym uruchomieniu podglądu)
Funkcja wykrywania twarzy nie jest obsługiwana na wszystkich urządzeniach. Aby sprawdzić, czy ta funkcja jest obsługiwana, zadzwoń pod numer getMaxNumDetectedFaces()
. Przykład tego sprawdzania znajdziesz w przykładowej metodzie startFaceDetection()
poniżej.
Aby otrzymywać powiadomienia o wykryciu twarzy i na nie reagować, aplikacja aparatu musi ustawić odbiornik zdarzeń wykrywania twarzy. Aby to zrobić, musisz utworzyć klasę odbiornika, która implementuje interfejs Camera.FaceDetectionListener
, jak pokazano w przykładzie kodu poniżej.
Kotlin
internal class MyFaceDetectionListener : Camera.FaceDetectionListener { override fun onFaceDetection(faces: Array<Camera.Face>, camera: Camera) { if (faces.isNotEmpty()) { Log.d("FaceDetection", ("face detected: ${faces.size}" + " Face 1 Location X: ${faces[0].rect.centerX()}" + "Y: ${faces[0].rect.centerY()}")) } } }
Java
class MyFaceDetectionListener implements Camera.FaceDetectionListener { @Override public void onFaceDetection(Face[] faces, Camera camera) { if (faces.length > 0){ Log.d("FaceDetection", "face detected: "+ faces.length + " Face 1 Location X: " + faces[0].rect.centerX() + "Y: " + faces[0].rect.centerY() ); } } }
Po utworzeniu tej klasy ustaw ją w obiekcie Camera
aplikacji, jak pokazano w poniższym przykładowym kodzie:
Kotlin
camera?.setFaceDetectionListener(MyFaceDetectionListener())
Java
camera.setFaceDetectionListener(new MyFaceDetectionListener());
Aplikacja musi uruchamiać funkcję wykrywania twarzy za każdym razem, gdy rozpoczynasz (lub ponownie uruchamiasz) podgląd z kamery. Utwórz metodę rozpoczynania wykrywania twarzy, aby móc ją wywoływać w razie potrzeby, jak pokazano w poniższym przykładowym kodzie.
Kotlin
fun startFaceDetection() { // Try starting Face Detection val params = mCamera?.parameters // start face detection only *after* preview has started params?.apply { if (maxNumDetectedFaces > 0) { // camera supports face detection, so can start it: mCamera?.startFaceDetection() } } }
Java
public void startFaceDetection(){ // Try starting Face Detection Camera.Parameters params = mCamera.getParameters(); // start face detection only *after* preview has started if (params.getMaxNumDetectedFaces() > 0){ // camera supports face detection, so can start it: mCamera.startFaceDetection(); } }
Wykrywanie twarzy musisz uruchamiać za każdym razem, gdy włączasz (lub ponownie włączasz) podgląd z kamery. Jeśli używasz klasy podglądu pokazanej w sekcji Tworzenie klasy podglądu, dodaj metodę startFaceDetection()
do metod surfaceCreated()
i surfaceChanged()
w klasie podglądu, jak pokazano w przykładowym kodzie poniżej.
Kotlin
override fun surfaceCreated(holder: SurfaceHolder) { try { mCamera.setPreviewDisplay(holder) mCamera.startPreview() startFaceDetection() // start face detection feature } catch (e: IOException) { Log.d(TAG, "Error setting camera preview: ${e.message}") } } override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) { if (holder.surface == null) { // preview surface does not exist Log.d(TAG, "holder.getSurface() == null") return } try { mCamera.stopPreview() } catch (e: Exception) { // ignore: tried to stop a non-existent preview Log.d(TAG, "Error stopping camera preview: ${e.message}") } try { mCamera.setPreviewDisplay(holder) mCamera.startPreview() startFaceDetection() // re-start face detection feature } catch (e: Exception) { // ignore: tried to stop a non-existent preview Log.d(TAG, "Error starting camera preview: ${e.message}") } }
Java
public void surfaceCreated(SurfaceHolder holder) { try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); startFaceDetection(); // start face detection feature } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { if (holder.getSurface() == null){ // preview surface does not exist Log.d(TAG, "holder.getSurface() == null"); return; } try { mCamera.stopPreview(); } catch (Exception e){ // ignore: tried to stop a non-existent preview Log.d(TAG, "Error stopping camera preview: " + e.getMessage()); } try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); startFaceDetection(); // re-start face detection feature } catch (Exception e){ // ignore: tried to stop a non-existent preview Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } }
Uwaga: pamiętaj, aby wywołać tę metodę po wywołaniu metody startPreview()
. Nie próbuj uruchamiać wykrywania twarzy w metodzie onCreate()
głównej aktywności aplikacji aparatu, ponieważ podgląd nie jest jeszcze dostępny w tym momencie wykonywania aplikacji.
Film poklatkowy
Film poklatkowy umożliwia użytkownikom tworzenie klipów wideo, które łączą zdjęcia zrobione w odstępie kilku sekund lub minut. Ta funkcja korzysta z MediaRecorder
, aby nagrywać obrazy do sekwencji poklatkowej.
Aby nagrać film poklatkowy za pomocą MediaRecorder
, musisz skonfigurować obiekt rejestratora tak, jakbyś nagrywał zwykły film, ustawiając niską liczbę klatek na sekundę i używając jednego z ustawień jakości filmu poklatkowego, jak pokazano w przykładzie kodu poniżej.
Kotlin
mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH)) mediaRecorder.setCaptureRate(0.1) // capture a frame every 10 seconds
Java
// Step 3: Set a CamcorderProfile (requires API Level 8 or higher) mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH)); ... // Step 5.5: Set the video capture rate to a low number mediaRecorder.setCaptureRate(0.1); // capture a frame every 10 seconds
Te ustawienia muszą być częścią większej procedury konfiguracji usługi MediaRecorder
. Pełny przykład kodu konfiguracji znajdziesz w artykule Konfigurowanie MediaRecorder. Po zakończeniu konfiguracji rozpocznij nagrywanie filmu tak samo jak w przypadku zwykłego klipu wideo. Więcej informacji o konfigurowaniu i uruchamianiu MediaRecorder
znajdziesz w artykule Nagrywanie filmów.
Przykłady Camera2Video i HdrViewfinder pokazują, jak używać interfejsów API opisanych na tej stronie.
Pola aparatu wymagające uprawnień
Aplikacje działające na Androidzie 10 (API na poziomie 29) lub nowszym muszą mieć uprawnienie CAMERA
, aby uzyskać dostęp do wartości tych pól, które zwraca metoda getCameraCharacteristics()
:
LENS_POSE_ROTATION
LENS_POSE_TRANSLATION
LENS_INTRINSIC_CALIBRATION
LENS_RADIAL_DISTORTION
LENS_POSE_REFERENCE
LENS_DISTORTION
LENS_INFO_HYPERFOCAL_DISTANCE
LENS_INFO_MINIMUM_FOCUS_DISTANCE
SENSOR_REFERENCE_ILLUMINANT1
SENSOR_REFERENCE_ILLUMINANT2
SENSOR_CALIBRATION_TRANSFORM1
SENSOR_CALIBRATION_TRANSFORM2
SENSOR_COLOR_TRANSFORM1
SENSOR_COLOR_TRANSFORM2
SENSOR_FORWARD_MATRIX1
SENSOR_FORWARD_MATRIX2
Dodatkowy przykładowy kod
Aby pobrać przykładowe aplikacje, zapoznaj się z przykładową aplikacją Camera2Basic i oficjalną przykładową aplikacją CameraX.