Interfejs API aparatu

Platforma Android zapewnia obsługę różnych aparatów i funkcji aparatu dostępnych na urządzeniach, dzięki czemu możesz robić zdjęcia i nagrywać filmy w aplikacjach. W tym dokumencie omawiamy szybkie i proste sposoby robienia zdjęć i nagrywania filmów oraz zaawansowane metody tworzenia niestandardowych funkcji kamery.

Uwaga: na tej stronie opisaliśmy klasę Camera, która została wycofana. Zalecamy użycie biblioteki CameraX Jetpack lub, w określonych przypadkach użycia, klasy camera2. Zarówno aplikacja CameraX, jak i Aparat 2 działają na Androidzie 5.0 (poziom interfejsu API 21) i nowszych.

Zapoznaj się z tymi powiązanymi materiałami:

co należy wziąć pod uwagę

Zanim włączysz w swojej aplikacji korzystanie z kamer na urządzeniach z Androidem, zastanów się, jak będzie ona używać tej funkcji sprzętowej.

  • Wymaganie dotyczące aparatu – czy korzystanie z kamery jest tak ważne w Twojej aplikacji, że nie chcesz, by aplikacja była instalowana na urządzeniu, które nie ma kamery? Jeśli tak, zadeklaruj w pliku manifestu wymóg dotyczący kamery.
  • Szybkie zdjęcia lub niestandardowy aparat – do czego aplikacja będzie używać aparatu? Chcesz zrobić tylko szybkie zdjęcie lub klip wideo, czy Twoja aplikacja zapewni nowy sposób korzystania z kamer? Jeśli chcesz zrobić zdjęcie lub klip wideo, możesz użyć istniejących aplikacji aparatu. Informacje o opracowaniu spersonalizowanej funkcji aparatu znajdziesz w sekcji Tworzenie aplikacji aparatu.
  • Wymagania dotyczące usług działających na pierwszym planie – kiedy aplikacja wchodzi w interakcję z aparatem? Na Androidzie 9 (poziom interfejsu API 28) i nowszych aplikacje działające w tle nie mają dostępu do aparatu. Dlatego używaj aparatu, gdy aplikacja działa na pierwszym planie lub w ramach usługi na pierwszym planie.
  • Pamięć – czy obrazy lub filmy generowane przez aplikację są widoczne tylko dla niej lub udostępniane tak, by inne aplikacje, takie jak Galeria czy inne aplikacje multimedialne i społecznościowe, mogły z nich korzystać? Czy zdjęcia i filmy mają być dostępne nawet po odinstalowaniu aplikacji? Informacje o implementowaniu tych opcji znajdziesz w sekcji Zapisywanie plików multimedialnych.

Podstawy

Platforma Androida umożliwia rejestrowanie obrazów i filmów za pomocą interfejsu android.hardware.camera2 API lub aparatuIntent. Oto odpowiednie zajęcia:

android.hardware.camera2
Ten pakiet jest podstawowym interfejsem API do sterowania kamerami urządzeń. Można jej używać do robienia zdjęć i nagrywania filmów.
Camera
Ta klasa to starsza, wycofana wersja interfejsu API do sterowania kamerami urządzeń.
SurfaceView
Te zajęcia pozwalają wyświetlić użytkownikowi podgląd obrazu na żywo z kamery.
MediaRecorder
Te zajęcia służą do nagrywania obrazu wideo z kamery.
Intent
Do rejestrowania obrazów i filmów można używać typu działania intencji MediaStore.ACTION_IMAGE_CAPTURE lub MediaStore.ACTION_VIDEO_CAPTURE bez bezpośredniego użycia obiektu Camera.

Deklaracje w pliku manifestu

Przed rozpoczęciem programowania aplikacji przy użyciu interfejsu Camera API upewnij się, że plik manifestu zawiera odpowiednie deklaracje umożliwiające korzystanie ze sprzętu do obsługi aparatu i innych powiązanych funkcji.

  • Uprawnienia dotyczące aparatu – aplikacja musi prosić o pozwolenie na korzystanie z aparatu urządzenia.
    <uses-permission android:name="android.permission.CAMERA" />
    

    Uwaga: jeśli używasz kamery poprzez wywoływanie istniejącej aplikacji, aplikacja nie musi prosić o te uprawnienia.

  • Funkcje aparatu – aplikacja musi też deklarować korzystanie z funkcji aparatu, takich jak:
    <uses-feature android:name="android.hardware.camera" />
    

    Listę funkcji aparatu znajdziesz w pliku manifestu.

    Dodanie funkcji aparatu do pliku manifestu powoduje, że Google Play uniemożliwia instalację Twojej aplikacji na urządzeniach, które nie mają kamery lub nie obsługują jej określonych przez Ciebie funkcji. Więcej informacji o filtrowaniu według funkcji w Google Play znajdziesz w artykule Google Play i filtrowanie oparte na funkcjach.

    Jeśli Twoja aplikacja może używać kamery lub jej funkcji do prawidłowego działania, ale jej nie wymaga, określ to w pliku manifestu, dodając atrybut android:required i ustawiając dla niego wartość false:

    <uses-feature android:name="android.hardware.camera" android:required="false" />
    
  • Uprawnienia do pamięci – aplikacja może zapisywać obrazy i filmy w zewnętrznej pamięci urządzenia (na karcie SD), jeśli jest kierowana na Androida 10 (poziom interfejsu API 29) lub starszego i określa w pliku manifestu te dane:
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
  • Uprawnienia do nagrywania dźwięku – aby nagrywać dźwięk za pomocą funkcji nagrywania filmów, aplikacja musi poprosić o pozwolenie na nagrywanie dźwięku.
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    
  • Uprawnienia do lokalizacji – jeśli aplikacja dodaje tagi do obrazów według danych GPS o lokalizacji, musisz poprosić o uprawnienie ACCESS_FINE_LOCATION. Pamiętaj, że jeśli Twoja aplikacja jest kierowana na Androida 5.0 (poziom interfejsu API 21) lub nowszego, musisz też zadeklarować, że aplikacja korzysta z GPS-a 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 lokalizacji.

