Camera API

Android 架構支援裝置上的各種相機和相機功能,可讓您在應用程式中擷取相片和影片。本文件說明一種快速簡單的拍照與錄影方法,並概述為使用者建立自訂相機體驗的進階方法。

注意:本頁面說明已淘汰的 Camera 類別。建議使用 CameraX Jetpack 程式庫,或針對特定用途使用 camera2 類別。CameraX 和 Camera2 都適用於 Android 5.0 (API 級別 21) 以上版本。

請參閱下列相關資源:

注意事項

讓應用程式在 Android 裝置上使用相機之前,建議您先考量一些問題,說明應用程式打算如何使用這項硬體功能。

  • 相機需求:使用相機是否對應用程式很重要,因為你不希望應用程式安裝在沒有相機的裝置上?在這種情況下,您應該在資訊清單中宣告相機需求
  • 快速相片或自訂相機 - 應用程式如何使用相機?您只是想要快速拍照或錄影,還是想推出新的相機使用方式?如要快速拍攝短片或短片,請考慮「使用現有的相機應用程式」。如要瞭解如何開發自訂相機功能,請參閱「建構相機應用程式」一節。
  • 前景服務要求 - 您的應用程式何時與相機互動?在 Android 9 (API 級別 28) 以上版本中,在背景執行的應用程式無法存取相機。因此,當應用程式在前景運作或做為前景服務的一部分時,您就應該使用相機。
  • 儲存空間:應用程式產生的圖片或影片是否僅供您的應用程式檢視或分享,以供其他應用程式 (例如圖片庫或其他媒體和社交應用程式) 使用?希望即使應用程式已解除安裝,使用者仍可使用相片和影片嗎?如需瞭解如何實作這些選項,請參閱「儲存媒體檔案」一節。

基本概念

Android 架構支援透過 android.hardware.camera2 API 或相機 Intent 擷取圖片和影片。相關類別如下:

android.hardware.camera2
這個套件是控制裝置相機的主要 API,建構相機應用程式時,此工具可用於拍照或影片。
Camera
這個類別先前已淘汰,用於控制裝置相機。
SurfaceView
這個類別可用來向使用者顯示即時相機預覽畫面。
MediaRecorder
這個類別可用來錄製攝影機的影片。
Intent
MediaStore.ACTION_IMAGE_CAPTUREMediaStore.ACTION_VIDEO_CAPTURE 的意圖動作類型可用來擷取圖片或影片,而無需直接使用 Camera 物件。

資訊清單宣告

使用 Camera API 開始在應用程式中開發之前,請先確認資訊清單有適當的宣告,允許使用相機硬體和其他相關功能。

  • 相機權限 - 應用程式必須要求使用裝置相機的權限。
    <uses-permission android:name="android.permission.CAMERA" />
    

    注意:如果您叫用現有的相機應用程式來使用相機,應用程式不需要要求這項權限。

  • 相機功能 - 應用程式也必須宣告相機功能的使用權限,例如:
    <uses-feature android:name="android.hardware.camera" />
    

    如需相機功能清單,請參閱資訊清單「功能參考資料」一文。

    在資訊清單中新增相機功能會使 Google Play 防止系統將您的應用程式安裝至不含相機的裝置,或不支援您指定的相機功能。如要進一步瞭解如何搭配 Google Play 使用以功能為基礎的篩選功能,請參閱「Google Play 與依據功能篩選」。

    如果應用程式可以使用相機或相機功能正常運作,但「不需要」這項功能,那麼您應該加入 android:required 屬性,並將其設為 false,藉此在資訊清單中指定這項功能:

    <uses-feature android:name="android.hardware.camera" android:required="false" />
    
  • 儲存空間權限 - 如果應用程式指定 Android 10 (API 級別 29) 以下版本,並在資訊清單中指定以下內容,即可將圖片或影片儲存到裝置的外部儲存空間 (SD 卡)。
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
  • 「Audio Recording Permission」(音訊錄音權限) - 如果應用程式需要錄製音訊,應用程式必須要求音訊擷取權限。
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    
  • 位置存取權 - 如果應用程式使用 GPS 位置資訊標記圖片,您必須要求 ACCESS_FINE_LOCATION 權限。請注意,如果您的應用程式指定 Android 5.0 (API 級別 21) 以上版本,您還需要宣告應用程式使用裝置的 GPS:

    <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" />
    

    如要進一步瞭解如何取得使用者位置資訊,請參閱「位置策略」一文。

