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_CAPTURE
或MediaStore.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()
呼叫。
與使用裝置相機拍照不同的是,拍攝影片需要特別的通話順序。您必須按照特定的執行順序,在應用程式中成功準備並擷取影片,如下所述。
- 「Open Camera」:使用
Camera.open()
取得相機物件的執行個體。 - 連結預覽:使用
Camera.setPreviewDisplay()
將SurfaceView
連結至相機,準備即時相機圖像預覽。 - 開始預覽:呼叫
Camera.startPreview()
即可開始顯示攝影機的即時影像。 - 開始錄影 - 您必須依序完成下列步驟,才能順利錄影:
- 解鎖相機:呼叫
Camera.unlock()
將相機解鎖,供MediaRecorder
使用。 - 設定 MediaRecorder:請依序使用下列
MediaRecorder
方法呼叫。詳情請參閱MediaRecorder
參考說明文件。setCamera()
:設定要用於錄影的相機,請使用應用程式目前的Camera
執行個體。setAudioSource()
:設定音訊來源,使用MediaRecorder.AudioSource.CAMCORDER
。setVideoSource()
:設定影片來源,使用MediaRecorder.VideoSource.CAMERA
。- 設定影片輸出格式和編碼。如果是 Android 2.2 (API 級別 8) 以上版本,請使用
MediaRecorder.setProfile
方法,並透過CamcorderProfile.get()
取得設定檔執行個體。如果是 Android 2.2 之前的版本,您必須設定視訊輸出格式和編碼參數:setOutputFormat()
:設定輸出格式,指定預設設定或MediaRecorder.OutputFormat.MPEG_4
。setAudioEncoder()
:設定音效編碼類型,指定預設設定或MediaRecorder.AudioEncoder.AMR_NB
。setVideoEncoder()
:設定影片編碼類型,指定預設設定或MediaRecorder.VideoEncoder.MPEG_4_SP
。
setOutputFile()
:設定輸出檔案,請使用「儲存媒體檔案」一節中範例方法的getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()
。setPreviewDisplay()
- 指定應用程式的SurfaceView
預覽版面配置元素。請使用您為連結預覽指定的相同物件。
注意:您必須依照此順序呼叫這些
MediaRecorder
設定方法,否則應用程式會發生錯誤並錄製失敗。 - 準備 MediaRecorder:呼叫
MediaRecorder.prepare()
,根據提供的配置設定準備MediaRecorder
。 - 啟動 MediaRecorder - 呼叫
MediaRecorder.start()
開始錄製影片。
- 解鎖相機:呼叫
- 停止錄製影片 - 請依序呼叫下列方法,順利完成錄影:
- Stop MediaRecorder:呼叫
MediaRecorder.stop()
停止錄影。 - Reset MediaRecorder:您也可以選擇呼叫
MediaRecorder.reset()
,從錄音工具中移除設定。 - Release MediaRecorder:呼叫
MediaRecorder.release()
來發布MediaRecorder
。 - 鎖定相機:鎖定相機,允許未來的
MediaRecorder
工作階段透過呼叫Camera.lock()
使用。從 Android 4.0 (API 級別 14) 開始,除非MediaRecorder.prepare()
呼叫失敗,否則不需要進行這項呼叫。
- Stop MediaRecorder:呼叫
- 「Stop the Preview」:活動使用相機結束後,請使用
Camera.stopPreview()
停止預覽。 - 「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
的影片錄製參數已採預設設定,不過我們建議您針對應用程式調整這些設定:
setVideoEncodingBitRate()
setVideoSize()
setVideoFrameRate()
setAudioEncodingBitRate()
setAudioChannels()
setAudioSamplingRate()
啟動及停止 MediaRecorder
使用 MediaRecorder
類別開始及停止錄製影片時,您必須按照下列特定順序。
- 使用「
Camera.unlock()
」解鎖相機 - 按照上述程式碼範例設定
MediaRecorder
- 使用「
MediaRecorder.start()
」開始錄製 - 錄製影片
- 停止使用「
MediaRecorder.stop()
」錄製 - 使用
MediaRecorder.release()
釋放媒體錄音工具 - 使用「
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
) - 這個方法會傳回用於儲存應用程式相關相片和影片的標準位置。如果解除安裝應用程式,系統會移除儲存在這個位置的所有檔案。系統不會對位於這個位置的檔案強制執行安全性措施,其他應用程式可能會讀取、變更及刪除這些檔案。
下列程式碼範例說明如何為媒體檔案建立 File
或 Uri
位置,此位置可在使用 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 新增至 Intent
的 EXTRA_OUTPUT
。
如需進一步瞭解如何在 Android 裝置上儲存檔案,請參閱資料儲存空間。
相機特色
Android 支援多種可透過相機應用程式控制的相機功能,例如照片格式、閃光燈模式、對焦設定等。本節列出常見的相機功能,並簡要討論如何使用這些功能。大部分的相機功能都可以透過 Camera.Parameters
物件存取及設定。但是,Camera.Parameters
中有些重要功能需要的不只是簡單設定。我們會在以下各節說明這些功能:
如要大致瞭解如何使用透過 Camera.Parameters
控制的功能,請參閱「使用相機功能」一節。如要進一步瞭解如何使用透過相機參數物件控制的功能,請透過下方功能清單中的連結前往 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 則代表相機圖片右下角。
此座標系統的邊界一律會對應相機預覽畫面中顯示的圖片外邊緣,而且不會縮小或放大至縮放等級。同樣地,使用 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
,請參閱「擷取影片」。
Camera2Video 和 HdrViewfinder 範例進一步示範如何使用本頁涵蓋的 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 官方範例應用程式。