Korzystanie z istniejących aplikacji aparatu

Szybkim sposobem na robienie zdjęć i nagrywanie filmów w aplikacji bez konieczności pisania dodatkowego kodu jest użycie klasy Intent do wywołania istniejącej aplikacji aparatu na Androida. Szczegółowe informacje znajdziesz w lekcjach szkoleniowych na temat prostego robienia zdjęć i nagrywania filmów.

Tworzenie aplikacji aparatu

Niektórzy deweloperzy mogą wymagać interfejsu aparatu dostosowanego do wyglądu aplikacji lub udostępniającego specjalne funkcje. Napisanie własnego kodu zwiększa atrakcyjność aplikacji dla użytkowników.

Uwaga: ten przewodnik dotyczy starszego, wycofanego interfejsu API Camera. W przypadku nowych lub zaawansowanych aplikacji aparatów zalecany jest nowszy interfejs API android.hardware.camera2.

Ogólne kroki w celu utworzenia niestandardowego interfejsu aparatu dla aplikacji są następujące:

  • Wykrywanie kamery i uzyskiwanie do niej dostępu – utwórz kod, aby sprawdzić, czy kamery są dostępne, i poprosić o dostęp.
  • Utwórz klasę podglądu – utwórz klasę podglądu aparatu, która rozszerza zakres SurfaceView i implementuje interfejs SurfaceHolder. Podczas tych zajęć wyświetla się podgląd obrazów na żywo z kamery.
  • Utwórz układ podglądu – gdy masz już klasę podglądu aparatu, utwórz układ widoku, który obejmuje odpowiedni podgląd i elementy interfejsu użytkownika.
  • Skonfiguruj detektory do przechwytywania – połącz detektory elementów interfejsu, aby rozpocząć nagrywanie obrazu lub filmu w odpowiedzi na działania użytkownika takie jak np. naciśnięcie przycisku.
  • Przechwytywanie i zapisywanie plików – skonfiguruj kod do robienia zdjęć lub filmów oraz zapisywania wyników.
  • Zwolnij aparat – po użyciu aparatu aplikacja musi prawidłowo go zwolnić, aby mogły zacząć z niego korzystać inne aplikacje.

Sprzęt do kamery to zasób współdzielony, którym należy ostrożnie zarządzać, aby aplikacja nie kolidowała z innymi aplikacjami, które również mogą z niego korzystać. W kolejnych sekcjach omówiono sposób wykrywania sprzętu fotograficznego, prośby o dostęp do aparatu, robienie zdjęć i nagrywania filmów oraz zwalnianie aparatu po zakończeniu pracy z aplikacją.

Uwaga: pamiętaj, aby zwolnić obiekt Camera, wywołując Camera.release(), gdy aplikacja przestanie go używać. Jeśli aplikacja nie zwolni aparatu poprawnie, wszystkie kolejne próby uzyskania do niej dostępu, w tym te przez Twoją własną aplikację, zakończą się niepowodzeniem i mogą spowodować wyłączenie aplikacji lub innych aplikacji.

Kamera wykrywająca

Jeśli Twoja aplikacja nie wymaga kamery za pomocą deklaracji w pliku manifestu, sprawdź, czy kamera jest dostępna w czasie działania. Aby to sprawdzić, użyj metody PackageManager.hasSystemFeature(), jak pokazano w przykładowym kodzie poniżej:

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 (do fotografowania) i przedni do rozmów wideo. Android 2.3 (poziom interfejsu API 9) i nowsze umożliwiają sprawdzenie liczby kamer dostępnych na urządzeniu za pomocą metody Camera.getNumberOfCameras().

Dostęp do kamer

Jeśli masz pewność, że urządzenie, na którym działa aplikacja, ma kamerę, musisz poprosić o dostęp do niej, uruchamiając wystąpienie Camera (chyba że używasz zamiaru dostępu do kamery).