使用現有的相機應用程式

無需大量程式碼,即可在應用程式中拍照或影片,快速的方式是使用 Intent 叫用現有的 Android 相機應用程式。詳情請參閱「簡單拍照」和「簡單錄影」訓練課程。

建構相機應用程式

部分開發人員可能需要自訂相機使用者介面,以調整應用程式外觀或提供特殊功能。自行撰寫拍照程式碼,可以為使用者提供更棒的體驗。

注意:以下指南適用於已淘汰的舊版 Camera API。如果是全新或進階相機應用程式,建議使用較新的 android.hardware.camera2 API。

以下是為應用程式建立自訂相機介面的一般步驟:

  • 偵測及存取相機:建立程式碼來檢查相機是否存在,並要求存取權。
  • 建立預覽類別 - 建立可擴充 SurfaceView 的相機預覽類別,並實作 SurfaceHolder 介面。這個類別可預覽相機的即時圖像。
  • 建立預覽版面配置 - 取得相機預覽類別後,請建立檢視版面配置,納入您要的預覽和所需的使用者介面控制項。
  • 擷取的設定事件監聽器 - 介面控制項連線事件監聽器,用於回應使用者動作 (例如按下按鈕) 來啟動圖片或影片擷取作業。
  • 擷取及儲存檔案:設定用於拍照或影片的程式碼,以及儲存輸出內容。
  • 釋出相機 - 使用相機後,應用程式必須正確釋出相機,供其他應用程式使用。

相機硬體是一種共用資源,必須經過謹慎管理,因此應用程式不會與可能也需要使用相機的其他應用程式衝突。以下各節將說明如何偵測相機硬體、如何要求相機存取權、如何拍照或錄影,以及如何在應用程式使用完畢時釋出相機。

注意:請記得在應用程式使用完成後呼叫 Camera.release() 來釋出 Camera 物件!如果應用程式未正確釋出相機,之後的所有嘗試存取相機 (包括您自己的應用程式) 將失敗,並且可能導致您的或其他應用程式關機。

正在偵測相機硬體

如果應用程式沒有使用資訊清單宣告明確需要使用相機,您應檢查在執行階段是否有相機可用。如要執行這項檢查,請使用 PackageManager.hasSystemFeature() 方法,如以下範例程式碼所示:

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

Android 裝置可以有多個鏡頭,例如用於攝影的後置鏡頭,以及用於視訊通話的前置鏡頭。Android 2.3 (API 級別 9) 以上版本可讓您使用 Camera.getNumberOfCameras() 方法查看裝置可用的相機數量。

存取攝影機

如果您已確定執行應用程式的裝置具有相機,則必須透過取得 Camera 的例項來要求存取相機 (除非您使用意圖存取相機)。

如要存取主要相機,請使用 Camera.open() 方法,且務必擷取所有例外狀況,如以下程式碼所示:

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
}

注意:使用 Camera.open() 時,請務必檢查例外狀況。如未使用或不存在相機,系統就無法檢查例外狀況,將導致系統關閉您的應用程式。

在搭載 Android 2.3 (API 級別 9) 以上版本的裝置中,您可以使用 Camera.open(int) 存取特定相機。上述範例程式碼會在具有多部相機的裝置上存取第一個後置鏡頭。

正在檢查相機功能

獲得相機的存取權後,您可以使用 Camera.getParameters() 方法取得相機功能的詳細資訊,並檢查傳回的 Camera.Parameters 物件以瞭解支援的功能。使用 API 級別 9 或以上版本時,請透過 Camera.getCameraInfo() 判斷相機位於裝置的正面或背面,以及圖片的方向。

建立預覽類別

為了讓使用者有效拍照或錄影,使用者必須能夠看到裝置相機偵測到的內容。相機預覽類別是一種 SurfaceView,可顯示來自相機的即時影像資料,讓使用者能夠對圖片/影片加入外框及擷取影片。

以下程式碼範例說明如何建立可納入 View 版面配置的基本相機預覽類別。這個類別會實作 SurfaceHolder.Callback,以擷取回呼事件,用於建立及刪除檢視畫面,以便指派相機預覽輸入內容。

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

