Android Camera 系列(三)Camera API 詳解
概述
Camera 可能是接下來個人想深入學習的課題,準備新起一個系列,從個人的角度總結闡述自己對於 Android Camera 的研究過程,希望也能夠對其他想學習 Camera 的同學一些幫助。
本小節內容為 Android Camera 官方文件 的精要翻譯,原文請參考:
正文
Android Framework 包括對裝置上可用的攝像頭和攝像頭功能的支援,以達到在應用程式中 拍照和錄製視訊 的目的。 本文件討論了一種快速,簡單的拍照和錄製視訊的實現方法,並概述了為使用者建立 自定義相機體驗 的高階使用說明。
請注意:本文示例程式碼使用的是已棄用的
Camera
類。 Google官方建議使用新的APICamera2
,它適用於Android 5.0(API級別21)或更高版本——但是國內的Rom似乎對Camera2
的支援都不是非常友好,並且僅支援API21以上的Android系統,因此Camera
依然有研究學習的價值。
一、注意事項
您的APP在Android裝置上使用Camera
相關API之前,應該注意的:
1.攝像頭要求
您應該在Manifest.xml
中宣告Camera
的使用,以保證安裝App的Android裝置都有Camera
的硬體支援。
2.優先考慮使用系統相機
如果您只想拍攝快照或錄製視訊,而不會使用Camera
的其它API,請考慮使用系統相機。
3.前臺服務要求
在Android 9(API級別28)及更高版本中,在後臺執行的應用程式無法訪問攝像頭。因此,您應該在應用程式位於前臺或作為前臺服務的一部分時使用相機。
4.儲存
您的應用程式生成的 影象或視訊是否僅對您的應用程式 可見或共享,以便其他應用程式(比如 相簿 或者其他應用)可以使用它們?即使您的應用程式已解除安裝,您是否希望圖片和視訊同樣被刪除?
二、基礎
Android FrameWork 支援通過Camera2
API或相機Intent
捕獲影象和視訊。 以下是相關官方API文件(注意:下面連結請自備梯子):
控制Android裝置攝像頭的主要API。 在構建相機應用程式時,它可用於拍攝照片或視訊。
用於控制裝置相機,較舊的、已棄用的API。
相機捕獲的影象都會在展示在上面預覽。
用於錄製來自攝像機的視訊。
直接通過Intent訪問系統相機,而無需呼叫Camera
三、使用系統相機
上面2個link對應的中文部落格請參考這篇:
四、構建相機應用
建立一個自定義的相機步驟如下:
- 檢測並獲取相機
- 建立一個預覽類:建立一個繼承自
SurfaceView
的類並實現SurfaceHolder
介面。這個類預覽相機的實時影象。 - 構建一個預覽介面:建立完相機預覽類以後,建立一個佈局以展示預覽。
- 增加監聽:增加對應的監聽,以操作
Camera
的行為,比如點選按鈕拍照。 - 採集並儲存檔案 :將拍照圖片或視訊儲存。
- 釋放相機資源 -使用完相機以後,必須正確釋放相機資源,以供其他應用繼續使用。
1.檢測相機硬體
/** 檢查相機是否可用 **/
fun cameraHardwareAvailable() =
packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
2.訪問相機
如果您確定Android裝置有攝像頭,則必須通過獲取Camera
例項來使用相關API(除非您使用Intent
訪問系統相機)。
要訪問主攝像頭,請使用Camera.open()方法並確保捕獲任何異常,如下面的程式碼所示:
/** 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
}
}
警告:使用
Camera.open()
時,請務必檢查異常。 如果相機正在使用或不存在,則無法檢查異常將導致崩潰。
在 Android 2.3 以上,你可以使用Camera.open(int)
獲取特定的相機,它將在有多個相機的裝置上獲取第一個後置攝像頭。
3.檢查相機特性
得到Camera
例項以後,你可以使用Camera.getParameters()
方法獲取關於相機的更多屬性引數的Parameters
物件。
4.建立一個預覽類
以下的示例程式碼展示瞭如何建立一個基本的相機預覽類:
/** 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}")
}
}
}
}
如果要為相機的預覽設定特定大小,請在surfaceChanged()
方法中進行設定,如上面的註釋中所述。 設定預覽大小時,必須使用getSupportedPreviewSizes()
中的值。不要在setPreviewSize()
方法中隨意地設定值。
注意:隨著Android 7.0(API24)及更高級別中的多視窗功能的引入,即使在呼叫
setDisplayOrientation()
之後,您也不能再認為預覽的寬高比與您的活動相同。 根據視窗大小和寬高比,您可能需要使用特定佈局將寬相機預覽適合縱向佈局,反之亦然。
5.將相機預覽展示出來
以下程式碼展示如何進行預覽。其中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"/>
接下來的程式碼展示了如何部署展示你的相機介面:
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)
// 該函式程式碼,請參考上面的【訪問相機】小節
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)
}
}
}
6.拍照
實現拍照,請使用Camera.takePicture()
方法。 該方法採用三個從攝像機接收資料的引數, 要以JPEG
格式接收資料,必須實現Camera.PictureCallback
介面以接收影象資料並將其寫入檔案。以下程式碼顯示了Camera.PictureCallback
介面的基本實現,用於儲存從相機接收的影象:
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}")
}
}
然後將這個Callback
在合理的時機使用:
val captureButton: Button = findViewById(R.id.button_capture)
captureButton.setOnClickListener {
// get an image from the camera
mCamera?.takePicture(null, null, mPicture)
}
7.錄製視訊
錄製視訊的實現相對複雜,而官方文件提供的三言兩語略顯單薄,因此該內容不放在本小節中,我將在之後的一篇文章中進行闡述。
8.釋放Camera資源
class CameraActivity : Activity() {
private var mCamera: Camera?
private var mPreview: SurfaceView?
private var mMediaRecorder: 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() {
mMediaRecorder?.reset() // clear recorder configuration
mMediaRecorder?.release() // release the recorder object
mMediaRecorder = null
mCamera?.lock() // lock camera for later use
}
private fun releaseCamera() {
mCamera?.release() // release the camera for other applications
mCamera = null
}
}
上述三篇文章主要來自Google官方的Camera文件,令人不解的是,官方的幾篇文件互相之間連結不上,很多地方還有令人看不懂的引數,因此僅作為系統入門文章勉強適合,接下來的文章將會從不同角度闡述Camera使用開發過程中的一些疑點難點。
相關推薦
Android Camera 系列(三)Camera API 詳解
概述 Camera 可能是接下來個人想深入學習的課題,準備新起一個系列,從個人的角度總結闡述自己對於 Android Camera 的研究過程,希望也能夠對其他想學習 Camera 的同學一些幫助。 本小節內容為 Android Camera 官方文件 的精要
OSGI學習系列(三)MANIFEST.MF詳解
<一>在osgi專案中META-INF目錄下有一個MANIFEST.MF檔案,是載入bundle時必不可少的,如下圖所示: <二>下面簡單解釋一下其中的幾個元素 #幾個必須的
Kafka 系列(三)—— Kafka 生產者詳解
一、生產者傳送訊息的過程 首先介紹一下 Kafka 生產者傳送訊息的過程: Kafka 會將傳送訊息包裝為 ProducerRecord 物件, ProducerRecord 物件包含了目標主題和要傳送的內容,同時還可以指定鍵和分割槽。在傳送 ProducerRecord 物件前,生產者會先把鍵和值物件序列
Android Studio系列(三)使用Version Control管理多倉庫多分支原始碼
開發android系統原始碼的同學都知道,我們的工作都是很多人協同工作,因此git版本管理及歷史修改查閱異常重要!甚至比開發app重要的多! 此文旨在介紹一下用AS中自帶的Version Control工具來管理android系統原始碼,鑑於android原始
Centos7系列(七)邏輯卷詳解
centos7 邏輯卷 博主QQ:819594300博客地址:http://zpf666.blog.51cto.com/有什麽疑問的朋友可以聯系博主,博主會幫你們解答,謝謝支持!Centos7可以用xfs_growfs來擴大XFS文件系統,用resize2fs 來擴大ext4文件系統,註意的是 XFS
Java8學習筆記(五)--Stream API詳解[轉]
有效 編程效率 實時處理 phaser 綜合 files -- bin 並發模式 為什麽要使用StreamStream 作為 Java 8 的一大亮點,它與 java.io 包裏的 InputStream 和 OutputStream 是完全不同的概念。它也不同於 StAX
(四)Hibernate API詳解
delet hibernate load 類型變量 nbsp ria 每次 transacti llb 一、Configuration類 用來加載默認文件路徑下的配置文件(hibernate.properties)。 調用configure()方法會加載默認文件路徑下的xm
探索 SpringBoot (三) 啟動流程詳解(下)
探索 SpringBoot (三) 啟動流程詳解(下) 文章目錄 探索 SpringBoot (三) 啟動流程詳解(下) 4 SpringBoot 執行階段 4.1 Spring 應用執行監聽者的載入和執行 4.2
python標準庫常用模組(三)-----------------------------OS模組詳解,呼叫系統的命令
OS模組是呼叫系統命令的模組,和執行cmd的命令相似。 1.獲取當前的工作目錄:getcwd() 2.改變當前指令碼的工作目錄:chdir(path) 3.返回當前目錄:curdir() 4.獲取當前目錄的父目錄:pardir() 5.若目錄為空,則刪除,遞迴到上一
kaldi筆記(三)train_mono.sh詳解
train_mono.sh 是音素訓練指令碼,下面詳細介紹各個功能: 1.首先是初始化GMM,使用的指令碼是/kaldi-trunk/src/gmmbin/gmm-init-mono,輸出是0.mdl和tree檔案; 2.compile training graphs,使用的指令碼是/kal
(三)xlwt模組詳解--合併單元格
本篇部落格主要展示了xlwt中合併單元格的操作 程式碼如下(純屬自學期間,如有更好的方法,請在下面評論區留下意見或建議,多謝!): #!/usr/bin/env python3.6 # encod
解讀ASP.NET 5 & MVC6系列(6):Middleware詳解
在第1章專案結構分析中,我們提到Startup.cs作為整個程式的入口點,等同於傳統的Global.asax檔案,即:用於初始化系統級的資訊(例如,MVC中的路由配置)。本章我們就來一一分析,在這裡如何初始化這些系統級的資訊。 新舊版本之間的Pipeline區別 ASP.NET 5和之前版本的最大區別是對HT
目標檢測與分割(三):SSD詳解
SSD github : https://github.com/weiliu89/caffe/tree/ssd SSD paper : https://arxiv.org/abs/1512.02325 SSD eccv2016 slide pdf : http://d
Mybatis學習(三)————— 對映檔案詳解
前面說了全域性配置檔案中內容的詳解,大家應該清楚了,現在來說說這對映檔案,這章就對輸入對映、輸出對映、動態sql這幾個知識點進行說明,其中高階對映(一對一,一對多,多對多對映)在下一章進行說明。 一、輸入對映 輸入對映:配置statement中輸入引數的型別。有四種
MVC之前的那點事兒系列(3):HttpRuntime詳解分析(下)
文章內容 話說,經過各種各樣複雜的我們不知道的內部處理,非託管程式碼正式開始呼叫ISPAIRuntime的ProcessRequest方法了(ISPAIRuntime繼承了IISPAIRuntime介面,該介面可以和COM進行互動,並且暴露了ProcessRequest介面方法)。至於為什麼要呼叫這個方法,
MVC之前的那點事兒系列(2):HttpRuntime詳解分析(上)
文章內容 從上章文章都知道,asp.net是執行在HttpRuntime裡的,但是從CLR如何進入HttpRuntime的,可能大家都不太清晰。本章節就是通過深入分析.Net4的原始碼來展示其中的重要步驟。請先看下圖: 首先,CLR在初始化載入的時候,會載入一個非常重要的類AppManagerApp
Spring Cloud Eureka 入門 (三)服務消費者詳解
摘要: 原創出處:www.bysocket.com 泥瓦匠BYSocket 希望轉載,保留摘要,謝謝! “真正的進步,不在於學習,而在於反思” 「Spring Cloud Eureka 入門系列」本文提綱 1. springcloud-eureka-sample 工程介紹 2. 執行 spring
MySQL基礎(三)SELECT語句詳解
1.基本select語句SELECT name,age FROM employee;2.數學符號條件SELECT name,age FROM employee WHERE age>25;SELECT name,age,phone FROM employee WHERE
VSCode外掛開發全攻略(三)package.json詳解
package.json 在詳細介紹vscode外掛開發細節之前,這裡我們先詳細介紹一下vscode外掛的package.json寫法,但是建議先只需要隨便看一下,瞭解個大概,等後面講到具體細節的時候再回過頭來看。 如下是package.json檔案的常用配置,當然這裡還不是全部: { // 外掛的
SpringBoot 2.0 系列(二):流程詳解(上)
寫在前面 本節將詳細介紹如何使用Spring Boot。它涵蓋了諸如專案管理及自動構建工具、自動配置以及如何執行應用程式等主題。我們還介紹了一些Spring Boot最佳實踐。Spring Boot沒有什麼特別之處(它只是另一個我們可以使用的庫),但是有一些約