Aby uzyskać dostęp do aparatu głównego, użyj metody Camera.open(). Pamiętaj, aby wykrywać wszystkie wyjątki, jak pokazano w tym 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
}

Uwaga: zawsze sprawdzaj wyjątki, gdy używasz Camera.open(). Brak sprawdzenia wyjątków oznaczających, że kamera jest używana lub nie istnieje, spowoduje wyłączenie aplikacji przez system.

Na urządzeniach z Androidem 2.3 (interfejs API na poziomie 9) lub nowszym możesz uzyskać dostęp do określonych aparatów za pomocą funkcji Camera.open(int). Powyższy przykładowy kod umożliwia dostęp do pierwszego, tylnego aparatu w urządzeniu z większą liczbą aparatów.

Sprawdzam funkcje aparatu

Po uzyskaniu dostępu do aparatu możesz uzyskać więcej informacji o jego możliwościach, korzystając z metody Camera.getParameters(). Możesz też sprawdzić zwrócony obiekt Camera.Parameters pod kątem obsługiwanych funkcji. Jeśli korzystasz z 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 czy określić orientację obrazu.

Tworzenie zajęć podglądu

Aby użytkownicy mogli skutecznie robić zdjęcia i nagrywać filmy, muszą widzieć to, co widzi aparat urządzenia. Klasa podglądu z aparatu to właściwość SurfaceView, która może wyświetlać dane obrazu na żywo z aparatu, dzięki czemu użytkownicy mogą wykadrować i zrobić zdjęcie lub nagrać film.

Ten przykładowy kod pokazuje, jak utworzyć podstawową klasę podglądu aparatu, którą można uwzględnić w układzie View. Ta klasa implementuje SurfaceHolder.Callback, aby przechwytywać zdarzenia wywołania zwrotnego na potrzeby tworzenia i niszczenia widoku, które są potrzebne do przypisania danych wejściowych z podglądem 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ć konkretny rozmiar podglądu z aparatu, użyj go w metodzie surfaceChanged() zgodnie z opisem powyżej. Podczas ustawiania rozmiaru podglądu musisz użyć wartości z getSupportedPreviewSizes(). Nie ustawiaj dowolnych wartości w metodzie setPreviewSize().

Uwaga: od momentu wprowadzenia funkcji wielu okien na Androidzie w wersji 7.0 (poziom interfejsu API 24) i nowszych nie możesz już zakładać, że format podglądu jest taki sam jak Twoja aktywność nawet po wywołaniu setDisplayOrientation(). W zależności od rozmiaru okna i formatu obrazu może być konieczne dopasowanie szerokiego podglądu aparatu do układu pionowego lub na odwrót, stosując układ letterbox.

Umieszczanie podglądu w układzie

Klasa podglądu z aparatu, np. przedstawiona w poprzedniej sekcji, musi być umieszczona w układzie aktywności razem 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 działanie na potrzeby podglądu.

Ten kod układu zapewnia bardzo podstawowy widok, którego można używać do wyświetlania podglądu z kamery. W tym przykładzie element FrameLayout ma być kontenerem klasy podglądu aparatu. Ten typ układu pozwala na nałożenie dodatkowych informacji o obrazie lub elementów sterujących na obraz z podglądu na żywo z kamery.

<?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ń podgląd z aparatu jest w orientacji poziomej. Ten przykładowy układ określa układ poziomy (poziomy), a poniższy kod ustala orientację aplikacji w orientacji poziomej. Aby ułatwić sobie renderowanie podglądu z aparatu, zmień orientację działania 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 aparatu nie musi być w trybie poziomym. Od Androida 2.2 (poziom interfejsu API 8) możesz używać metody setDisplayOrientation() do ustawiania obrotu obrazu podglądu. Aby zmienić orientację podglądu podczas zmiany orientacji telefonu przez użytkownika, w ramach metody surfaceChanged() klasy podglądu najpierw zatrzymaj podgląd za pomocą Camera.stopPreview(), zmień orientację, a następnie ponownie uruchom podgląd, używając opcji Camera.startPreview().

W aktywności związanej z widokiem kamery dodaj klasę podglądu do elementu FrameLayout pokazanego w przykładzie powyżej. Działanie kamery musi też odpowiadać, czy kamera uwalnia ją w momencie wstrzymania lub wyłączenia. Poniższy przykład pokazuje, jak zmienić działanie kamery, aby dołączyć klasę podglądu widoczną w artykule 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 powyższym przykładzie odnosi się do metody pokazanej w artykule Dostęp do kamer.

Robienie zdjęć

Po utworzeniu klasy podglądu i układu widoku, w którym chcesz ją wyświetlać, możesz rozpocząć przechwytywanie obrazów w aplikacji. W kodzie aplikacji musisz skonfigurować detektory elementów interfejsu użytkownika, tak aby robiły zdjęcia i reagowały na działanie użytkownika.