如要指定相機預覽的特定大小,請按照上述註解中的 surfaceChanged() 方法設定。設定預覽大小時,您「必須」使用 getSupportedPreviewSizes() 的值。請不要setPreviewSize() 方法中設定任意值。

注意:隨著 Android 7.0 (API 級別 24) 以上版本推出了 多視窗模式功能,您不再需要假設預覽的顯示比例與活動相同,即使在呼叫 setDisplayOrientation() 後也一樣。視視窗大小和長寬比而定,您可能需要將寬相機預覽畫面調整為直向版面配置;反之亦然。

在版面配置中放置預覽畫面

相機預覽類別 (例如上一節中的範例) 必須放置在活動的版面配置,以及其他用來拍照或影片的使用者介面控制項。本節說明如何建構用於預覽的基本版面配置和活動。

以下版面配置程式碼提供非常基本的檢視畫面,可用於顯示相機預覽畫面。在這個範例中,FrameLayout 元素是要是相機預覽類別的容器。您可以利用這個版面配置類型,讓其他相片資訊或控制項重疊在即時相機預覽圖片上。

<?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>

在大多數裝置上,相機預覽畫面的預設方向為橫向。這個範例版面配置指定水平 (橫向) 版面配置,而下方的程式碼則將應用程式方向修正為橫向。為簡化算繪相機預覽畫面的過程,建議您在資訊清單中加入以下內容,將應用程式的預覽活動方向變更為橫向。

<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>

注意:相機預覽不一定要處於橫向模式。從 Android 2.2 (API 級別 8) 開始,您可以使用 setDisplayOrientation() 方法來設定預覽圖片的旋轉。如要在使用者重新調整手機方向時變更預覽方向,請先在預覽類別的 surfaceChanged() 方法內透過 Camera.stopPreview() 變更方向,然後使用 Camera.startPreview() 再次啟動預覽。

在相機檢視畫面的活動中,將預覽類別新增至上例所示的 FrameLayout 元素。此外,攝影機活動也必須確保在暫停或關閉時釋出相機。以下範例說明如何修改相機活動,以附加建立預覽類別中顯示的預覽類別。

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

注意:上述範例的 getCameraInstance() 方法參照的是「存取攝影機」中顯示的範例方法。

拍照

建立預覽類別和要顯示的檢視區塊版面配置後,就可以開始使用應用程式擷取圖片。在應用程式的程式碼中,您必須為使用者介面控制項設定事件監聽器,以拍照的方式回應使用者動作。

如要擷取圖片,請使用 Camera.takePicture() 方法。這個方法需要三個參數,才能接收相機的資料。如要接收 JPEG 格式的資料,您必須實作 Camera.PictureCallback 介面才能接收圖片資料,並將其寫入檔案。以下程式碼展示了 Camera.PictureCallback 介面的基本實作,用於儲存從相機接收的圖片。

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

呼叫 Camera.takePicture() 方法觸發擷取圖片的觸發條件下列程式碼範例說明如何透過 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);
        }
    }
);

注意:以下範例中的 mPicture 成員是指上述的範例程式碼。

注意:請記得在應用程式使用完成後呼叫 Camera.release() 來釋出 Camera 物件!如要瞭解如何釋出相機,請參閱「釋出相機」一文。

拍攝影片

使用 Android 架構拍攝影片需要謹慎管理 Camera 物件,以及與 MediaRecorder 類別進行協調。使用 Camera 錄製影片時,您必須管理 Camera.lock()Camera.unlock() 呼叫,以便允許 MediaRecorder 存取相機硬體 (除了 Camera.open()Camera.release() 呼叫)。

注意:從 Android 4.0 (API 級別 14) 開始,系統會自動為您管理 Camera.lock()Camera.unlock() 呼叫。

