2.android開發(fā) 自定義相機

Android中開發(fā)相機的兩種方式

Android系統(tǒng)提供了兩種使用手機相機資源實現(xiàn)拍攝功能的方法拍冠,一種是直接通過Intent調(diào)用系統(tǒng)相機組件,這種方法快速方便,適用于直接獲得照片的場景质涛,如上傳相冊,微博掰担、朋友圈發(fā)照片等汇陆。另一種是使用相機API來定制自定義相機,這種方法適用于需要定制相機界面或者開發(fā)特殊相機功能的場景带饱,如需要對照片做裁剪毡代、濾鏡處理,添加貼紙勺疼,表情教寂,地點標簽等。

1.調(diào)用系統(tǒng)自帶相機

關于系統(tǒng)自帶相機的調(diào)用非常簡單执庐,這里我就不過多敘述了酪耕,具體可以參考谷歌的Training。我只說容易被大家忽視的幾個點:

如果我們的應用使用相機轨淌,但相機并不是應用的正常運行所必不可少的組件因妇,可以將權限聲明中的android:required設置為”false”。這樣的話猿诸,Google Play 也會允許沒有相機的設備下載該應用婚被。當然我們有必要在使用相機之前通過調(diào)用hasSystemFeature(PackageManager.FEATURE_CAMERA)方法來檢查設備上是否有相機。如果沒有梳虽,我們應該禁用和相機相關的功能址芯!

在調(diào)用startActivityForResult()方法之前,先調(diào)用resolveActivity()窜觉,這個方法會返回能處理該Intent的第一個Activity(譯注:即檢查有沒有能處理這個Intent的Activity)谷炸。執(zhí)行這個檢查非常重要,因為如果在調(diào)用startActivityForResult()時禀挫,沒有應用能處理你的Intent旬陡,應用將會崩潰。所以只要返回結果不為null语婴,使用該Intent就是安全的描孟。

使用Android框架所提供的API來直接控制相機硬件

使用API來控制相機我們需要用到關鍵類和接口:

使用Camera對象來控制相機

使用SurfaceView來展現(xiàn)照相機采集的圖像

通過surfaceholder來控制surfac的尺寸和格式驶睦,修改surface的像素,監(jiān)視surface的變化等等

通過SurfaceHolder.Callback 接口匿醒,監(jiān)聽surface狀態(tài)變化

接下來我們分為以下三部分來介紹:關鍵類以及接口的作用和方法场航,Camera控制拍照步驟,自定義相機容易踩到的坑以及解決辦法廉羔。

API說明

Camera :最主要的類溉痢,用于管理和操作camera資源。它提供了完整的相機底層接口憋他,支持相機資源切換孩饼,設置預覽/拍攝尺寸,設定光圈竹挡、曝光镀娶、聚焦等相關參數(shù),獲取預覽/拍攝幀數(shù)據(jù)等功能此迅,主要方法有以下這些:

open():獲取camera實例汽畴。

setPreviewDisplay(SurfaceHolder):綁定繪制預覽圖像的surface旧巾。surface是指向屏幕窗口原始圖像緩沖區(qū)(raw buffer)的一個句柄耸序,通過它可以獲得這塊屏幕上對應的canvas,進而完成在屏幕上繪制View的工作鲁猩。通過surfaceHolder可以將Camera和surface連接起來坎怪,當camera和surface連接后,camera獲得的預覽幀數(shù)據(jù)就可以通過surface顯示在屏幕上了廓握。

setPrameters設置相機參數(shù)搅窿,包括前后攝像頭,閃光燈模式隙券、聚焦模式男应、預覽和拍照尺寸等。

startPreview():開始預覽娱仔,將camera底層硬件傳來的預覽幀數(shù)據(jù)顯示在綁定的surface上沐飘。

stopPreview():停止預覽,關閉camra底層的幀數(shù)據(jù)傳遞以及surface上的繪制牲迫。

release():釋放Camera實例

takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg):這個是實現(xiàn)相機拍照的主要方法耐朴,包含了三個回調(diào)參數(shù)。shutter是快門按下時的回調(diào)盹憎,raw是獲取拍照原始數(shù)據(jù)的回調(diào)筛峭,jpeg是獲取經(jīng)過壓縮成jpg格式的圖像數(shù)據(jù)的回調(diào)。


