顯示相機預覽內容是每一個相機類應用都會包含的功能,想要完美實現這個卻並不是易事。緣由是,在某些特別極端狀況下 camera2 API 的使用會變得很複雜,並且在不一樣設備上的行爲還會有所不一樣。還好,Jetpack CameraX 庫 的 PreviewView
能夠幫助您解決這一問題。經過在各類 Android 設備上提供開發者友好、一致且穩定的 API,使得展現相機的預覽變得再也不困難。html
PreviewView
是一個能夠顯示相機畫面的自定義 View,它被構建的初衷即是下降開發者們在設置和處理相機所使用的預覽畫面 (preview surface) 的難度。java
若是您須要在應用中提供展現相機畫面的基本功能,使用 PreviewView 是最推薦的作法,它有如下幾個優勢:android
PreviewView
是一個 View
,它經過管理 Preview 用例所使用的 Surface 來實現將相機捕捉到的畫面展現在界面佈局中的所有功能;PreviewView
只專一於實現相機畫面預覽功能。它全部內部資源都致力於對相機預覽畫面的展現,以及在相機使用過程當中對預覽畫面 (preview surface) 進行管理。這樣的關注點分離使得 PreviewView
的代碼可以保持簡潔;PreviewView
解決了在屏幕上展現相機畫面過程當中最難處理的部分,包括對畫面寬高比、縮放和旋轉的處理。不一樣的設備會致使不一致的行爲,包括設備、屏幕尺寸、攝像頭硬件支持水平,還會須要適配諸如分屏模式、不一樣鎖定方向和可動態調節尺寸的展現窗口等顯示模式,爲了解決這些問題並在多種設備上提供無縫體驗,PreviewView 還作了一些兼容性的處理。PreviewView
是 FrameLayout
的子類,它會使用 SurfaceView
或者 TextureView
展現來自相機捕捉到的畫面。一旦相機準備好,就會建立一個預覽畫面 (preview surface) 的實例,並在相機使用過程當中儘可能持有該實例,若是相機還在工做中卻提早釋放了所持有的預覽畫面 (preview surface) 實例,就會從新建立一個。git
當涉及到諸如功耗和響應時間這些關鍵指標時,SurfaceView
的表現通常都比 TextureView
要好,這也是爲何 PreviewView
會將 SurfaceView
做爲默認實現模式的緣由。然而,一些設備 (主要是一些 舊版設備) 會在預覽畫面 (preview surface) 過早釋放時出現閃退的狀況。惋惜的是,使用 SurfaceView 時沒法控制什麼時候對畫面 (surface) 進行釋放,由於這是由 View 層級結構所控制的。所以在這些設備上,PreviewView 只能使用 TextureView 做爲實現模式。另外在須要對相機預覽界面進行旋轉、改變透明度或加入動畫的狀況下,您也應該強制 PreviewView 使用 TextureView 做爲實現模式。github
您能夠經過調用 PreviewView.setPreferredImplementationMode(ImplementationMode)) 並設置 ImplementationMode
參數爲 SURFACE_VIEW
或 TEXTURE_VIEW
來更改 PreviewView
的實現模式。當首選模式設置爲 SURFACE_VIEW
時,PreviewView 會盡量遵循您的設置 (使用 SurfaceView);而當首選模式設置爲 TEXTURE_VIEW
時,PreviewView 會確保一直使用 TEXTURE_VIEW
模式。api
⚠️ 在開始使用 PreviewView 以前,請務必經過調用 Preview.setSurfaceProvider(PreviewView.createSurfaceProvider())) 來設置您想要的實現模式。app
下面介紹如何設置 PreviewView 的實現模式:ide
// 進行相機畫面預覽以前,設置想要的實現模式 previewView.preferredImplementationMode = ImplementationMode.SURFACE_VIEW // 將 previewView 設置到 preview 用例中來開始進行相機畫面預覽 preview.setSurfaceProvider(previewView.createSurfaceProvider(cameraInfo))
PreviewView
經過處理建立 Preview
用例所須要的 SurfaceProvider
,來啓動一個預覽畫面的數據流。SurfaceProvider
會準備好須要提供給相機的 Surface,用來對預覽畫面的數據流進行展現,並負責在必要時從新建立 Surface
。PreviewView.createSurfaceProvider(CameraInfo)
) 接收一個 nullable 的 CameraInfo
實例。PreviewView 會結合所傳入的 CameraInfo 參數,以及您所設定的實現模式和當前相機具有功能,來決定內部如何進行功能上的實現。若是您所傳入的 CameraInfo
是一個 null
,那 PreviewView
會使用 TextureView
做爲實現模式,由於它沒法肯定所選的相機若使用 SurfaceView 是否能夠正常工做。函數
一旦您建立好了 Preview 用例和一些別的所須要的 實例 後,將它們綁定至 LifecycleOwner
,使用所綁定的相機的 CameraInfo 來建立 SurfaceProvider
,再將其綁定至 Preview
用例,調用 Preview.setSurfaceProvider(SurfaceProvider)
來啓動預覽畫面數據流。佈局
下面的例子展現瞭如何將 PreviewView 綁定至 Preview 來開啓預覽畫面數據流:
// 建立 preview 用例 val preview = Preview.Builder().build() // 將 preview 和其餘須要的用例綁定到 lifecycle 中 val camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview, imageAnalysis, imageCapture) // 使用所綁定相機的 cameraInfo 建立 surfaceProvider val surfaceProvider = previewView.createSurfaceProvider(camera.cameraInfo) // 將 surfaceProvider 綁定至 preview 用例來啓動預覽 preview.setSurfaceProvider(surfaceProvider)
PreviewView 提供了一個 API,經過它可讓您控制預覽畫面的樣式是怎樣的 (how) 和在父級視圖中的位置 (where):
"how" 和 "where" 所組合出來的結果,表明了 PreviewView 支持的縮放 (scale) 類型,包括 FIT_START、FIT_CENTER、FIT_END、FILL_START、FILL_CENTER and FILL_END。其中最經常使用的是 FIT_CENTER 和 FILL_CENTER,前者將預覽界面在保證寬高比的前提下進行縮放而後居中,後者不會進行縮放,保證居中可是可能會致使畫面被裁剪。
有兩種方法能夠設置縮放 (scale) 類型:
scaleType
屬性來實現,如如下示例所示:<androidx.camera.view.PreviewView android:layout_width="match_parent" android:layout_height="match_parent" app:scaleType="fitEnd" />
PreviewView.setScaleType(ScaleType)
) 來實現,如如下示例所示:previewView.setScaleType(ScaleType.FIT_CENTER)
想要獲取到當前 PreviewView
所使用的縮放 (scale) 類型,調用 PreviewView.getScaleType()
) 便可。
根據相機攝像頭傳感器的方向、設備的旋轉方向、以及顯示模式和預覽比例,PreviewView 可能會對從相機接收到的預覽幀進行相應地縮放、旋轉和轉換處理,以便在 UI 界面中能正確展現。這也是爲何將 UI 座標轉換成攝像頭傳感器座標是很重要的。在 CameraX 中,這種轉換是由 MeteringPointFactory
完成的,它能夠經過 PreviewView 提供的 API 進行建立: PreviewView.createMeteringPointFactory(cameraSelector)),其中 CameraSelector 參數表明所傳入畫面流數據的攝像頭。
當您須要實現輕點對焦 (tap-to-focus) 功能的時候,PreviewView 的 MeteringPointFactor 輕易就可作到。儘管相機預覽中默認啓用了自動對焦 (須要攝像頭支持),但在 PreviewView 上點擊時,您仍是能夠控制對焦目標。MeteringPointFactory 會將對焦目標的座標轉換爲攝像頭傳感器的座標,而後再使用攝像頭對該區域進行對焦。
下面的示例展現瞭如何使用 觸摸監聽器 (touch listener) 在 PreviewView
上實現輕點對焦功能:
fun onTouch(x: Float, y: Float) { // 建立 MeteringPoint,命名爲 factory val factory = previewView.createMeteringPointFactory(cameraSelector) // 將 UI 界面的座標轉換爲攝像頭傳感器的座標 val point = factory.createPoint(x, y) // 建立對焦須要用的 action val action = FocusMeteringAction.Builder(point).build() // 執行所建立的對焦 action cameraControl.startFocusAndMetering(action) }
另外一個在相機預覽界面中經常使用的功能是捏拉縮放 (pinch-to-zoom),它可讓您經過在預覽界面進行捏拉來實現畫面的縮放操做。想要在 PreviewView 上實現它,在其之上添加一個 觸摸監聽器,並將其綁定到縮放手勢監聽器 (scale gesture listener) 上。這樣就能夠作到攔截捏拉手勢,而後相應地更新攝像頭的縮放比例。
下方的示例展現瞭如何在 PreviewView 上實現捏拉縮放 (pinch-to-zoom) 操做:
// 建立一個名爲 listener 的回調函數,當手勢事件發生時會調用這個回調函數 val listener = object : ScaleGestureDetector.SimpleOnScaleGestureListener() { override fun onScale(detector: ScaleGestureDetector): Boolean { // 獲取當前的攝像頭的縮放比例 val currentZoomRatio: Float = cameraInfo.zoomRatio.value ?: 1F // 獲取用戶捏拉手勢所更改的縮放比例 val delta = detector.scaleFactor // 更新攝像頭的縮放比例 cameraControl.setZoomRatio(currentZoomRatio * delta) return true } } // 將 PreviewView 的觸摸監聽器綁定到縮放手勢監聽器上 val scaleGestureDetector = ScaleGestureDetector(context, listener) // 將 PreviewView 的觸摸事件傳遞給縮放手勢監聽器上 previewView.setOnTouchListener { _, event -> scaleGestureDetector.onTouchEvent(event) return@setOnTouchListener true }
PreviewView 可在各類不一樣的 Android 設備上提供一致的相機處理行爲,這要歸功於 CameraX 在 自動化測試實驗室 中對 PreviewView 及其其餘 API 上進行的投資。這些測試主要分爲兩個主要類別:
綜上所述:
PreviewView
是一個自定義的 View
,它能夠方便地展現相機的預覽畫面;PreviewView
默認使用 SurfaceView
做爲它預覽畫面 (preview surface) 的實現,可是在須要的時候會轉而使用 TextureView;ImageCapture
和 ImageAnalysis
這樣的用例綁定到 LifecycleOwner
上,建立一個 surfaceProvider
,將其綁定到 Preview
用例來啓動相機預覽;PreviewView
的縮放類型來控制預覽畫面的展現方式;PreviewView
建立 MeteringPointFactory
來實現對焦功能;PreviewView
設置手勢監聽來實現捏拉縮放功能。想了解更多關於 CameraX 的優秀功能嗎?請查閱如下資料: