Interfejs API aparatu

Platforma Androida obsługuje różne aparaty i funkcje aparatu dostępne na urządzeniach, co pozwala na robienie zdjęć i nagrywanie filmów w aplikacjach. W tym dokumencie opisujemy szybkie i proste podejście do robienia zdjęć i nagrywania filmów, a także zaawansowane podejście do tworzenia niestandardowych rozwiązań dla użytkowników.

Uwaga: na tej stronie opisano wycofaną klasę Camera. Zalecamy korzystanie z biblioteki CameraX Jetpack lub, w określonych przypadkach, z klasy camera2. Zarówno AparatX, jak i Aparat2 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 pozwolisz aplikacji na korzystanie z aparatów na urządzeniach z Androidem, zastanów się, do czego Twoja aplikacja będzie używać tej funkcji sprzętowej.

  • Wymagania dotyczące aparatu – czy korzystanie z kamery jest dla Twojej aplikacji na tyle ważne, że nie chcesz jej instalować na urządzeniu, które jej nie ma? Jeśli tak, musisz w pliku manifestu zadeklarować wymagania dotyczące aparatu.
  • Szybkie zdjęcie lub niestandardowy aparat – w jaki sposób Twoja aplikacja będzie korzystać z aparatu? Chcesz po prostu zrobić krótkie zdjęcie lub klip wideo, czy Twoja aplikacja zapewni Ci nowy sposób korzystania z kamer? Jeśli chcesz szybko zrobić zdjęcie lub klip, zapoznaj się z sekcją Korzystanie z istniejących aplikacji aparatu. Informacje o rozwijaniu spersonalizowanej funkcji kamery znajdziesz w sekcji Tworzenie aplikacji kamery.
  • Wymagania dotyczące usług działających na pierwszym planie – kiedy Twoja 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 należy używać kamery, gdy aplikacja działa na pierwszym planie lub w ramach usługi na pierwszym planie.
  • Pamięć – czy generowane przez aplikację obrazy i filmy są widoczne tylko dla niej lub udostępniane tylko w taki sposób, aby mogły z nich korzystać inne aplikacje, takie jak Galeria lub inne aplikacje multimedialne? Czy chcesz, aby zdjęcia i filmy były dostępne nawet po odinstalowaniu aplikacji? Informacje o implementowaniu tych opcji znajdziesz w sekcji Zapisywanie plików multimedialnych.

Podstawy

Platforma Androida obsługuje robienie obrazów i filmów za pomocą interfejsu API android.hardware.camera2 lub aparatu Intent. Oto ich odpowiednie klasy:

android.hardware.camera2
Ten pakiet jest podstawowym interfejsem API do sterowania kamerami urządzenia. Można jej używać do robienia zdjęć i nagrywania filmów podczas tworzenia aplikacji aparatu.
Camera
Ta klasa to starszy wycofany interfejs API do sterowania kamerami urządzenia.
SurfaceView
Te zajęcia służą do prezentowania użytkownikowi podglądu z kamery na żywo.
MediaRecorder
Te zajęcia służą do nagrywania obrazu z kamery.
Intent
Działanie intencji MediaStore.ACTION_IMAGE_CAPTURE lub MediaStore.ACTION_VIDEO_CAPTURE może być używane do przechwytywania obrazów i filmów bez bezpośredniego użycia obiektu Camera.

Deklaracje w pliku manifestu