SurfaceView :用于繪制相機預覽圖像的類陪每,提供給用戶實時的預覽圖像影晓。普通的view以及派生類都是共享同一個surface的镰吵,所有的繪制都必須在UI線程中進行。而surfaceview是一種比較特殊的view俯艰,它并不與其他普通view共享surface捡遍,而是在內(nèi)部持有了一個獨立的surface,surfaceview負責管理這個surface的格式、尺寸以及顯示位置。由于UI線程還要同時處理其他交互邏輯,因此對view的更新速度和幀率無法保證葡粒,而surfaceview由于持有一個獨立的surface域蜗,因而可以在獨立的線程中進行繪制,因此可以提供更高的幀率收奔。自定義相機的預覽圖像由于對更新速度和幀率要求比較高,所以比較適合用surfaceview來顯示。

SurfaceHolder :surfaceholder是控制surface的一個抽象接口续挟,它能夠控制surface的尺寸和格式,修改surface的像素侥衬,監(jiān)視surface的變化等等诗祸,surfaceholder的典型應用就是用于surfaceview中。surfaceview通過getHolder()方法獲得surfaceholder 實例轴总,通過后者管理監(jiān)聽surface 的狀態(tài)直颅。

SurfaceHolder.Callback 接口 :負責監(jiān)聽surface狀態(tài)變化的接口,有三個方法:

surfaceCreated(SurfaceHolder holder):在surface創(chuàng)建后立即被調(diào)用怀樟。在開發(fā)自定義相機時功偿,可以通過重載這個函數(shù)調(diào)用camera.open()、camera.setPreviewDisplay()往堡,來實現(xiàn)獲取相機資源械荷、連接camera和surface等操作。

surfaceChanged(SurfaceHolder holder, int format, int width, int height):在surface發(fā)生format或size變化時調(diào)用虑灰。在開發(fā)自定義相機時吨瞎,可以通過重載這個函數(shù)調(diào)用camera.startPreview來開啟相機預覽,使得camera預覽幀數(shù)據(jù)可以傳遞給surface穆咐,從而實時顯示相機預覽圖像颤诀。

surfaceDestroyed(SurfaceHolder holder):在surface銷毀之前被調(diào)用。在開發(fā)自定義相機時庸娱,可以通過重載這個函數(shù)調(diào)用camera.stopPreview()着绊,camera.release()來實現(xiàn)停止相機預覽及釋放相機資源等操作。


Camera控制拍照的過程

調(diào)用Camera的open()方法打開相機熟尉。

調(diào)用Camera的getParameters()獲取拍照參數(shù)归露,該方法返回一個Cmera.Parameters對象。

調(diào)用Camera.Parameters對象對照相的參數(shù)進行設置斤儿。

調(diào)用Camera的setParameters()剧包,并將Camera.Parameters對象作為參數(shù)傳入恐锦,這樣就可以對拍照進行參數(shù)控制,Android2.3.3以后不用設置疆液。

調(diào)用Camerade的startPreview()的方法開始預覽取景一铅,在之前需要調(diào)用Camera的setPreviewDisplay(SurfaceHolder holder)設置使用哪個SurfaceView來顯示取得的圖片。

調(diào)用Camera的takePicture()方法進行拍照堕油。

程序結束時潘飘,要調(diào)用Camera的stopPreview()方法停止預覽,并且通過Camera.release()來釋放資源掉缺。


預覽方向

先看下官方文檔的說明

Most camera applications lock the display into landscape mode because that is the natural orientation of the camera sensor. This setting does not prevent you from taking portrait-mode photos, because the orientation of the device is recorded in the EXIF header. The setCameraDisplayOrientation() method lets you change how the preview is displayed without affecting how the image is recorded. However, in Android prior to API level 14, you must stop your preview before changing the orientation and then restart it.

大多數(shù)相機程序會鎖定預覽為橫屏狀態(tài)卜录,因為該方向是相機傳感器的自然方向。當然這一設定并不會阻止我們?nèi)ヅ呢Q屏的照片眶明,因為設備的方向信息會被記錄在EXIF頭中艰毒。setCameraDisplayOrientation()方法可以讓你在不影響照片拍攝過程的情況下,改變預覽的方向搜囱。然而丑瞧,對于Android API Level 14及以下版本的系統(tǒng),在改變方向之前蜀肘,我們必須先停止預覽绊汹,然后再去重啟它。


SurfaceView預覽圖像拉伸變形幌缝,拍攝照片尺寸不對