與使用裝置相機拍照不同的是,拍攝影片需要特別的通話順序。您必須按照特定的執行順序,在應用程式中成功準備並擷取影片,如下所述。

  1. 「Open Camera」:使用 Camera.open() 取得相機物件的執行個體。
  2. 連結預覽:使用 Camera.setPreviewDisplay()SurfaceView 連結至相機,準備即時相機圖像預覽。
  3. 開始預覽:呼叫 Camera.startPreview() 即可開始顯示攝影機的即時影像。
  4. 開始錄影 - 您必須依序完成下列步驟,才能順利錄影:
    1. 解鎖相機:呼叫 Camera.unlock() 將相機解鎖,供 MediaRecorder 使用。
    2. 設定 MediaRecorder:請依序使用下列 MediaRecorder 方法呼叫。詳情請參閱 MediaRecorder 參考說明文件。
      1. setCamera():設定要用於錄影的相機,請使用應用程式目前的 Camera 執行個體。
      2. setAudioSource():設定音訊來源,使用 MediaRecorder.AudioSource.CAMCORDER
      3. setVideoSource():設定影片來源,使用 MediaRecorder.VideoSource.CAMERA
      4. 設定影片輸出格式和編碼。如果是 Android 2.2 (API 級別 8) 以上版本,請使用 MediaRecorder.setProfile 方法,並透過 CamcorderProfile.get() 取得設定檔執行個體。如果是 Android 2.2 之前的版本,您必須設定視訊輸出格式和編碼參數:
        1. setOutputFormat():設定輸出格式,指定預設設定或 MediaRecorder.OutputFormat.MPEG_4
        2. setAudioEncoder():設定音效編碼類型,指定預設設定或 MediaRecorder.AudioEncoder.AMR_NB
        3. setVideoEncoder():設定影片編碼類型,指定預設設定或 MediaRecorder.VideoEncoder.MPEG_4_SP
      5. setOutputFile():設定輸出檔案,請使用「儲存媒體檔案」一節中範例方法的 getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()
      6. setPreviewDisplay() - 指定應用程式的 SurfaceView 預覽版面配置元素。請使用您為連結預覽指定的相同物件。

      注意:您必須依照此順序呼叫這些 MediaRecorder 設定方法,否則應用程式會發生錯誤並錄製失敗。

    3. 準備 MediaRecorder:呼叫 MediaRecorder.prepare(),根據提供的配置設定準備 MediaRecorder
    4. 啟動 MediaRecorder - 呼叫 MediaRecorder.start() 開始錄製影片。
  5. 停止錄製影片 - 請依序呼叫下列方法,順利完成錄影:
    1. Stop MediaRecorder:呼叫 MediaRecorder.stop() 停止錄影。
    2. Reset MediaRecorder:您也可以選擇呼叫 MediaRecorder.reset(),從錄音工具中移除設定。
    3. Release MediaRecorder:呼叫 MediaRecorder.release() 來發布 MediaRecorder
    4. 鎖定相機:鎖定相機,允許未來的 MediaRecorder 工作階段透過呼叫 Camera.lock() 使用。從 Android 4.0 (API 級別 14) 開始,除非 MediaRecorder.prepare() 呼叫失敗,否則不需要進行這項呼叫。
  6. 「Stop the Preview」:活動使用相機結束後,請使用 Camera.stopPreview() 停止預覽。
  7. 「Release Camera」:釋出相機,即可透過呼叫 Camera.release() 讓其他應用程式使用。

注意:您可以使用 MediaRecorder,不必先建立相機預覽,並略過這項程序的前幾個步驟。不過,由於使用者通常偏好在開始記錄前預覽,因此這裡不說明這項程序。

提示:如果您的應用程式通常用於錄影,請在開始預覽之前將 setRecordingHint(boolean) 設為 true。這項設定有助於縮短開始錄製的時間。

設定 MediaRecorder

使用 MediaRecorder 類別錄製影片時,您必須按照特定順序執行設定步驟,然後呼叫 MediaRecorder.prepare() 方法檢查並實作設定。以下程式碼範例說明如何正確設定及準備用於錄製影片的 MediaRecorder 類別。

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

在 Android 2.2 (API 級別 8) 之前,您必須直接設定輸出格式和編碼格式參數,而不是使用 CamcorderProfile。以下程式碼將說明這個方法:

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

下列 MediaRecorder 的影片錄製參數已採預設設定,不過我們建議您針對應用程式調整這些設定:

啟動及停止 MediaRecorder

使用 MediaRecorder 類別開始及停止錄製影片時,您必須按照下列特定順序。

  1. 使用「Camera.unlock()」解鎖相機
  2. 按照上述程式碼範例設定 MediaRecorder
  3. 使用「MediaRecorder.start()」開始錄製
  4. 錄製影片
  5. 停止使用「MediaRecorder.stop()」錄製
  6. 使用 MediaRecorder.release() 釋放媒體錄音工具
  7. 使用「Camera.lock()」鎖定相機

