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
- 這個類別是較舊的已淘汰 API,用於控制裝置相機。
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" />
- 錄音權限 - 如要透過影片擷取功能錄製音訊,應用程式必須要求音訊擷取權限。
<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
的例項,要求存取相機 (除非您是使用 Intent 存取相機)。
如要存取主要攝影機,請使用 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.open()
和 Camera.release()
呼叫之外,您也必須管理 Camera.lock()
和 Camera.unlock()
呼叫,才能允許 MediaRecorder
存取攝影機硬體。
注意:自 Android 4.0 (API 級別 14) 開始,系統會自動為您管理 Camera.lock()
和 Camera.unlock()
呼叫。
與使用裝置相機拍照不同,擷取影片需要以非常特定的呼叫順序進行。您必須按照特定執行順序,才能順利準備及擷取應用程式的影片,詳情如下。
- 開啟相機 - 使用
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()
開始錄製影片。
- 解鎖相機:呼叫
- 停止錄影 - 依序呼叫下列方法,順利完成錄影:
- 停止 MediaRecorder - 呼叫
MediaRecorder.stop()
停止錄製影片。 - 重設 MediaRecorder - 如有需要,呼叫
MediaRecorder.reset()
從錄音機移除設定。 - 釋放 MediaRecorder - 呼叫
MediaRecorder.release()
即可釋放MediaRecorder
。 - 鎖定攝影機:呼叫
Camera.lock()
鎖定攝影機,供未來的MediaRecorder
工作階段使用。自 Android 4.0 (API 級別 14) 起,除非MediaRecorder.prepare()
呼叫失敗,否則不需要進行這項呼叫。
- 停止 MediaRecorder - 呼叫
- 停止預覽 - 活動完成相機使用後,請使用
Camera.stopPreview()
停止預覽。 - 發布攝影機 - 發布攝影機,讓其他應用程式可以呼叫
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
) - This method returns a standard location for saving pictures and videos which are associated with your application. 如果解除安裝應用程式,系統會移除儲存在這個位置的所有檔案。這個位置的檔案不會強制執行安全性措施,其他應用程式可能會讀取、變更及刪除這些檔案。
下列程式碼範例說明如何為媒體檔案建立 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 新增至 EXTRA_OUTPUT
的 Intent
。
如要進一步瞭解如何在 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
,請參閱「擷取影片」。
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 範例應用程式。