說明這個問題之前灸促,同樣先說一下幾個跟相機有關的尺寸诫欠。

SurfaceView尺寸 :即自定義相機應用中用于顯示相機預覽圖像的View的尺寸涵卵,當它鋪滿全屏時就是屏幕的大小。這里surfaceview顯示的預覽圖像暫且稱作手機預覽圖像荒叼。

Previewsize :相機硬件提供的預覽幀數(shù)據(jù)尺寸轿偎。預覽幀數(shù)據(jù)傳遞給SurfaceView,實現(xiàn)預覽圖像的顯示被廓。這里預覽幀數(shù)據(jù)對應的預覽圖像暫且稱作相機預覽圖像坏晦。

Picturesize :相機硬件提供的拍攝幀數(shù)據(jù)尺寸。拍攝幀數(shù)據(jù)可以生成位圖文件嫁乘,最終保存成.jpg或者.png等格式的圖片昆婿。這里拍攝幀數(shù)據(jù)對應的圖像稱作相機拍攝圖像。圖4說明了以上幾種圖像及照片之間的關系蜓斧。手機預覽圖像是直接提供給用戶看的圖像仓蛆,它由相機預覽圖像生成,拍攝照片的數(shù)據(jù)則來自于相機拍攝圖像挎春。

原因是沒有正確設置比例 parameter.setPictureSize(width,height)看疙,這個比例不是你決定的豆拨,要先通過camera.getParameters().getSupportedPictureSizes()獲得手機支持的尺寸。

/*** 設置照片格式*/

private void setParameter() {

Camera.Parameters parameters = camera.getParameters(); // 獲取各項參數(shù)

parameters.setPictureFormat(PixelFormat.JPEG); // 設置圖片格式

parameters.setJpegQuality(100); // 設置照片質(zhì)量//獲得相機支持的照片尺寸,選擇合適的尺寸

List sizes = parameters.getSupportedPictureSizes();

int maxSize = Math.max(display.getWidth(), display.getHeight());

int length = sizes.size();

if (maxSize > 0) {

for (int i = 0; i < length; i++) {

? ? ? if (maxSize <= Math.max(sizes.get(i).width, sizes.get(i).height)) {

? ? ? ? ? ?parameters.setPictureSize(sizes.get(i).width, sizes.get(i).height);

break;

}

}

}


List ShowSizes = parameters.getSupportedPreviewSizes();

int showLength = ShowSizes.size();

if (maxSize > 0) {for (int i = 0; i < showLength; i++) {

if (maxSize <= Math.max(ShowSizes.get(i).width, ShowSizes.get(i).height)) {parameters.setPreviewSize(ShowSizes.get(i).width, ShowSizes.get(i).height);

break;

}

}

}

camera.setParameters(parameters);

}


前置攝像頭的鏡像效果

Android 相機硬件有個特殊設定能庆,就是對于前置攝像頭施禾,在展示預覽視圖時采用類似鏡面的效果,顯示的是攝像頭成像的鏡像搁胆。而拍攝出的照片則仍采用攝像頭成像弥搞。看到這里渠旁,大家可能會有些懷疑拓巧,不妨現(xiàn)在就試試自己 Android 手機上的前置攝像頭,對比下預覽圖像和拍攝出照片的區(qū)別一死。這是由于底層相機在傳遞前置攝像頭預覽數(shù)據(jù)時做了水平翻轉變換肛度,即將x方向鏡像翻轉180度。這個變化對之前豎屏預覽的方向也會造成影響投慈,本來對于后置攝像頭旋轉90度即可使預覽視圖正確承耿,而對前置攝像頭,如果也旋轉90度的話伪煤,看到的預覽圖像則是上下顛倒的(因為x方向翻轉了180度)加袋,因此必須再旋轉180度,才能顯示正確抱既。

解決方案职烧,在保存圖片的時候根據(jù)選擇的攝像頭做對應的翻轉。

Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);

Matrix matrix = new Matrix();

switch (cameraPosition) {

? ? ?case 0://前matrix.preRotate(270);

? ? ?break;

case 1:matrix.preRotate(90);

? ?break;

}

bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

同時在開發(fā)的過程中發(fā)現(xiàn)了一個有趣的東西防泵,我們用前置攝像頭拍出來的照片其實是左右翻轉的蚀之。但我用小米自帶的相機測試發(fā)現(xiàn),當攝像頭中有人臉出現(xiàn)的時候捷泞,相機會做左右翻轉的操作足删,以給用戶更好的體驗。