下列程式碼範例示範如何使用相機和 MediaRecorder 類別,連接按鈕,以便正確開始及停止錄影。

注意:完成錄影時,請勿釋出相機,否則預覽將會停止。

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

注意:在上述範例中,prepareVideoRecorder() 方法是指「設定 MediaRecorder」中顯示的程式碼範例。這個方法會鎖定相機、設定及準備 MediaRecorder 例項。

釋出相機

相機是裝置上應用程式共用的資源。應用程式取得 Camera 的執行個體後,可以使用相機。請務必在應用程式停止使用相機物件,以及應用程式暫停時 (Activity.onPause()) 盡快釋出該物件。如果應用程式未正確釋出相機,則後續的所有嘗試存取相機 (包括自己應用程式的應用程式) 都會失敗,並可能導致您或其他應用程式停止運作。

如要釋出 Camera 物件的例項,請使用 Camera.release() 方法,如下方範例程式碼所示。

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

注意:如果應用程式未正確釋出相機,後續的所有嘗試存取相機 (包括您自己的應用程式) 將失敗,且可能導致您的或其他應用程式關機。

正在儲存媒體檔案

使用者建立的媒體檔案 (例如相片和影片) 應儲存到裝置的外部儲存空間目錄 (SD 卡),以節省系統空間,並允許使用者在沒有裝置的情況下存取這些檔案。裝置有很多可能儲存媒體檔案的目錄位置,不過其中只有兩個標準位置應做為開發人員使用:

  • Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) - 這個方法會傳回儲存相片和影片的標準、分享及建議位置。此目錄是共用 (公開),因此其他應用程式可以輕鬆找到、讀取、變更及刪除儲存在此位置的檔案。如果使用者解除安裝您的應用程式,儲存在這個位置的媒體檔案不會遭到移除。為了避免干擾使用者現有的相片和影片,建議您為此目錄中的應用程式媒體檔案建立子目錄,如以下程式碼範例所示。這個方法適用於 Android 2.2 (API 級別 8),適用於舊版 API 中的對等呼叫,請參閱「儲存共用檔案」。
  • Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) - 這個方法會傳回用於儲存應用程式相關相片和影片的標準位置。如果解除安裝應用程式,系統會移除儲存在這個位置的所有檔案。系統不會對位於這個位置的檔案強制執行安全性措施,其他應用程式可能會讀取、變更及刪除這些檔案。

下列程式碼範例說明如何為媒體檔案建立 FileUri 位置,此位置可在使用 Intent 叫用裝置相機時,或做為建構相機應用程式的一部分使用。

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

注意: Environment.getExternalStoragePublicDirectory() 適用於 Android 2.2 (API 級別 8) 以上版本。如果您指定搭載舊版 Android 的裝置,請改用 Environment.getExternalStorageDirectory()。詳情請參閱「儲存共用檔案」。

如要讓 URI 支援工作資料夾,請先 將檔案 URI 轉換為內容 URI。接著,將內容 URI 新增至 IntentEXTRA_OUTPUT

如需進一步瞭解如何在 Android 裝置上儲存檔案,請參閱資料儲存空間

相機特色

Android 支援多種可透過相機應用程式控制的相機功能,例如照片格式、閃光燈模式、對焦設定等。本節列出常見的相機功能,並簡要討論如何使用這些功能。大部分的相機功能都可以透過 Camera.Parameters 物件存取及設定。但是,Camera.Parameters 中有些重要功能需要的不只是簡單設定。我們會在以下各節說明這些功能:

如要大致瞭解如何使用透過 Camera.Parameters 控制的功能,請參閱「使用相機功能」一節。如要進一步瞭解如何使用透過相機參數物件控制的功能,請透過下方功能清單中的連結前往 API 參考說明文件。

表 1. 依導入的 Android API 級別排序常見的相機功能。