Zanim zaczniesz programować przy użyciu 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 dotyczące aparatu – aplikacja musi prosić o pozwolenie na korzystanie z kamery urządzenia.
    <uses-permission android:name="android.permission.CAMERA" />
    

    Uwaga: jeśli używasz kamery, wywołując istniejącą aplikację kamery, aplikacja nie musi prosić o to uprawnienie.

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

    Listę funkcji aparatu znajdziesz w dokumentacji funkcji pliku manifestu.

    Dodanie funkcji kamery do pliku manifestu powoduje, że Google Play uniemożliwia instalowanie Twojej aplikacji na urządzeniach, które nie mają kamery lub nie obsługują określonych przez Ciebie funkcji kamery. Więcej informacji o używaniu filtrowania na podstawie cech w Google Play znajdziesz w artykule Google Play i filtrowanie oparte na funkcjach.

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

    <uses-feature android:name="android.hardware.camera" android:required="false" />
    
  • Uprawnienie do pamięci – aplikacja może zapisywać obrazy i filmy w pamięci zewnętrznej urządzenia (karta SD), jeśli jest kierowana na Androida 10 (poziom interfejsu API 29) lub niższym i w pliku manifestu określają:
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
  • Uprawnienia do nagrywania dźwięku – w przypadku nagrywania dźwięku aplikacja musi o niego poprosić.
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    
  • Dostęp do lokalizacji – jeśli aplikacja taguje obrazy za pomocą informacji o lokalizacji GPS, musisz poprosić o uprawnienie ACCESS_FINE_LOCATION. Jeśli Twoja aplikacja jest kierowana na Androida 5.0 (poziom interfejsu API 21) lub nowszego, musisz zadeklarować, że aplikacja używa 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 informacji o lokalizacji użytkownika znajdziesz w artykule Strategie lokalizacyjne.

Korzystanie z istniejących aplikacji aparatu

Szybkim sposobem na robienie zdjęć i nagrywanie filmów w aplikacji bez dodatkowego kodu jest użycie Intent do wywoływania istniejącej aplikacji aparatu na Androidzie. Szczegółowe informacje na ten temat znajdziesz w lekcjach omawiających te zagadnienia: Proste robienie zdjęć i Proste nagrywanie filmów.

Tworzę aplikację aparatu

Niektórzy deweloperzy mogą wymagać interfejsu użytkownika kamery dostosowanego do wyglądu aplikacji lub mających specjalne funkcje. Napisanie własnego kodu do robienia zdjęć może zwiększyć wygodę użytkowników.

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

Ogólne kroki tworzenia interfejsu aparatu niestandardowego na potrzeby aplikacji są następujące:

  • Wykryj kamerę i uzyskaj do niej dostęp – utwórz kod, by sprawdzić, czy kamery istnieją, i poprosić o dostęp.
  • Utwórz klasę podglądu – utwórz klasę podglądu kamery, która rozszerza język SurfaceView i implementuje interfejs SurfaceHolder. Ta klasa wyświetla podgląd obrazu na żywo z aparatu.
  • Utwórz układ podglądu – gdy masz już klasę podglądu aparatu, utwórz układ widoku danych obejmujący odpowiedni podgląd i odpowiednie elementy interfejsu.
  • Skonfiguruj detektory przechwytywania – podłącz detektory dla elementów sterujących interfejsu, aby rozpoczynać przechwytywanie obrazów lub filmów w odpowiedzi na działania użytkownika, takie jak naciśnięcie przycisku.
  • Przechwyć i zapisz pliki – pozwala skonfigurować kod do robienia zdjęć i filmów oraz zapisywania wyników.
  • Zwalnianie kamery – po użyciu kamery aplikacja musi prawidłowo je udostępnić, aby mogły używać jej inne aplikacje.

Kamera to zasób, którym trzeba starannie zarządzać, aby aplikacja nie kolidowała z innymi aplikacjami, które też mogą z niego korzystać. W sekcjach poniżej znajdziesz informacje o tym, jak wykryć aparat, jak poprosić o dostęp do niego, jak robić zdjęcia i nagrywać filmy, a także jak odblokować aparat, gdy aplikacja z niego korzysta.

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

Wykrywam aparat

Jeśli Twoja aplikacja nie wymaga użycia kamery za pomocą deklaracji w pliku manifestu, sprawdź, czy kamera jest dostępna w czasie działania. Aby to zrobić, użyj metody PackageManager.hasSystemFeature() zgodnie z przykładowym kodem 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, na przykład tylny do zdjęć 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 ustalisz, że urządzenie, na którym działa aplikacja, ma kamerę, musisz poprosić o dostęp do niej, pobierając instancję Camera (chyba że używasz zamiaru uzyskania dostępu do kamery).

Aby uzyskać dostęp do aparatu głównego, użyj metody Camera.open(). Pamiętaj, by wykryć wyjątki, które widać w kodzie poniżej:

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: jeśli używasz Camera.open(), zawsze sprawdzaj wyjątki. Brak wyjątków, jeśli kamera jest używana lub nie istnieje, spowoduje wyłączenie aplikacji przez system.