SurfaceView預覽圖像拉伸變形锁右,拍攝照片尺寸不對

說明這個問題之前失受,同樣先說一下幾個跟相機有關的尺寸。

SurfaceView尺寸 :即自定義相機應用中用于顯示相機預覽圖像的View的尺寸咏瑟,當它鋪滿全屏時就是屏幕的大小拂到。這里surfaceview顯示的預覽圖像暫且稱作手機預覽圖像。

Previewsize :相機硬件提供的預覽幀數(shù)據(jù)尺寸码泞。預覽幀數(shù)據(jù)傳遞給SurfaceView兄旬,實現(xiàn)預覽圖像的顯示。這里預覽幀數(shù)據(jù)對應的預覽圖像暫且稱作相機預覽圖像浦夷。

Picturesize :相機硬件提供的拍攝幀數(shù)據(jù)尺寸辖试。拍攝幀數(shù)據(jù)可以生成位圖文件辜王,最終保存成.jpg或者.png等格式的圖片。這里拍攝幀數(shù)據(jù)對應的圖像稱作相機拍攝圖像罐孝。圖4說明了以上幾種圖像及照片之間的關系呐馆。手機預覽圖像是直接提供給用戶看的圖像,它由相機預覽圖像生成莲兢,拍攝照片的數(shù)據(jù)則來自于相機拍攝圖像汹来。

原因是沒有正確設置比例 parameter.setPictureSize(width,height),這個比例不是你決定的改艇,要先通過camera.getParameters().getSupportedPictureSizes()獲得手機支持的尺寸收班。

前置攝像頭的鏡像效果

Android 相機硬件有個特殊設定,就是對于前置攝像頭谒兄,在展示預覽視圖時采用類似鏡面的效果摔桦,顯示的是攝像頭成像的鏡像。而拍攝出的照片則仍采用攝像頭成像承疲×诟看到這里,大家可能會有些懷疑燕鸽,不妨現(xiàn)在就試試自己 Android 手機上的前置攝像頭兄世,對比下預覽圖像和拍攝出照片的區(qū)別。這是由于底層相機在傳遞前置攝像頭預覽數(shù)據(jù)時做了水平翻轉變換啊研,即將x方向鏡像翻轉180度御滩。這個變化對之前豎屏預覽的方向也會造成影響,本來對于后置攝像頭旋轉90度即可使預覽視圖正確党远,而對前置攝像頭削解,如果也旋轉90度的話,看到的預覽圖像則是上下顛倒的(因為x方向翻轉了180度)麸锉,因此必須再旋轉180度钠绍,才能顯示正確舆声。

解決方案花沉,在保存圖片的時候根據(jù)選擇的攝像頭做對應的翻轉。

---

更多了解媳握,可關注公眾號:人人懂編程

![微信公眾號:人人懂編程](https://upload-images.jianshu.io/upload_images/2471034-0dce8137109d82b4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末碱屁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蛾找,更是在濱河造成了極大的恐慌娩脾,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件打毛,死亡現(xiàn)場離奇詭異柿赊,居然都是意外死亡俩功,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門碰声,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诡蜓,“玉大人,你說我怎么就攤上這事胰挑÷#” “怎么了?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵瞻颂,是天一觀的道長豺谈。 經(jīng)常有香客問我,道長贡这,這世上最難降的妖魔是什么茬末? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮盖矫,結果婚禮上团南,老公的妹妹穿的比我還像新娘。我一直安慰自己炼彪,他們只是感情好吐根,可當我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著辐马,像睡著了一般拷橘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喜爷,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天冗疮,我揣著相機與錄音,去河邊找鬼檩帐。 笑死术幔,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的湃密。 我是一名探鬼主播诅挑,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼泛源!你這毒婦竟也來了拔妥?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤达箍,失蹤者是張志新(化名)和其女友劉穎没龙,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡硬纤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年解滓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筝家。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡伐蒂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出肛鹏,到底是詐尸還是另有隱情逸邦,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布在扰,位于F島的核電站缕减,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏芒珠。R本人自食惡果不足惜桥狡,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望皱卓。 院中可真熱鬧裹芝,春花似錦、人聲如沸娜汁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掐禁。三九已至怜械,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間傅事,已是汗流浹背缕允。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蹭越,地道東北人障本。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像响鹃,于是被迫代替她去往敵國和親驾霜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內(nèi)容