功能 API 級別 說明
臉部偵測 14 辨識相片中的人臉,並用於對焦、測光和白平衡
測光區域 14 指定圖片中的一或多個區域來計算白平衡
重點領域 14 設定圖片中的一或多個區域供焦點
White Balance Lock 14 停止或開始自動白平衡調整功能
Exposure Lock 14 停止或啟動自動曝光調整
Video Snapshot 14 錄影時拍照 (影格擷取)
縮時錄影影片 11 以設定延遲錄製影格,以錄製縮時影片
Multiple Cameras 9 在裝置上支援多部相機,包括前置和後置鏡頭
Focus Distance 9 回報相機與似乎對焦物體之間的距離
Zoom 8 設定圖片放大功能
Exposure Compensation 8 調高或調低光源曝光等級
GPS Data 5 在圖片中納入或省略地理位置資料
White Balance 5 設定白平衡模式,此模式會影響擷取圖片的色彩值
Focus Mode 5 設定相機在拍攝主體上的對焦方式,例如自動、固定、微距或無限
Scene Mode 5 針對夜晚、海灘、雪地或燭光場景等特定類型的攝影情境套用預設模式
JPEG Quality 5 設定 JPEG 圖片的壓縮等級,提高或降低圖片輸出檔案的品質和大小
Flash Mode 5 開啟、關閉或使用自動設定
Color Effects 5 為擷取的圖片套用色彩效果,例如黑白、深褐色調或負色。
Anti-Banding 5 降低因 JPEG 壓縮而產生的色彩漸層效果
Picture Format 1 指定圖片的檔案格式
Picture Size 1 指定已儲存圖片的像素尺寸

注意:由於硬體差異和軟體實作方式,因此並非所有裝置都支援這些功能。如要瞭解如何檢查應用程式執行的裝置所用功能是否可用,請參閱「檢查功能可用性」。

正在檢查功能適用情況

開始在 Android 裝置上使用相機功能時,首先必須瞭解並非所有裝置都支援所有相機功能。此外,支援特定功能的裝置可能支援不同的等級或選項。因此,在開發相機應用程式的決策過程中,您需要決定要支援的相機功能以及哪個等級。做出決定後,您應規劃將程式碼納入相機應用程式,以檢查裝置硬體是否支援這些功能;如果沒有功能可用,則優雅失敗。

您可以取得相機參數物件的執行個體並查看相關方法,藉此檢查相機功能的可用性。以下程式碼範例說明如何取得 Camera.Parameters 物件,並檢查相機是否支援自動對焦功能:

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
}

大部分的相機功能都可以使用上述技巧。Camera.Parameters 物件提供 getSupported...()is...Supported()getMax...() 方法來判斷系統是否支援某項功能 (以及指定程度)。

如果應用程式需要某些相機功能才能正常運作,您可以在應用程式資訊清單中新增這些功能以要求這些功能。當您宣告使用特定相機功能 (例如閃光燈和自動對焦) 時,Google Play 會限制應用程式在不支援這些功能的裝置上安裝。如需可在應用程式資訊清單中宣告的相機功能清單,請參閱資訊清單「 功能參考資料」。

使用相機功能

大部分的相機功能都會透過 Camera.Parameters 物件啟用及控制。如要取得此物件,請先取得 Camera 物件的例項、呼叫 getParameters() 方法、變更傳回的參數物件,然後將該物件設回相機物件,如以下範例程式碼所示:

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

這項技巧適用於幾乎所有相機功能,而且在取得 Camera 物件的例項後,大多數參數可以隨時變更。參數變更通常會立即顯示在應用程式的相機預覽畫面中。在軟體端,參數變更可能需要多個影格才會生效,因為相機硬體處理新指示,然後傳送更新後的圖片資料。

重要事項:部分相機功能無法變更。請特別注意,如要變更相機預覽畫面的大小或方向,您必須先停止預覽並變更預覽大小,然後重新啟動預覽。自 Android 4.0 (API 級別 14) 起,無須重新啟動預覽即可變更預覽方向。

其他相機功能需要更多程式碼才能實作,包括:

  • 測光和對焦區域
  • 臉部偵測
  • 縮時錄影影片

在以下各節中,我們會簡單介紹這些功能的實作方式。

測光和對焦區域

在某些攝影情境下,自動對焦和光源計量功能可能無法產生想要的結果。從 Android 4.0 (API 級別 14) 開始,相機應用程式可以提供其他控制項,讓應用程式或使用者指定圖片中的區域,用於判斷焦點或光度設定,並將這些值傳遞至相機硬體,以便用於擷取圖片或影片。

計量和對焦區域的運作方式與其他相機功能非常相似,您可以透過 Camera.Parameters 物件中的方法控制這項功能。以下程式碼示範如何為 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);