Na urządzeniach z Androidem 2.3 (poziom interfejsu API 9) lub nowszym możesz uzyskać dostęp do określonych aparatów przy użyciu Camera.open(int). Powyższy przykładowy kod zapewni dostęp do pierwszego tylnego aparatu w urządzeniu z więcej niż 1 kamerą.

Sprawdzam funkcje 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 interfejsu Camera.getCameraInfo(), aby określić, czy aparat znajduje się z przodu czy z tyłu urządzenia, oraz określić orientację obrazu.

Tworzenie zajęć w wersji testowej

Aby użytkownicy mogli wykonywać zdjęcia i nagrywać filmy, muszą mieć dostęp do tego, co widzi aparat urządzenia. Klasa podglądu aparatu to obiekt SurfaceView, który może wyświetlać dane obrazu na żywo pochodzące z aparatu, dzięki czemu użytkownicy mogą wykadrować i nagrać 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 funkcję SurfaceHolder.Callback, aby przechwytywać zdarzenia wywołania zwrotnego do utworzenia i zniszczenia 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ć konkretny rozmiar podglądu z aparatu, ustaw go w metodzie surfaceChanged() zgodnie z opisem w komentarzach powyżej. Podczas ustawiania rozmiaru podglądu musisz użyć wartości z tabeli getSupportedPreviewSizes(). Nie ustawiaj dowolnych wartości w metodzie setPreviewSize().

Uwaga: po wprowadzeniu funkcji Wiele okien na Androidzie 7.0 (poziom interfejsu API 24) i nowszym nie możesz już zakładać, że format obrazu podglądu jest taki sam jak Twojej aktywności, nawet po wywołaniu funkcji setDisplayOrientation(). W zależności od rozmiaru okna i współczynnika proporcji może być konieczne dopasowanie szerokiego podglądu z aparatu do układu w orientacji pionowej lub na odwrót z użyciem układu letterbox.

Umieszczanie podglądu w układzie

Klasa podglądu z aparatu, jak w przykładzie w poprzedniej sekcji, musi być umieszczona w układzie działania razem z innymi elementami interfejsu użytkownika do robienia zdjęcia lub nagrywania filmów. W tej sekcji pokazujemy, jak utworzyć podstawowy układ i aktywność dla podglądu.

Poniższy kod układu udostępnia bardzo podstawowy widok, którego można użyć do wyświetlenia 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 lub elementów sterujących na obraz podglądu z aparatu 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 aparatu to pozioma. Ten przykładowy układ określa układ poziomy (poziomy), a poniższy kod dostosowuje orientację aplikacji do poziomej. Aby ułatwić renderowanie podglądu z kamery, musisz zmienić 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. Począwszy od Androida w wersji 2.2 (poziom interfejsu API 8) możesz używać metody setDisplayOrientation() do ustawiania obracania obrazu podglądu. Aby zmienić orientację podglądu, gdy użytkownik ponownie ustala orientację telefonu, w ramach metody surfaceChanged() klasy podglądu najpierw zatrzymaj podgląd przy użyciu polecenia Camera.stopPreview() i zmień orientację, a następnie ponownie uruchom podgląd za pomocą parametru Camera.startPreview().

W aktywności związanej z widokiem z kamery dodaj klasę podglądu do elementu FrameLayout widocznego w powyższym przykładzie. Aktywność kamery musi też umożliwiać jej zwolnienie po wstrzymaniu lub wyłączeniu. Poniższy przykład pokazuje, jak zmodyfikować działanie kamery, aby dołączyć klasę podglądu widoczną 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 powyższym przykładzie odnosi się do przykładowej metody pokazanej w sekcji Uzyskiwanie dostępu do kamer.

Robię zdjęcia

Po utworzeniu klasy podglądu i układu widoku, w którym chcesz ją wyświetlać, możesz zacząć robić zdjęcia za pomocą aplikacji. Musisz skonfigurować w kodzie aplikacji detektory elementów interfejsu, które będą reagować na działanie użytkownika poprzez zrobienie zdjęcia.