Aby pobrać zdjęcie, użyj metody Camera.takePicture(). Ta metoda wykorzystuje 3 parametry, które odbierają dane z kamery. Aby odbierać dane w formacie JPEG, musisz zaimplementować interfejs Camera.PictureCallback, który umożliwi odbieranie danych obrazu i zapisanie ich w pliku. Poniższy kod przedstawia podstawową implementację interfejsu Camera.PictureCallback w celu zapisywania obrazu otrzymanego z aparatu.

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());
        }
    }
};

Aby aktywować przechwytywanie obrazu, wywołaj metodę Camera.takePicture(). Poniższy przykładowy kod pokazuje, jak wywołać tę metodę z 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: użytkownik mPicture w tym przykładzie odnosi się do przykładowego kodu powyżej.

Uwaga: pamiętaj, aby zwolnić obiekt Camera, wywołując Camera.release(), gdy aplikacja przestanie go używać. Informacje o tym, jak zwolnić aparat, znajdziesz w sekcji Zwalnianie aparatu.

Nagrywanie filmów

Nagrywanie filmów za pomocą platformy Android wymaga dokładnego zarządzania obiektem Camera i koordynacji go z klasą MediaRecorder. Jeśli chcesz nagrywać filmy za pomocą funkcji Camera, musisz zarządzać połączeniami Camera.lock() i Camera.unlock(), aby zezwolić aplikacji MediaRecorder na dostęp do sprzętu do obsługi kamery, oprócz połączeń Camera.open() i Camera.release().

Uwaga: od Androida 4.0 (poziom interfejsu API 14) wywołania Camera.lock() i Camera.unlock() są zarządzane automatycznie.

W przeciwieństwie do robienia zdjęć aparatem urządzenia, nagrywanie wideo wymaga określonej kolejności rozmów. Aby przygotować się do nagrywania filmów i nagrywać je w aplikacji, musisz wykonać określoną kolejność wykonywania, jak opisano poniżej.

  1. Otwórz kamerę – za pomocą Camera.open() możesz pobrać instancję obiektu kamery.
  2. Connect Preview (Podgląd połączenia) – przygotuj podgląd obrazu na żywo z aparatu, podłączając SurfaceView do aparatu za pomocą Camera.setPreviewDisplay().
  3. Uruchom podgląd – zadzwoń pod numer Camera.startPreview(), aby rozpocząć wyświetlanie obrazów z kamery na żywo.
  4. Rozpocznij nagrywanie filmu – aby nagrać film, musisz wykonać te czynności:
    1. Odblokuj kameręMediaRecorder może odblokować aparat, dzwoniąc pod numer Camera.unlock().
    2. Skonfiguruj MediaRecorder – wywołaj metody MediaRecorder w tej kolejności. Więcej informacji znajdziesz w dokumentacji referencyjnej MediaRecorder.
      1. setCamera() – ustaw kamerę do nagrywania wideo. Użyj obecnego wystąpienia aplikacji Camera.
      2. setAudioSource() – ustaw źródło dźwięku za pomocą MediaRecorder.AudioSource.CAMCORDER.
      3. setVideoSource() – ustaw źródło filmu za pomocą MediaRecorder.VideoSource.CAMERA.
      4. Ustaw format wyjściowy i kodowanie wideo. W przypadku Androida 2.2 (poziom interfejsu API 8) lub nowszego użyj metody MediaRecorder.setProfile, aby uzyskać instancję profilu za pomocą CamcorderProfile.get(). W przypadku Androida starszych niż 2.2 ustaw format wyjściowy i parametry kodowania:
        1. setOutputFormat() – ustaw format wyjściowy, określ ustawienie domyślne lub MediaRecorder.OutputFormat.MPEG_4.
        2. setAudioEncoder() – ustaw typ kodowania dźwięku; określ ustawienie domyślne lub MediaRecorder.AudioEncoder.AMR_NB.
        3. setVideoEncoder() – ustaw typ kodowania filmu. Wartość domyślna to MediaRecorder.VideoEncoder.MPEG_4_SP.
      5. setOutputFile() – ustaw plik wyjściowy. Użyj metody getOutputMediaFile(MEDIA_TYPE_VIDEO).toString() z przykładowej metody w sekcji Zapisywanie plików multimedialnych.
      6. setPreviewDisplay() – określa element układu podglądu SurfaceView dla aplikacji. Użyj tego samego obiektu określonego dla Connect Preview (Podgląd połączenia).

      Uwaga: metody konfiguracji MediaRecorder musisz wywoływać w tej kolejności. W przeciwnym razie aplikacja napotka błędy i nie uda się zarejestrować.

    3. Przygotowanie MediaRecorder – przygotuj MediaRecorder z podanymi ustawieniami konfiguracji, wywołując MediaRecorder.prepare().
    4. Uruchom MediaRecorder – rozpocznij nagrywanie filmu, dzwoniąc pod numer MediaRecorder.start().
  5. Zatrzymaj nagrywanie filmu – aby zakończyć nagrywanie filmu, wywołaj te metody:
    1. Zatrzymaj MediaRecorder – zatrzymaj nagrywanie filmu, dzwoniąc pod numer MediaRecorder.stop().
    2. Reset MediaRecorder (Zresetuj MediaRecorder) – opcjonalnie możesz usunąć ustawienia konfiguracji z nagrywarki, wywołując MediaRecorder.reset().
    3. Release MediaRecorder – zwolnij MediaRecorder, wywołując MediaRecorder.release().
    4. Zablokuj kamerę – zablokuj kamerę, aby w przyszłych sesjach MediaRecorder mogła z niej korzystać, dzwoniąc pod numer Camera.lock(). Od Androida 4.0 (poziom interfejsu API 14) to wywołanie nie jest wymagane, chyba że wywołanie MediaRecorder.prepare() się nie powiedzie.
  6. Zatrzymywanie podglądu – gdy zakończy się korzystanie z kamery, zatrzymaj podgląd za pomocą polecenia Camera.stopPreview().
  7. Zwolnij aparat – puść aparat, by inne aplikacje mogły z niej korzystać, wywołując Camera.release().

