您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“Android相機(jī)開發(fā)的方法是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Android相機(jī)開發(fā)的方法是什么”吧!
Android系統(tǒng)提供了兩種使用手機(jī)相機(jī)資源實(shí)現(xiàn)拍攝功能的方法,一種是直接通過Intent調(diào)用系統(tǒng)相機(jī)組件,這種方法快速方便,適用于直接獲得照片的場景,如上傳相冊,微博、朋友圈發(fā)照片等。另一種是使用相機(jī)API來定制自定義相機(jī),這種方法適用于需要定制相機(jī)界面或者開發(fā)特殊相機(jī)功能的場景,如需要對(duì)照片做裁剪、濾鏡處理,添加貼紙,表情,地點(diǎn)標(biāo)簽等。這篇文章主要是從如何使用相機(jī)API來定制自定義相機(jī)這個(gè)方向展開的。
通過相機(jī)API實(shí)現(xiàn)拍攝功能涉及以下幾個(gè)關(guān)鍵類和接口:
Camera:最主要的類,用于管理和操作camera資源。它提供了完整的相機(jī)底層接口,支持相機(jī)資源切換,設(shè)置預(yù)覽/拍攝尺寸,設(shè)定光圈、曝光、聚焦等相關(guān)參數(shù),獲取預(yù)覽/拍攝幀數(shù)據(jù)等功能,主要方法有以下這些:
open():獲取camera實(shí)例。
setPreviewDisplay(SurfaceHolder):綁定繪制預(yù)覽圖像的surface。surface是指向屏幕窗口原始圖像緩沖區(qū)(raw buffer)的一個(gè)句柄,通過它可以獲得這塊屏幕上對(duì)應(yīng)的canvas,進(jìn)而完成在屏幕上繪制View的工作。通過surfaceHolder可以將Camera和surface連接起來,當(dāng)camera和surface連接后,camera獲得的預(yù)覽幀數(shù)據(jù)就可以通過surface顯示在屏幕上了。
setPrameters設(shè)置相機(jī)參數(shù),包括前后攝像頭,閃光燈模式、聚焦模式、預(yù)覽和拍照尺寸等。
startPreview():開始預(yù)覽,將camera底層硬件傳來的預(yù)覽幀數(shù)據(jù)顯示在綁定的surface上。
stopPreview():停止預(yù)覽,關(guān)閉camra底層的幀數(shù)據(jù)傳遞以及surface上的繪制。
release():釋放Camera實(shí)例
takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg):這個(gè)是實(shí)現(xiàn)相機(jī)拍照的主要方法,包含了三個(gè)回調(diào)參數(shù)。shutter是快門按下時(shí)的回調(diào),raw是獲取拍照原始數(shù)據(jù)的回調(diào),jpeg是獲取經(jīng)過壓縮成jpg格式的圖像數(shù)據(jù)的回調(diào)。
SurfaceView:用于繪制相機(jī)預(yù)覽圖像的類,提供給用戶實(shí)時(shí)的預(yù)覽圖像。普通的view以及派生類都是共享同一個(gè)surface的,所有的繪制都必須在UI線程中進(jìn)行。而surfaceview是一種比較特殊的view,它并不與其他普通view共享surface,而是在內(nèi)部持有了一個(gè)獨(dú)立的surface,surfaceview負(fù)責(zé)管理這個(gè)surface的格式、尺寸以及顯示位置。由于UI線程還要同時(shí)處理其他交互邏輯,因此對(duì)view的更新速度和幀率無法保證,而surfaceview由于持有一個(gè)獨(dú)立的surface,因而可以在獨(dú)立的線程中進(jìn)行繪制,因此可以提供更高的幀率。自定義相機(jī)的預(yù)覽圖像由于對(duì)更新速度和幀率要求比較高,所以比較適合用surfaceview來顯示。
SurfaceHolder:surfaceholder是控制surface的一個(gè)抽象接口,它能夠控制surface的尺寸和格式,修改surface的像素,監(jiān)視surface的變化等等,surfaceholder的典型應(yīng)用就是用于surfaceview中。surfaceview通過getHolder()方法獲得surfaceholder 實(shí)例,通過后者管理監(jiān)聽surface 的狀態(tài)。
SurfaceHolder.Callback接口:負(fù)責(zé)監(jiān)聽surface狀態(tài)變化的接口,有三個(gè)方法:
surfaceCreated(SurfaceHolder holder):在surface創(chuàng)建后立即被調(diào)用。在開發(fā)自定義相機(jī)時(shí),可以通過重載這個(gè)函數(shù)調(diào)用camera.open()、camera.setPreviewDisplay(),來實(shí)現(xiàn)獲取相機(jī)資源、連接camera和surface等操作。
surfaceChanged(SurfaceHolder holder, int format, int width, int height):在surface發(fā)生format或size變化時(shí)調(diào)用。在開發(fā)自定義相機(jī)時(shí),可以通過重載這個(gè)函數(shù)調(diào)用camera.startPreview來開啟相機(jī)預(yù)覽,使得camera預(yù)覽幀數(shù)據(jù)可以傳遞給surface,從而實(shí)時(shí)顯示相機(jī)預(yù)覽圖像。
surfaceDestroyed(SurfaceHolder holder):在surface銷毀之前被調(diào)用。在開發(fā)自定義相機(jī)時(shí),可以通過重載這個(gè)函數(shù)調(diào)用camera.stopPreview(),camera.release()來實(shí)現(xiàn)停止相機(jī)預(yù)覽及釋放相機(jī)資源等操作。
定制一個(gè)自定義相機(jī)應(yīng)用,通常需要完成以下步驟,其流程圖如圖1所示:
檢測并訪問相機(jī)資源 檢查手機(jī)是否存在相機(jī)資源,如果存在,請求訪問相機(jī)資源。
創(chuàng)建預(yù)覽類 創(chuàng)建繼承自SurfaceView并實(shí)現(xiàn)SurfaceHolder接口的拍攝預(yù)覽類。此類能夠顯示相機(jī)的實(shí)時(shí)預(yù)覽圖像。
建立預(yù)覽布局 有了拍攝預(yù)覽類,即可創(chuàng)建一個(gè)布局文件,將預(yù)覽畫面與設(shè)計(jì)好的用戶界面控件融合在一起。
設(shè)置拍照監(jiān)聽器 給用戶界面控件綁定監(jiān)聽器,使其能響應(yīng)用戶操作(如按下按鈕), 開始拍照過程。
拍照并保存文件 將拍攝獲得的圖像轉(zhuǎn)換成位圖文件,最終輸出保存成各種常用格式的圖片。
釋放相機(jī)資源 相機(jī)是一個(gè)共享資源,必須對(duì)其生命周期進(jìn)行細(xì)心的管理。當(dāng)相機(jī)使用完畢后,應(yīng)用程序必須正確地將其釋放,以免其它程序訪問使用時(shí),發(fā)生沖突。
圖1 定制自定義相機(jī)的過程
對(duì)應(yīng)到代碼編寫上可以分成三個(gè)步驟:
第一步:在AndroidManifest.xml中添加Camera相關(guān)功能使用的權(quán)限,具體聲明有以下這些:
第二步:編寫相機(jī)操作功能類CameraOperationHelper。采用單例模式來統(tǒng)一管理相機(jī)資源,封裝相機(jī)API的直接調(diào)用,并提供用于跟自定義相機(jī)Activity做UI交互的回調(diào)接口,其功能函數(shù)如下,主要有創(chuàng)建\釋放相機(jī),連接\開始\關(guān)閉預(yù)覽界面,拍照,自動(dòng)對(duì)焦,切換前后攝像頭,切換閃光燈模式等,具體實(shí)現(xiàn)可以參考官方API文檔。
第三步:編寫自定義相機(jī)Activity,主要是定制相機(jī)界面,實(shí)現(xiàn)UI交互邏輯,如按鈕點(diǎn)擊事件處理,icon資源切換,鏡頭尺寸切換動(dòng)畫等。這里需要聲明一個(gè)SurfaceView對(duì)象來實(shí)時(shí)顯示相機(jī)預(yù)覽畫面。通過SurfaceHolder及其Callback接口來一同管理屏幕surface和相機(jī)資源的連接,相機(jī)預(yù)覽圖像的顯示/關(guān)閉。
下面再講講我在開發(fā)自定義相機(jī)時(shí)踩過的一些坑:
說明這個(gè)問題之前,先介紹下Android手機(jī)上幾個(gè)方向的概念:
屏幕方向:在Android系統(tǒng)中,屏幕的左上角是坐標(biāo)系統(tǒng)的原點(diǎn)(0,0)坐標(biāo)。原點(diǎn)向右延伸是X軸正方向,原點(diǎn)向下延伸是Y軸正方向。
相機(jī)傳感器方向:手機(jī)相機(jī)的圖像數(shù)據(jù)都是來自于攝像頭硬件的圖像傳感器,這個(gè)傳感器在被固定到手機(jī)上后有一個(gè)默認(rèn)的取景方向,如下圖2所示,坐標(biāo)原點(diǎn)位于手機(jī)橫放時(shí)的左上角,即與橫屏應(yīng)用的屏幕X方向一致。換句話說,與豎屏應(yīng)用的屏幕X方向呈90度角。
圖2 相機(jī)傳感器方向示意圖
相機(jī)的預(yù)覽方向:由于手機(jī)屏幕可以360度旋轉(zhuǎn),為了保證用戶無論怎么旋轉(zhuǎn)手機(jī)都能看到“正確”的預(yù)覽畫面(這個(gè)“正確”是指顯示在UI預(yù)覽界面的畫面與人眼看到的眼前的畫面是一致的),Android系統(tǒng)底層根據(jù)當(dāng)前手機(jī)屏幕的方向?qū)D像傳感器采集到的數(shù)據(jù)進(jìn)行了旋轉(zhuǎn)處理,然后才送給顯示系統(tǒng),因此可以保證預(yù)覽畫面始終“正確”。在相機(jī)API中可以通過setDisplayOrientation()設(shè)置相機(jī)預(yù)覽方向。在默認(rèn)情況下,這個(gè)值為0,與圖像傳感器一致。因此對(duì)于橫屏應(yīng)用來說,由于屏幕方向和預(yù)覽方向一致,預(yù)覽圖像不會(huì)顛倒90度。但是對(duì)于豎屏應(yīng)用,屏幕方向和預(yù)覽方向垂直,所以會(huì)出現(xiàn)顛倒90度現(xiàn)象。為了得到正確的預(yù)覽畫面,必須通過API將相機(jī)的預(yù)覽方向旋轉(zhuǎn)90,保持與屏幕方向一致,如圖3所示。
圖3 相機(jī)預(yù)覽方向示意圖
(紅色箭頭為預(yù)覽方向,藍(lán)色方向?yàn)槠聊环较颍?br/> 相機(jī)的拍照方向:當(dāng)點(diǎn)擊拍照按鈕,拍攝的照片是由圖像傳感器采集到的數(shù)據(jù)直接存儲(chǔ)到SDCard上產(chǎn)生的,因此,相機(jī)的拍照方向與傳感器方向是一致的。
說明這個(gè)問題之前,同樣先說一下幾個(gè)跟相機(jī)有關(guān)的尺寸。
SurfaceView尺寸:即自定義相機(jī)應(yīng)用中用于顯示相機(jī)預(yù)覽圖像的View的尺寸,當(dāng)它鋪滿全屏?xí)r就是屏幕的大小。這里surfaceview顯示的預(yù)覽圖像暫且稱作手機(jī)預(yù)覽圖像。
Previewsize:相機(jī)硬件提供的預(yù)覽幀數(shù)據(jù)尺寸。預(yù)覽幀數(shù)據(jù)傳遞給SurfaceView,實(shí)現(xiàn)預(yù)覽圖像的顯示。這里預(yù)覽幀數(shù)據(jù)對(duì)應(yīng)的預(yù)覽圖像暫且稱作相機(jī)預(yù)覽圖像。
Picturesize:相機(jī)硬件提供的拍攝幀數(shù)據(jù)尺寸。拍攝幀數(shù)據(jù)可以生成位圖文件,最終保存成.jpg或者.png等格式的圖片。這里拍攝幀數(shù)據(jù)對(duì)應(yīng)的圖像稱作相機(jī)拍攝圖像。圖4說明了以上幾種圖像及照片之間的關(guān)系。手機(jī)預(yù)覽圖像是直接提供給用戶看的圖像,它由相機(jī)預(yù)覽圖像生成,拍攝照片的數(shù)據(jù)則來自于相機(jī)拍攝圖像。
圖4 幾種圖像之間的關(guān)系
下面說下我在開發(fā)過程中遇到的三種拉伸變形現(xiàn)象:
1、手機(jī)預(yù)覽畫面中物體被拉伸變形。
2、拍攝照片中物體被拉伸變形。
3、點(diǎn)擊拍照瞬間,手機(jī)預(yù)覽畫面會(huì)停頓下,此時(shí)的圖像是拉伸變形的,然后預(yù)覽畫面恢復(fù)后圖像又正常了。
現(xiàn)象1的原因是SurfaceView和Previewsize的長寬比率不一致。因?yàn)槭謾C(jī)預(yù)覽視圖的圖像是由相機(jī)預(yù)覽圖像根據(jù)SurfaceView大小縮放得來的,當(dāng)長寬比不一致時(shí)必然會(huì)導(dǎo)致圖像變形。后兩個(gè)現(xiàn)象的原因則是Previewsize和Picturesize的長寬比率不一致所致,查了相關(guān)的資料,發(fā)現(xiàn)其具體原因跟某些手機(jī)相機(jī)硬件的底層實(shí)現(xiàn)有關(guān)??傊疄榱吮苊庖陨蠋追N變形現(xiàn)象的發(fā)生,在開發(fā)時(shí)最好將SurfaceView、PreviewSize、PictureSize三個(gè)尺寸保證長寬比例一致。具體實(shí)現(xiàn)可以先通過camera.getSupportedPreviewSizes()和camera.getSupportedPictureSizes()獲得相機(jī)硬件支持的所有預(yù)覽和拍攝尺寸,然后在里面篩選出和SurfaceView的長寬比一致并且大小合適的尺寸,通過camera.setPrameters來更新設(shè)置。注意:市場上手機(jī)相機(jī)硬件支持的尺寸一般都是主流的4:3或者16:9,所以SurfaceView尺寸不能太奇葩,最好也設(shè)置成這樣的長寬比。
前兩個(gè)Crash的原因是:相機(jī)硬件在聚焦和拍照前必須要保證已經(jīng)連接到surface,并且開啟相機(jī)預(yù)覽,surface有收到預(yù)覽數(shù)據(jù)。如果在還沒有執(zhí)行camera. setPreviewDisplay或者未調(diào)用camera. startPreview之前,就調(diào)用camera.autofocus或camera.takepicture,就會(huì)出現(xiàn)這個(gè)運(yùn)行時(shí)異常。對(duì)應(yīng)到自定義相機(jī)的代碼中,要注意在拍照按鈕事件響應(yīng)中執(zhí)行camera.autofocus或camera.takepicture前,一定要檢驗(yàn)camera有沒有設(shè)置預(yù)覽Surfaceview并開啟了相機(jī)預(yù)覽。這里有個(gè)方法可以判斷預(yù)覽狀態(tài):Camera.setPreviewCallback是預(yù)覽幀數(shù)據(jù)的回調(diào)函數(shù),它會(huì)在SurfaceView收到相機(jī)的預(yù)覽幀數(shù)據(jù)時(shí)被調(diào)用,因此在里面可以設(shè)置是否允許對(duì)焦和拍照的標(biāo)志位。
還有一點(diǎn)要注意,camera.takePicture()在執(zhí)行過程中會(huì)執(zhí)行camera.stopPreview來獲取拍攝幀數(shù)據(jù),表現(xiàn)為預(yù)覽畫面卡住,而如果此時(shí)用戶點(diǎn)擊了按鈕的話,也就是調(diào)用camera.takepicture,也會(huì)出現(xiàn)上面的crash,因此在開發(fā)時(shí),可能還需要屏蔽拍照按鈕的連續(xù)點(diǎn)擊。
第三個(gè)crash則涉及圖像的裁剪,由于要支持1:1或者4:3尺寸鏡頭,所以會(huì)需要對(duì)預(yù)覽視圖進(jìn)行裁剪,由于是豎屏應(yīng)用,所以裁剪區(qū)域的坐標(biāo)系跟相機(jī)傳感器方向是成90度角的,表現(xiàn)在裁剪里就是,屏幕上的x方向,對(duì)應(yīng)在拍攝圖像上是高度方向,而屏幕上的y方向,對(duì)應(yīng)到拍攝圖像上則是寬度方向。因此在計(jì)算時(shí)要一定注意坐標(biāo)系的轉(zhuǎn)換以及越界保護(hù)。
Android相機(jī)硬件有個(gè)特殊設(shè)定,就是對(duì)于前置攝像頭,在展示預(yù)覽視圖時(shí)采用類似鏡面的效果,顯示的是攝像頭成像的鏡像。而拍攝出的照片則仍采用攝像頭成像??吹竭@里,大家可能會(huì)有些懷疑,不妨現(xiàn)在就試試自己Android手機(jī)上的前置攝像頭,對(duì)比下預(yù)覽圖像和拍攝出照片的區(qū)別。這是由于底層相機(jī)在傳遞前置攝像頭預(yù)覽數(shù)據(jù)時(shí)做了水平翻轉(zhuǎn)變換,即將x方向鏡像翻轉(zhuǎn)180度。這個(gè)變化對(duì)之前豎屏預(yù)覽的方向也會(huì)造成影響,本來對(duì)于后置攝像頭旋轉(zhuǎn)90度即可使預(yù)覽視圖正確,而對(duì)前置攝像頭,如果也旋轉(zhuǎn)90度的話,看到的預(yù)覽圖像則是上下顛倒的(因?yàn)閤方向翻轉(zhuǎn)了180度),因此必須再旋轉(zhuǎn)180度,才能顯示正確,如圖5所示,大家可以結(jié)合之前相機(jī)預(yù)覽方向的示意圖一起理解。
圖5 前置攝像頭的預(yù)覽方向示意圖
此外,由于拍攝圖像并沒有做水平翻轉(zhuǎn),所以對(duì)于前置攝像頭拍出來的照片,用戶會(huì)發(fā)現(xiàn)跟預(yù)覽時(shí)所見的是左右翻轉(zhuǎn)的。這個(gè)在一定程度上會(huì)影響用戶體驗(yàn)。為了解決這個(gè)問題,可以對(duì)前置攝像頭拍攝的圖像在生成位圖文件時(shí)增加一個(gè)水平翻轉(zhuǎn)矩陣變換。
為了節(jié)省手機(jī)電量,不浪費(fèi)相機(jī)資源,在開發(fā)的自定義相機(jī)里,如果預(yù)覽圖像已不需要顯示,如按Home鍵盤切換后臺(tái)或者鎖屏后,此時(shí)就應(yīng)該關(guān)閉預(yù)覽并把相機(jī)資源釋放掉。參考官方API文檔,當(dāng)surfaceView變成可見時(shí),會(huì)創(chuàng)建surface并觸發(fā)surfaceHolder.callback接口中surfaceCreated回調(diào)函數(shù)。而surfaceview變成不可見時(shí),則會(huì)銷毀surface,并觸發(fā)surfacedestroyed回調(diào)函數(shù)。我們可以在對(duì)應(yīng)的回調(diào)函數(shù)里,處理相機(jī)的相關(guān)操作,如連接surface、開啟/關(guān)閉預(yù)覽。 至于相機(jī)資源釋放,則可以放在Acticity的onpause里執(zhí)行。相應(yīng)的,要重新恢復(fù)預(yù)覽圖像時(shí),可以把相機(jī)資源申請和初始化放在Acticity的onResume里執(zhí)行,然后通過創(chuàng)建surfaceview,將camera和surface相連并開啟預(yù)覽。
但是在開發(fā)過程中發(fā)現(xiàn),對(duì)于按HOME鍵切后臺(tái)場景,程序可以正常運(yùn)行。對(duì)于鎖屏場景,則在重新申請相機(jī)資源時(shí)會(huì)發(fā)生crash,說相機(jī)資源訪問失敗。那么原因是什么呢?我在代碼里增加了調(diào)試log, 檢查了代碼的執(zhí)行順序,結(jié)果如下:
在自定義相機(jī)頁面按HOME鍵時(shí)的執(zhí)行流程:
程序運(yùn)行->按HOME鍵
Activity調(diào)用的順序是onPause->onStop
SurfaceView調(diào)用了surfaceDestroyed方法
然后再切回程序
Activity調(diào)用的順序是onRestart->onStart->onResume
SurfaceView調(diào)用了surfaceCreated->surfaceChanged方法
而對(duì)于鎖屏,其執(zhí)行流程則是:
Activity只調(diào)用onPause方法
解鎖后Activity調(diào)用onResume方法
SurfaceView中surfaceholder.callback的所有方法都沒有執(zhí)行
問題找到了,由于鎖屏?xí)r,callback的回調(diào)方法沒有執(zhí)行,導(dǎo)致相機(jī)和預(yù)覽的連接還沒有斷開,相機(jī)資源就被釋放了,所以導(dǎo)致在重新申請相機(jī)資源時(shí),系統(tǒng)報(bào)crash。根據(jù)上面的文檔,推測是鎖屏下系統(tǒng)并沒有改變surfaceview的可見性,于是我嘗試在onPause和onResume時(shí)通過手動(dòng)設(shè)置surfaceview的visibile屬性,結(jié)果發(fā)現(xiàn)可以正常觸發(fā)回調(diào)函數(shù)了。由于在切后臺(tái)或者鎖屏?xí)r,用戶本來就應(yīng)該看不到surfaceview,因此這種手動(dòng)更改surfaceview的可見性的方法,并不會(huì)對(duì)用戶的體驗(yàn)造成影響。
到此,相信大家對(duì)“Android相機(jī)開發(fā)的方法是什么”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。