Aby pobrać zdjęcie, użyj metody Camera.takePicture(). Ta metoda wykorzystuje 3 parametry, które odbierają dane z kamery. Jeśli chcesz otrzymywać dane w formacie JPEG, musisz wdrożyć interfejs Camera.PictureCallback, który będzie odbierać dane obrazu i zapisać je 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ę za pomocą 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: element mPicture w poniższym przykładzie odnosi się do przykładowego kodu powyżej.

Uwaga: pamiętaj, aby zwolnić obiekt Camera, wywołując Camera.release(), gdy aplikacja go zakończy. Informacje na temat zwalniania aparatu znajdziesz w sekcji Zwalnianie aparatu.

Nagrywanie filmów

Nagrywanie filmów za pomocą platformy Androida wymaga starannego zarządzania obiektem Camera i koordynacji z klasą MediaRecorder. Podczas nagrywania filmów za pomocą funkcji Camera musisz zarządzać połączeniami Camera.lock() i Camera.unlock(), aby zezwolić MediaRecorder na dostęp nie tylko do połączeń Camera.open() i Camera.release(), ale także do sprzętu kamery.

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 filmów wymaga określonej kolejności wywołań. Aby przygotować aplikację i nagrać film, musisz wykonać określoną kolejność wykonywania, zgodnie z opisem poniżej.

  1. Otwórz aparat – użyj Camera.open(), aby pobrać instancję obiektu Camera.
  2. Podłącz podgląd – przygotuj podgląd obrazu z kamery na żywo, podłączając SurfaceView do aparatu za pomocą Camera.setPreviewDisplay().
  3. Rozpocznij podgląd – wywołaj Camera.startPreview(), by rozpocząć wyświetlanie obrazów na żywo z kamery.
  4. Rozpocznij nagrywanie filmu – aby nagrać film, musisz po kolei wykonać te czynności:
    1. Odblokuj aparat – odblokuj aparat i korzystaj z niego do MediaRecorder, dzwoniąc pod numer Camera.unlock().
    2. Skonfiguruj MediaRecorder – wywołaj poniższe metody MediaRecorder w tej kolejności. Więcej informacji znajdziesz w dokumentacji referencyjnej MediaRecorder.
      1. setCamera() – ustaw kamerę, która będzie używana do nagrywania wideo. Użyj bieżącej instancji aplikacji Camera.
      2. setAudioSource() – ustaw źródło dźwięku: MediaRecorder.AudioSource.CAMCORDER.
      3. setVideoSource() – ustaw źródło wideo, użyj opcji MediaRecorder.VideoSource.CAMERA.
      4. Ustaw format i kodowanie wyjściowe wideo. W przypadku Androida 2.2 (poziom interfejsu API 8) lub nowszego użyj metody MediaRecorder.setProfile i pobierz instancję profilu za pomocą CamcorderProfile.get(). W przypadku wersji Androida starszych niż 2.2 musisz ustawić format wyjściowy wideo 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, podaj ustawienie domyślne lub MediaRecorder.AudioEncoder.AMR_NB.
        3. setVideoEncoder() – ustaw typ kodowania wideo, określ ustawienie domyślne lub MediaRecorder.VideoEncoder.MPEG_4_SP.
      5. setOutputFile() – ustaw plik wyjściowy, korzystając z metody getOutputMediaFile(MEDIA_TYPE_VIDEO).toString() z przykładowej metody w sekcji Zapisywanie plików multimedialnych.
      6. setPreviewDisplay() – określ element układu podglądu SurfaceView dla swojej aplikacji. Użyj tego samego obiektu, który został podany w podglądzie 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. Przygotuj MediaRecorder – przygotuj MediaRecorder z podanymi ustawieniami konfiguracji, wywołując funkcję MediaRecorder.prepare().
    4. Uruchom MediaRecorder – rozpocznij nagrywanie filmu, dzwoniąc pod numer MediaRecorder.start().
  5. Zatrzymanie nagrywania filmu – aby zakończyć nagrywanie filmu, wywołaj w podanej kolejności te metody:
    1. Zatrzymaj MediaRecorder – zatrzymaj nagrywanie filmu, wywołując MediaRecorder.stop().
    2. Resetuj MediaRecorder – opcjonalnie usuń ustawienia konfiguracji z dyktafonu, wywołując MediaRecorder.reset().
    3. Zwolnij MediaRecorder – zwolnij MediaRecorder, wywołując funkcję MediaRecorder.release().
    4. Zablokuj kamerę – zablokuj kamerę, aby kolejne sesje MediaRecorder mogły z niej korzystać, wywołując funkcję Camera.lock(). Od Androida 4.0 (poziom interfejsu API 14) to wywołanie nie jest wymagane, chyba że wywołanie MediaRecorder.prepare() zakończy się niepowodzeniem.
  6. Zatrzymać podgląd – gdy zakończysz aktywność z użyciem kamery, zatrzymaj podgląd za pomocą przycisku Camera.stopPreview().
  7. Zwolnij aparat – zwolnij aparat, by mogły użyć go inne aplikacje, wywołując Camera.release().