Uwaga: możesz użyć MediaRecorder bez wcześniejszego tworzenia podglądu aparatu i pominąć pierwsze kroki tego procesu. Użytkownicy zwykle wolą jednak zobaczyć podgląd przed rozpoczęciem nagrywania, więc proces ten nie jest tu omawiany.

Wskazówka: jeśli Twoja aplikacja zwykle używa się do nagrywania filmów, przed uruchomieniem podglądu ustaw setRecordingHint(boolean) na true. To ustawienie może przyspieszyć rozpoczęcie nagrywania.

Konfigurowanie funkcji MediaRecorder

Jeśli do nagrywania filmu używasz 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 wideo.

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 wersjach starszych niż 2.2 (poziom interfejsu API 8) należy bezpośrednio ustawiać parametry formatu wyjściowego i formatów kodowania, a nie używać właściwości CamcorderProfile. To podejście przedstawia ten kod:

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 wideo w funkcji MediaRecorder są określone domyślnie, ale możesz je dostosować do swojej aplikacji:

Uruchamianie i zatrzymywanie MediaRecorder

Rozpoczynając i zatrzymując nagrywanie wideo za pomocą klasy MediaRecorder, musisz przestrzegać określonej kolejności podanej poniżej.

  1. Odblokuj aparat, używając aplikacji Camera.unlock()
  2. Skonfiguruj MediaRecorder w sposób pokazany w powyższym przykładzie kodu
  3. Zacznij nagrywanie za pomocą aplikacji MediaRecorder.start()
  4. Nagraj film
  5. Zatrzymaj nagrywanie za pomocą aplikacji MediaRecorder.stop()
  6. Zwolnij nagrywanie multimediów, używając urządzenia MediaRecorder.release()
  7. Zablokuj kamerę za pomocą: Camera.lock()

Poniższy przykładowy kod pokazuje, jak podłączyć przycisk, aby prawidłowo rozpocząć i zatrzymać nagrywanie filmu za pomocą kamery i klasy MediaRecorder.

Uwaga: podczas nagrywania filmu nie puszczaj kamery, 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 usługi MediaRecorder. Ta metoda umożliwia zablokowanie kamery oraz skonfigurowanie i przygotowanie instancji MediaRecorder.

Zwalnianie aparatu

Aparaty to zasób, który jest udostępniany przez aplikacje na urządzeniu. Aplikacja może korzystać z kamery po otrzymaniu wystąpienia Camera. Szczególnie należy zachować ostrożność, aby zwolnić obiekt kamery, gdy aplikacja przestanie jej używać oraz gdy aplikacja zostanie wstrzymana (Activity.onPause()). Jeśli aplikacja nie zwolni kamery prawidłowo, wszystkie kolejne próby uzyskania dostępu do kamery, także przez własną aplikację, zakończą się niepowodzeniem i mogą spowodować wyłączenie aplikacji lub innych aplikacji.

Aby zwolnić wystąpienie obiektu Camera, użyj metody Camera.release(), jak pokazano w przykładowym kodzie 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;
        }
    }
}

Uwaga: jeśli aplikacja nie zwolni kamery prawidłowo, wszystkie kolejne próby jej uzyskania, w tym te przez własną aplikację, zakończą się niepowodzeniem i mogą spowodować wyłączenie aplikacji lub innych aplikacji.

Zapisuję pliki multimedialne