Camera.Area 物件包含兩個資料參數:一個 Rect 物件,用於指定相機視野內的區域,以及權重值,可讓相機瞭解該區域應在光計或聚焦計算時提供的重要程度。

Camera.Area 物件中的 Rect 欄位說明對應於 2000 x 2000 單位方格的矩形形狀。座標 -1000 和 -1000 表示相機圖片的左上角,-1000 則代表相機圖片右下角,而 1000 則代表相機圖片右下角。

圖 1:紅線表示在相機預覽畫面中指定 Camera.Area 的座標系統。藍色方塊顯示相機區域的位置和形狀,Rect 值為 333,333,667,667。

此座標系統的邊界一律會對應相機預覽畫面中顯示的圖片外邊緣,而且不會縮小或放大至縮放等級。同樣地,使用 Camera.setDisplayOrientation() 進行圖片預覽旋轉時,並不會重新對應座標系統。

臉部偵測

對包含人物的相片來說,臉孔通常是相片中最重要的部分,且拍攝圖片時應用來判斷對焦和白平衡。Android 4.0 (API 級別 14) 架構提供 API,可使用臉部辨識技術識別臉部及計算相片設定。

注意:臉部偵測功能執行時,setWhiteBalance(String)setFocusAreas(List<Camera.Area>)setMeteringAreas(List<Camera.Area>) 不會有任何作用。

在相機應用程式中使用臉部偵測功能,只需完成幾個一般步驟:

  • 確認裝置是否支援臉部偵測功能
  • 建立臉部偵測事件監聽器
  • 將臉部偵測事件監聽器新增至相機物件
  • 在預覽後啟動臉部偵測 (以及在每次預覽重新啟動後啟動臉部偵測)

並非所有裝置都支援臉部偵測功能。您可以呼叫 getMaxNumDetectedFaces() 來檢查系統是否支援這項功能。這項檢查的範例如以下 startFaceDetection() 範例方法所示。

為了接收通知並回應臉孔偵測,您的相機應用程式必須設定臉部偵測事件的事件監聽器。為此,您必須建立可實作 Camera.FaceDetectionListener 介面的事件監聽器類別,如以下程式碼範例所示。

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

建立這個類別後,請將其設為應用程式的 Camera 物件,如以下範例程式碼所示:

Kotlin

camera?.setFaceDetectionListener(MyFaceDetectionListener())

Java

camera.setFaceDetectionListener(new MyFaceDetectionListener());

每次啟動 (或重新啟動) 相機預覽時,應用程式都必須啟動臉部偵測功能。建立啟動臉部偵測的方法,以便視需要呼叫,如下方程式碼範例所示。

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

每次啟動 (或重新啟動) 相機預覽時,都必須啟動臉部偵測功能。如果您使用「建立預覽類別」中顯示的預覽類別,請將 startFaceDetection() 方法同時新增至預覽類別的 surfaceCreated()surfaceChanged() 方法中,如以下程式碼範例所示。

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

注意:請記得在呼叫 startPreview() 之後,呼叫這個方法。請勿嘗試在相機應用程式主要活動的 onCreate() 方法中啟動臉部偵測功能,因為在應用程式執行作業期間無法進行預覽。

縮時錄影影片

縮時攝影影片可讓使用者建立短片,然後結合數秒或分鐘間隔的相片。這項功能會使用 MediaRecorder 記錄縮時攝影序列的圖片。

如要使用 MediaRecorder 錄製縮時影片,您必須設定錄音工具物件,就像錄製一般影片一樣,將每秒擷取的影格數設為較小的數值,並使用其中一種縮時攝影品質設定,如下方的程式碼範例所示。

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

這些設定必須在 MediaRecorder 的較大設定程序中完成。如需完整的設定程式碼範例,請參閱「設定 MediaRecorder」。設定完成後,即可開始錄影,如同錄製一般短片一樣。如要進一步瞭解如何設定及執行 MediaRecorder,請參閱「擷取影片」。

Camera2VideoHdrViewfinder 範例進一步示範如何使用本頁涵蓋的 API。

需要權限的相機欄位

搭載 Android 10 (API 級別 29) 或以上版本的應用程式必須具備 CAMERA 權限,才能存取 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

其他程式碼範例

如要下載範例應用程式,請參閱 Camera2Basic 範例CameraX 官方範例應用程式