Uwaga: możesz użyć MediaRecorder bez wcześniejszego utworzenia podglądu z aparatu i pominąć kilka pierwszych kroków. Użytkownicy zwykle wolą jednak zobaczyć podgląd przed rozpoczęciem nagrywania, więc nie mówimy o tym procesie.

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

Konfigurowanie funkcji MediaRecorder

Jeśli do nagrywania wideo 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;
}

Przed Androidem 2.2 (poziom interfejsu API 8) trzeba było ustawiać parametry formatu wyjściowego i formatów kodowania bezpośrednio, zamiast używać funkcji CamcorderProfile. To podejście zostało pokazane 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);

Podane niżej parametry nagrywania wideo w usłudze MediaRecorder mają ustawienia domyślne, ale możesz je dostosować do swojej aplikacji:

Uruchamianie i zatrzymywanie MediaRecorder

Gdy rozpoczynasz i zatrzymujesz nagrywanie wideo za pomocą klasy MediaRecorder, musisz przestrzegać określonej kolejności, którą znajdziesz poniżej.

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

Ten przykładowy kod pokazuje, jak podłączyć przycisk, aby prawidłowo rozpoczynać i zatrzymywać nagrywanie wideo za pomocą kamery i klasy MediaRecorder.

Uwaga: podczas nagrywania filmu nie puszczaj kamery, bo inaczej 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 sekcji Konfigurowanie MediaRecorder. Ta metoda obejmuje zablokowanie kamery oraz konfigurowanie i przygotowywanie instancji MediaRecorder.

Zwalniam aparat

Kamery to zasób, z którego korzystają aplikacje zainstalowane na urządzeniu. Twoja aplikacja może korzystać z kamery po uzyskaniu instancji Camera. Zwróć szczególną uwagę na zwolnienie obiektu kamery, gdy aplikacja przestanie z niego korzystać oraz gdy zostanie wstrzymana (Activity.onPause()). Jeśli aplikacja nie uruchomi prawidłowo kamery, wszystkie kolejne próby jej otwarcia, w tym te wykonywane przez Ciebie, zakończą się niepowodzeniem i mogą spowodować wyłączenie aplikacji lub innych aplikacji.

Aby uruchomić 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 uruchomi poprawnie kamery, wszystkie kolejne próby uzyskania dostępu do kamery, również te wykonywane przez Twoją 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 i filmy, powinny być zapisywane w zewnętrznym katalogu pamięci urządzenia (karta SD), aby oszczędzać miejsce w systemie i umożliwić użytkownikom dostęp do tych plików bez urządzenia. Jest wiele możliwych lokalizacji katalogów, w których można zapisać pliki multimedialne na urządzeniu, jednak są tylko 2 standardowe lokalizacje, które warto wziąć pod uwagę jako programista:

  • Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) – ta metoda zwraca standardową, udostępnianą i zalecaną lokalizację do zapisywania zdjęć i filmów. Ten katalog jest udostępniany (publiczny), dzięki czemu inne aplikacje mogą łatwo wykrywać, 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łócania pracy użytkowników przez istniejące zdjęcia i filmy, utwórz w tym katalogu podkatalog na pliki multimedialne aplikacji, jak pokazano w przykładowym poniżej kodzie. Ta metoda jest dostępna w Androidzie 2.2 (poziom interfejsu API 8). Instrukcje dotyczące równoważnych wywołań we wcześniejszych wersjach interfejsu API znajdziesz w sekcji Zapisywanie udostępnionych plików.
  • Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) – ta metoda zwraca standardową lokalizację zapisywania zdjęć i filmów powiązanych z Twoją aplikacją. Jeśli odinstalujesz aplikację, wszystkie pliki zapisane w tej lokalizacji zostaną usunięte. W tej lokalizacji zabezpieczenia nie są egzekwowane, a inne aplikacje mogą je odczytywać, zmieniać i usuwać.