Pliki multimedialne utworzone przez użytkowników, takie jak zdjęcia czy filmy, powinny być zapisywane w zewnętrznym katalogu pamięci masowej (na karcie SD), aby zaoszczędzić miejsce w systemie i umożliwić użytkownikom dostęp do nich bez urządzenia. Pliki multimedialne zapisane na urządzeniu można zapisywać na urządzeniu w różnych lokalizacjach, ale istnieją tylko 2 standardowe lokalizacje.

  • Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) – ta metoda zwraca standardową, udostępnianą i zalecaną lokalizację do zapisywania zdjęć i filmów. Ten katalog jest udostępniony (publiczny), więc inne aplikacje mogą łatwo wykrywać, odczytywać, zmieniać i usuwać pliki zapisane w tej lokalizacji. Jeśli aplikacja zostanie odinstalowana przez użytkownika, pliki multimedialne zapisane w tej lokalizacji nie zostaną usunięte. Aby uniknąć zakłócania działania istniejących zdjęć i filmów użytkowników, utwórz w tym katalogu podkatalog na pliki multimedialne aplikacji, jak w przykładzie poniżej. Ta metoda jest dostępna w Androidzie 2.2 (poziom interfejsu API 8). W przypadku równoważnych wywołań we wcześniejszych wersjach interfejsu API przeczytaj artykuł Zapisywanie plików udostępnionych.
  • Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) – ta metoda zwraca standardową lokalizację do zapisywania zdjęć i filmów powiązanych z Twoją aplikacją. Jeśli Twoja aplikacja zostanie odinstalowana, wszystkie pliki zapisane w tej lokalizacji zostaną usunięte. Zabezpieczenia nie są egzekwowane w przypadku plików w tej lokalizacji, a inne aplikacje mogą je odczytywać, zmieniać i usuwać.

Poniższy przykładowy kod pokazuje, jak utworzyć lokalizację File lub Uri na potrzeby pliku multimedialnego, której można używać podczas wywoływania kamery urządzenia za pomocą interfejsu Intent lub w ramach tworzenia aplikacji Aparat.

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: interfejs Environment.getExternalStoragePublicDirectory() jest dostępny na urządzeniach z Androidem 2.2 (interfejs API na poziomie 8) lub nowszym. Jeśli kierujesz reklamy na urządzenia z wcześniejszymi wersjami Androida, użyj parametru Environment.getExternalStorageDirectory(). 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 szeroki zakres funkcji aparatu, którymi możesz sterować za pomocą aplikacji – takich jak format obrazu, tryb lampy błyskowej, ustawienia ostrości i wiele innych. W tej sekcji wymieniono popularne funkcje aparatu i pokrótce omówimy, jak z nich korzystać. Dostęp do większości funkcji kamery można uzyskać i skonfigurować za pomocą obiektu Camera.Parameters. Jest jednak kilka ważnych funkcji, które wymagają nie tylko prostych ustawień w Camera.Parameters. Te funkcje są omówione w następujących sekcjach:

Ogólne informacje o korzystaniu z funkcji kontrolowanych przez Camera.Parameters znajdziesz w sekcji Korzystanie z funkcji kamery. Więcej informacji o korzystaniu z funkcji kontrolowanych przez obiekt parametrów kamery znajdziesz, klikając na poniższej liście linki do dokumentacji API.

Tabela 1. Typowe funkcje aparatu posortowane według poziomu interfejsu Android API, na którym zostały wprowadzone.

Cecha Poziom API Opis
Wykrywanie twarzy 14 Identyfikuj ludzkie twarze na zdjęciu i używaj ich do regulacji ostrości, pomiaru i balansu bieli
Obszary pomiaru 14 Określ co najmniej jeden obszar na obrazie do obliczenia balansu bieli
Obszary specjalizacji 14 Ustawianie jednego lub większej liczby obszarów na obrazie, które mają być używane jako ostrość
White Balance Lock 14 Włączanie i wyłączanie automatycznej korekty balansu bieli
Exposure Lock 14 Wyłączanie i włączanie automatycznej korekty ekspozycji
Video Snapshot 14 Robienie zdjęcia podczas nagrywania filmu (zbieranie klatek)
Film poklatkowy 11 Nagrywaj klatki z określonymi opóźnieniami, aby nagrać film poklatkowy.
Multiple Cameras 9 Obsługa więcej niż jednej kamery w urządzeniu, w tym aparatów przednich i tylnych
Focus Distance 9 Zgłasza odległość między aparatem a ostrymi obiektami
Zoom 8 Ustaw powiększenie obrazu
Exposure Compensation 8 Zwiększ lub zmniejsz poziom ekspozycji na światło
GPS Data 5 Dołącz lub pomiń dane lokalizacji geograficznej
White Balance 5 Ustaw tryb balansu bieli, który wpływa na wartości kolorów na robionym zdjęciu
Focus Mode 5 Ustaw ostrość ostrości na obiekcie – automatycznie, na stałe, w trybie makro lub w nieskończoności.
Scene Mode 5 Stosuj tryb gotowych ustawień w określonych sytuacjach, np. w nocy, na plaży, na śniegu czy przy świecach.
JPEG Quality 5 Ustaw poziom kompresji obrazu JPEG, by zwiększyć lub zmniejszyć jakość i rozmiar wyjściowego pliku
Flash Mode 5 Włączanie i wyłączanie lampy błyskowej oraz korzystanie z ustawień automatycznych
Color Effects 5 Zastosuj efekt koloru do zdjęcia, np. czarno-biały, odcień sepii lub negatyw.
Anti-Banding 5 Zmniejsza efekt pasma w gradientach kolorów dzięki kompresji JPEG
Picture Format 1 Określ format pliku zdjęcia.
Picture Size 1 Określ wymiary zapisanego zdjęcia w pikselach.