Ten przykładowy kod pokazuje, jak utworzyć lokalizację File lub Uri dla pliku multimedialnego, której można użyć podczas wywoływania kamery urządzenia za pomocą 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: usługa Environment.getExternalStoragePublicDirectory() jest dostępna w Androidzie 2.2 (poziom interfejsu API 8) lub nowszym. Jeśli kierujesz reklamy na urządzenia z wcześniejszymi wersjami Androida, użyj zamiast tego 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 w Intent.

Więcej informacji o zapisywaniu plików na urządzeniu z Androidem znajdziesz w artykule Przechowywanie danych.

Funkcje aparatu

Android obsługuje szeroką gamę funkcji aparatu, którymi możesz sterować za pomocą aplikacji aparatu, takich jak format obrazu, tryb lampy błyskowej czy ustawienia ostrości. W tej części opisujemy najczęściej spotykane funkcje aparatu i wyjaśniamy krótko, jak z nich korzystać. Do większości funkcji aparatu można uzyskać dostęp i skonfigurować za pomocą obiektu Camera.Parameters. Jest jednak kilka ważnych funkcji, które wymagają więcej niż prostych ustawień w Camera.Parameters. Funkcje te są opisane w tych sekcjach:

Ogólne informacje o korzystaniu z funkcji kontrolowanych za pomocą Camera.Parameters znajdziesz w sekcji Korzystanie z funkcji aparatu. Aby uzyskać bardziej szczegółowe informacje o korzystaniu z funkcji kontrolowanych za pomocą obiektu parametrów kamery, kliknij linki na liście funkcji poniżej do dokumentacji API.

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

Cecha Poziom API Opis
Wykrywanie twarzy 14 Rozpoznawaj twarze na zdjęciu i używaj ich do ustawiania ostrości, pomiaru skuteczności i balansu bieli
Obszary pomiaru 14 Określ co najmniej jeden obszar zdjęcia do obliczania balansu bieli
Obszary specjalizacji 14 Ustaw co najmniej jeden obszar obrazu, który będzie używany do ostrości
White Balance Lock 14 Zatrzymywanie i uruchamianie automatycznej korekty balansu bieli
Exposure Lock 14 Zatrzymywanie i uruchamianie automatycznych korekt ekspozycji
Video Snapshot 14 Robienie zdjęcia podczas nagrywania filmu (zrzut klatki)
Film w trybie poklatkowym 11 Nagrywaj klatki z ustawionym opóźnieniem, aby nagrywać filmy w trybie poklatkowym
Multiple Cameras 9 Obsługa więcej niż 1 aparatu w urządzeniu, w tym przedniego i tylnego aparatu
Focus Distance 9 Informuje o odległości między aparatem a obiektami, które są na pierwszym planie
Zoom 8 Ustaw powiększenie obrazu
Exposure Compensation 8 Zwiększ lub zmniejsz poziom ekspozycji na światło
GPS Data 5 Dołączaj lub pomijaj dane o lokalizacji geograficznej na zdjęciu
White Balance 5 Ustaw tryb balansu bieli, który ma wpływ na wartości kolorów na robionym zdjęciu.
Focus Mode 5 Ustawianie ostrości aparatu na obiekcie – na przykład automatyczne, stałe, makro lub nieskończoność
Scene Mode 5 Zastosowanie gotowego trybu w określonych sytuacjach, np. w nocy, na plaży, na śniegu czy przy świecach
JPEG Quality 5 Ustaw poziom kompresji obrazu JPEG, który zwiększa lub zmniejsza jakość i rozmiar wyjściowego pliku obrazu
Flash Mode 5 Włączanie i wyłączanie lampy błyskowej oraz używanie ustawień automatycznych
Color Effects 5 Zastosuj efekt kolorystyczny do zdjęcia, taki jak czarno-biały, w odcieniu sepii lub negatyw.
Anti-Banding 5 Ogranicza efekt pasów w gradientach kolorów spowodowanych kompresją JPEG.
Picture Format 1 Określ format pliku zdjęcia
Picture Size 1 Określ wymiary zapisanego obrazu w pikselach.

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