Uwaga: te funkcje nie są obsługiwane na wszystkich urządzeniach ze względu na różnice pod względem sprzętu i implementacji oprogramowania. Informacje o sprawdzaniu dostępności funkcji na urządzeniu, na którym działa Twoja aplikacja, znajdziesz w sekcji Sprawdzanie dostępności funkcji.

Sprawdzam dostępność funkcji

Przed rozpoczęciem korzystania z funkcji aparatu na urządzeniu z Androidem należy pamiętać, że nie wszystkie funkcje aparatu są obsługiwane na każdym urządzeniu. Ponadto urządzenia, które obsługują określone funkcje, mogą obsługiwać je na różnych poziomach lub za pomocą innych opcji. Jednym z elementów procesu decyzyjnego podczas tworzenia aplikacji jest podjęcie decyzji, które funkcje kamery mają być obsługiwane i na jakim poziomie. Po podjęciu takiej decyzji umieść w aplikacji kod, który sprawdza, czy sprzęt obsługuje te funkcje, i nie działa w pełni, jeśli dana funkcja jest niedostępna.

Dostępność funkcji kamery możesz sprawdzać, pobierając wystąpienie obiektu parametrów kamery 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
}

W przypadku większości funkcji kamery możesz używać opisanej powyżej metody. Obiekt Camera.Parameters udostępnia metodę getSupported...(), is...Supported() lub getMax...() pozwalającą określić, czy funkcja jest obsługiwana (i w jakim zakresie).

Jeśli Twoja aplikacja do prawidłowego działania wymaga określonych funkcji aparatu, możesz ich wymagać, dodając je do pliku manifestu. Jeśli deklarujesz korzystanie z określonych funkcji aparatu, takich jak lampa błyskowa i autofokus, Google Play ogranicza możliwość instalowania aplikacji na urządzeniach, które nie obsługują tych funkcji. Listę funkcji aparatu, które można zadeklarować w manifeście aplikacji, znajdziesz w dokumentacji pliku manifestu.

Korzystanie z funkcji aparatu

Większość funkcji kamery włącza się i steruje przy użyciu obiektu Camera.Parameters. Obiekt uzyskuje się po pobraniu instancji obiektu Camera, wywołaniu metody getParameters(), zmianie zwróconego obiektu parametru, a następnie ustawieniu go z powrotem na obiekt kamery, jak w tym przykładzie:

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 metoda sprawdza się w przypadku niemal wszystkich funkcji kamery, a większość parametrów można zmienić w dowolnym momencie po uzyskaniu instancji obiektu Camera. Zmiany parametrów są zwykle widoczne dla użytkownika natychmiast w podglądzie aparatu w aplikacji. Po stronie oprogramowania zmiany parametrów mogą być widoczne dopiero po kilku klatkach, gdy aparat aparatu przetwarza nowe instrukcje, a potem wysyła zaktualizowane dane zdjęcia.

Ważne: niektórych funkcji aparatu nie można zmieniać według własnego uznania. Szczególnie zmiana rozmiaru lub orientacji podglądu z aparatu wymaga najpierw zatrzymania podglądu, zmiany rozmiaru podglądu, a następnie ponownego uruchomienia podglądu. Od Androida 4.0 (poziom interfejsu API 14) orientację podglądu można zmienić bez ponownego uruchamiania podglądu.

Implementacja innych funkcji aparatu wymaga więcej kodu. Są to na przykład:

  • Pomiary i obszary ostrości
  • Wykrywanie twarzy
  • Film poklatkowy

W kolejnych sekcjach znajdziesz krótkie omówienie wdrożenia tych funkcji.

Pomiary i obszary ostrości

W niektórych scenariuszach fotograficznych automatyczne ustawianie ostrości i miernik światła może nie dać oczekiwanych wyników. Począwszy od Androida 4.0 (poziom interfejsu API 14), aplikacja aparatu może udostępniać dodatkowe elementy sterujące, które pozwalają aplikacji lub użytkownikom określać obszary obrazu na potrzeby określania ustawień ostrości lub jasności i przekazywać te wartości do sprzętu aparatu do robienia zdjęć i nagrywania filmów.

Obszary pomiaru i ostrości działają bardzo podobnie do innych funkcji kamery, ponieważ możesz nimi sterować za pomocą metod dostępnych w obiekcie Camera.Parameters. Ten kod pokazuje, jak ustawić 2 obszary pomiaru światła w 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 służący do określania obszaru w polu widzenia kamery oraz wartość wagi, która informuje kamerę, jak ważny jest ten obszar podczas obliczania oświetlenia lub pomiaru ostrości.