Sprawdzam dostępność funkcji

Zanim zaczniesz korzystać z funkcji aparatu na urządzeniach z Androidem, musisz wiedzieć, że nie wszystkie z nich są obsługiwane. Dodatkowo urządzenia, które obsługują daną funkcję, mogą ją obsługiwać na różnych poziomach lub w zależności od dostępnych opcji. Dlatego podczas prac nad aplikacją aparatu musisz zdecydować, które funkcje kamery chcesz obsługiwać i na jakim poziomie. Po podjęciu decyzji warto dodać do aplikacji aparatu kod, który sprawdza, czy sprzęt obsługuje te funkcje, a jeśli jakaś funkcja jest niedostępna, kończy się niepowodzeniem.

Dostępność funkcji kamery możesz sprawdzić, 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ę autofokusu:

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 aparatu możesz skorzystać z opisanej powyżej metody. Obiekt Camera.Parameters udostępnia metodę getSupported...(), is...Supported() lub getMax...(), która określa, czy (i w jakim zakresie) dana funkcja jest obsługiwana.

Jeśli Twoja aplikacja do prawidłowego działania wymaga określonych funkcji kamery, możesz tego wymagać, dodając dodatki do pliku manifestu aplikacji. Gdy zadeklarujesz korzystanie z określonych funkcji aparatu, takich jak lampa błyskowa czy autofokus, Google Play ograniczy możliwość instalowania Twojej aplikacji na urządzeniach, które nie obsługują tych funkcji. Listę funkcji kamery, które można zadeklarować w pliku manifestu aplikacji, znajdziesz w dokumentacji funkcji manifestu.

Korzystanie z funkcji aparatu

Większość funkcji kamery jest aktywowana i sterowana za pomocą obiektu Camera.Parameters. Aby uzyskać ten obiekt, musisz najpierw pobrać wystąpienie obiektu Camera, wywołać metodę getParameters(), zmienić zwrócony obiekt parametru, a następnie ponownie go ustawić z powrotem w obiekcie Camera, 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 metoda działa w przypadku prawie wszystkich funkcji kamery, a większość parametrów można zmienić w dowolnym momencie po uzyskaniu wystąpienia obiektu Camera. Zmiany parametrów są zwykle widoczne dla użytkownika bezpośrednio w podglądzie aparatu w aplikacji. Od strony oprogramowania zmiany parametrów mogą być widoczne dopiero po wykonaniu kilku klatek, ponieważ aparat przetwarza nowe instrukcje, a następnie wysyła zaktualizowane dane obrazu.

Ważne: niektórych funkcji aparatu nie można zmieniać. W szczególności zmiana rozmiaru lub orientacji podglądu z aparatu wymaga zatrzymania podglądu, zmiany rozmiaru podglądu i ponownego uruchomienia podglądu. Od wersji Androida 4.0 (poziom interfejsu API 14) orientację podglądu można zmienić bez jej ponownego uruchamiania.

Wdrożenie innych funkcji aparatu wymaga dodatkowego kodu, takich jak:

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

Poniżej znajdziesz krótki opis implementacji tych funkcji.

Pomiar i obszary ostrości

W niektórych sytuacjach związanych z fotografowaniem automatyczne ustawianie ostrości i pomiar światła mogą nie dawać oczekiwanych rezultató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ślić obszary na zdjęciu, które będą używane do określania ustawień ostrości lub poziomu oświetlenia, i przekazywać te wartości do sprzętu aparatu, aby mógł robić zdjęcia i nagrywać filmy.

Obszary do pomiaru odległości 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 na potrzeby 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 aparat o znaczeniu tego obszaru przy obliczaniu pomiaru światła i 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ą lewy górny róg zdjęcia aparatu, a współrzędne 1000 i 1000 oznaczają dolny, prawy róg obrazu, jak widać na ilustracji poniżej.

Rysunek 1. Czerwone linie ilustrują układ współrzędnych służący do określania właściwości Camera.Area na podglądzie z aparatu. Niebieskie pole pokazuje lokalizację i kształt obszaru aparatu z Rect wartościami 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 rozszerzają wraz z powiększeniem. Podobnie obrót podglądu obrazu za pomocą funkcji Camera.setDisplayOrientation() nie mapuje układu współrzędnych.

Wykrywanie twarzy

W przypadku zdjęć przedstawiających osoby twarze są zwykle najważniejszym elementem 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 rozpoznawania twarzy i obliczania ustawień obrazu za pomocą technologii rozpoznawania twarzy.

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

Aby zacząć korzystać z funkcji wykrywania twarzy w aplikacji aparatu, musisz wykonać kilka ogólnych czynności:

  • Sprawdź, czy urządzenie obsługuje wykrywanie twarzy
  • Utwórz detektor wykrywania twarzy
  • Dodaj detektor wykrywania twarzy do obiektu kamery
  • Uruchamiaj 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, wywołaj getMaxNumDetectedFaces(). Przykład tego znajduje się w metodzie startFaceDetection() poniżej.

Aby otrzymywać powiadomienia i reagować na wykrycie twarzy, aplikacja kamery musi ustawić detektor zdarzeń związanych z wykrywaniem twarzy. Aby to zrobić, musisz utworzyć klasę detektora, która implementuje interfejs Camera.FaceDetectionListener, jak pokazano w przykładowym kodzie 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 przykładowym kodzie poniżej:

Kotlin

camera?.setFaceDetectionListener(MyFaceDetectionListener())

Java

camera.setFaceDetectionListener(new MyFaceDetectionListener());

Aplikacja musi uruchamiać funkcję wykrywania twarzy za każdym razem, gdy uruchamiasz (lub ponownie uruchamiasz) podgląd z aparatu. Utwórz metodę uruchamiania wykrywania twarzy, aby móc ją w razie potrzeby wywołać, 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 włączać za każdym razem, gdy uruchomisz (lub ponownie uruchomisz) podgląd aparatu. 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 widać 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, by wywołać tę metodę po wywołaniu startPreview(). Nie próbuj uruchamiać wykrywania twarzy w metodzie onCreate() w ramach głównej aktywności aplikacji aparatu, ponieważ do tego momentu podgląd nie jest dostępny podczas wykonywania aplikacji.

Film poklatkowy

W filmach poklatkowych użytkownicy mogą tworzyć klipy wideo łączące zdjęcia robione w odstępach kilku sekund lub minut. Ta funkcja wykorzystuje MediaRecorder do rejestrowania obrazów w sekwencji poklatkowej.

Aby za pomocą MediaRecorder nagrywać filmy w trybie poklatkowym, skonfiguruj obiekt rejestratora tak, jak w przypadku normalnego filmu, ustaw małą liczbę przechwyconych klatek na sekundę i użyj jednego z ustawień jakości w trybie poklatkowym, 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 usługi MediaRecorder. Pełny kod konfiguracji znajdziesz w artykule Konfigurowanie funkcji MediaRecorder. Po zakończeniu konfiguracji zaczniesz nagrywać film tak, jakby był to zwykły klip wideo. Więcej informacji o konfigurowaniu i uruchamianiu MediaRecorder znajdziesz w artykule Nagrywanie filmów.

Przykłady z Camera2Video i HdrViewfinder dokładniej pokazują wykorzystanie interfejsów API opisanych na tej stronie.

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

Aplikacje na Androidzie 10 (poziom interfejsu API 29) lub nowszym muszą mieć uprawnienie CAMERA, aby uzyskać 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

Przykładowe aplikacje znajdziesz w przykładach aplikacji Camera2 Basic i Oficjalnej przykładowej aplikacji CameraX.