Pole Rect w obiekcie Camera.Area opisuje prostokątny kształt zmapowany na siatkę jednostek 2000 x 2000. Współrzędne -1000, -1000 oznaczają górny i lewy róg obrazu aparatu, a współrzędne 1000 i 1000 – dolny, prawy róg obrazu, tak jak na ilustracji poniżej.

Rysunek 1. Czerwone linie pokazują układ współrzędnych określających element Camera.Area na podglądzie z aparatu. Niebieskie pole pokazuje lokalizację i kształt obszaru kamery z wartościami Rect 333 333 667 667.

Granice tego układu współrzędnych zawsze odpowiadają zewnętrznej krawędzi obrazu widocznego na podglądzie z aparatu i nie zmniejszają się ani nie rozszerzają wraz z poziomem powiększenia. I podobnie, obrót podglądu obrazu za pomocą funkcji Camera.setDisplayOrientation() nie powoduje ponownego mapowania układu współrzędnych.

Wykrywanie twarzy

W przypadku zdjęć przedstawiających osoby twarz jest zwykle najważniejszą częścią zdjęcia i to właśnie ona służy do określania ostrości i balansu bieli. Platforma Androida 4.0 (poziom interfejsu API 14) udostępnia interfejsy API do rozpoznawania twarzy i określania ustawień zdjęcia przy użyciu technologii rozpoznawania twarzy.

Uwaga: gdy funkcja wykrywania twarzy jest włączona, funkcje setWhiteBalance(String), setFocusAreas(List<Camera.Area>) i setMeteringAreas(List<Camera.Area>) nie działają.

Korzystanie z funkcji wykrywania twarzy w aplikacji aparatu wymaga kilku ogólnych czynności:

  • Sprawdź, czy urządzenie obsługuje wykrywanie twarzy
  • Tworzenie detektora wykrywania twarzy
  • Dodaj odbiornik wykrywania twarzy do obiektu aparatu
  • Uruchamiaj wykrywanie twarzy po wyświetleniu podglądu (i po każdym ponownym uruchomieniu podglądu)

Funkcja wykrywania twarzy jest niedostępna na niektórych urządzeniach. Aby sprawdzić, czy ta funkcja jest obsługiwana, wywołaj getMaxNumDetectedFaces(). Przykład kontroli znajdziesz poniżej w przykładowej metodzie startFaceDetection().

Aby otrzymywać powiadomienia i reagować na wykrywanie twarzy, aplikacja kamery musi ustawić odbiornik na zdarzenia wykrywania twarzy. Aby to zrobić, musisz utworzyć klasę detektora, która implementuje interfejs Camera.FaceDetectionListener zgodnie z poniższym przykładowym kodem.

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 przykładowym kodzie poniżej:

Kotlin

camera?.setFaceDetectionListener(MyFaceDetectionListener())

Java

camera.setFaceDetectionListener(new MyFaceDetectionListener());

Aplikacja musi uruchamiać funkcję wykrywania twarzy przy każdym uruchomieniu (lub ponownym uruchomieniu) podglądu aparatu. Utwórz metodę uruchamiania wykrywania twarzy, aby wywoływać ją zgodnie z potrzebami, jak pokazano w przykładowym kodzie poniżej.

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 uruchamiasz (lub uruchamiasz) podgląd aparatu. Jeśli używasz klasy podglądu pokazanej w artykule 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 za pomocą metody onCreate() głównego działania aplikacji aparatu, ponieważ do tego momentu podczas uruchamiania aplikacji podgląd jest niedostępny.

Film poklatkowy

Filmy poklatkowe umożliwiają użytkownikom tworzenie klipów wideo łączących zdjęcia zrobione w odstępie kilku sekund lub minut. Ta funkcja używa metody MediaRecorder do nagrywania obrazów na potrzeby sekwencji w trybie poklatkowym.

Aby nagrać film poklatkowy za pomocą MediaRecorder, musisz skonfigurować obiekt nagrywania tak, jakby nagrywał zwykły film, ustawić małą liczbę klatek na sekundę i użyć jednego z ustawień jakości filmu poklatkowego, jak pokazano w przykładzie 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 należy wprowadzić w ramach większej procedury konfiguracji MediaRecorder. Pełny przykład kodu konfiguracji znajdziesz w artykule Konfigurowanie MediaRecorder. Po zakończeniu konfiguracji możesz rozpocząć nagrywanie wideo tak, jakby nagrywano zwykły klip wideo. Więcej informacji o konfigurowaniu i uruchamianiu MediaRecorder znajdziesz w artykule o rejestrowaniu filmów.

Przykłady użycia interfejsów Camera2Video i HdrViewfinder dodatkowo pokazują wykorzystanie interfejsów API omówionych na tej stronie.

Pola dotyczące kamery, które wymagają uprawnień

Aplikacje na Androidzie 10 (poziom interfejsu API 29) lub nowszym muszą mieć uprawnienie CAMERA, aby mieć dostęp do wartości tych pól zwracanych przez metodę 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, przeczytaj przykładową aplikację Camera2 Basic i w artykule Oficjalna przykładowa aplikacja CameraX.