Android Camera探究之路——起步
Camera在手機(jī)中有著舉足輕重的地位宛琅,不管是二維碼還是照片浅碾、識(shí)別,都離不開攝像頭纵刘,本文將對(duì)Android中的Camera進(jìn)行全面解析穴翩。
權(quán)限鎮(zhèn)樓:
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.camera"/>
調(diào)用系統(tǒng)Camera
通過(guò)系統(tǒng)定義的Intent Action犬第,我們可以很方便的使用所有實(shí)現(xiàn)了Camera功能的App。
ACTION_IMAGE_CAPTURE
這個(gè)action是最常用的一個(gè)調(diào)用系統(tǒng)Camera的action芒帕。
使用方式如下:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
通過(guò)這樣一個(gè)Action歉嗓,我們就可以調(diào)用所有聲明了Camera的App。
那么如何收到拍攝的圖片呢背蟆?我們自然是需要使用startActivityForResult方法鉴分。
這里我們先來(lái)看最簡(jiǎn)單的:
我們?cè)冢?/p>
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
onActivityResult方法中,通過(guò)data參數(shù)來(lái)獲取圖像:
/**
* 通過(guò)data取得圖片
*/
Bundle extras = data.getExtras();
Bitmap bitmap = (Bitmap) extras.get("data");
mImageViewShow.setImageBitmap(bitmap);
但是带膀,現(xiàn)在手機(jī)像素這么高志珍,萬(wàn)一圖片特別大呢,會(huì)不會(huì)data過(guò)大而FC呢垛叨?放心伦糯,Android早就考慮到了,所以嗽元,data里面壓根就不是完整的圖片敛纲,它只是一張縮略圖,對(duì)还棱,真的是縮略圖载慈。所以惭等,我們需要獲取到拍攝的原圖珍手,就不能使用這種方法。但是我們可以這樣做,我們可以指定MediaStore類的一個(gè)EXTRA_OUTPUT來(lái)指定拍攝圖像保存的位置琳要,相當(dāng)于建立一個(gè)臨時(shí)文件寡具。在onActivityResult中,我們不使用data來(lái)獲取圖像稚补,而是直接去讀這個(gè)臨時(shí)文件即可童叠。
指定EXTRA_OUTPUT:
String tempPath = Environment.getExternalStorageDirectory().getPath();
mFilePath = tempPath + "/" + "test1.png";
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Uri photoUri = Uri.fromFile(new File(mFilePath));
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
startActivityForResult(intent, CAMERA_CODE1);
onActivityResult:
/**
* 通過(guò)暫存路徑取得圖片
*/
FileInputStream fis = null;
Bitmap bitmap = null;
try {
fis = new FileInputStream(mFilePath);
bitmap = BitmapFactory.decodeStream(fis);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
這樣我們就可以獲取到完整的拍攝圖片了。后面你可以讓圖像顯示出來(lái)课幕,顯示的時(shí)候厦坛,同樣需要考慮大圖的處理,避免圖像尺寸帶來(lái)的問(wèn)題乍惊,這些東西杜秸,請(qǐng)參考這里:
http://blog.csdn.net/eclipsexys/article/details/44459771
這里就不贅述了。如果你的App僅僅是需要非常簡(jiǎn)單的拍攝功能润绎,那么通過(guò)調(diào)用系統(tǒng)Intent就足夠了撬碟,但是大部分時(shí)候,這都是不可能的莉撇,所以下面我們來(lái)看看如何自定義Camera呢蛤。
自定義Camera
根據(jù)Google Android Doc,自定義一個(gè)Camera需要如下幾個(gè)步驟:
1.檢查Camera是否存在棍郎,并在AndroidManifest.xml中賦予相關(guān)的權(quán)限其障;
2.創(chuàng)建一個(gè)繼承于SurfaceView并實(shí)現(xiàn)SurfaceHolder接口的Camera Preview類;
3.新建一個(gè)Camera Preview布局文件涂佃;
4.設(shè)置一個(gè)拍照的監(jiān)聽事件静秆,例如單擊按鈕事件等;
5.實(shí)現(xiàn)拍照巡李,并保存拍照后的圖片到設(shè)備抚笔;
6.釋放Camera。
看上去還是比較復(fù)雜的侨拦。所以我們一步步來(lái)殊橙。
首先,我們創(chuàng)建預(yù)覽Camera的界面:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:orientation="horizontal">
<Button
android:id="@+id/btn_switch_camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:onClick="switchCamera"
android:text="切換攝像頭"/>
<Button
android:id="@+id/btn_capture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:onClick="capture"
android:text="拍照"/>
</LinearLayout>
<SurfaceView
android:id="@+id/sv_camera"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/ll"
android:text="camera"/>
</RelativeLayout>
非常簡(jiǎn)單狱从,兩個(gè)button下面一個(gè)surfaceview:
然后膨蛮,我們創(chuàng)建一個(gè)Activity,用來(lái)展示Camera的預(yù)覽:
這個(gè)Activity里面肯定有SurfaceView季研,所以敞葛,SurfaceView的那一套東西,自然是少不了与涡,不懂的請(qǐng)自行腦補(bǔ)惹谐。
那么在這個(gè)Activity里面持偏,我們需要做什么呢??jī)杉虑椋?/p>
- 初始化相機(jī)
- 將內(nèi)容顯示到SurfaceView
Android的Camera是獨(dú)享的氨肌,如果多處調(diào)用鸿秆,就會(huì)拋出異常,所以怎囚,我們需要將Camera的生命周期與Activity的生命周期綁定:
- onResume方法中初始化相機(jī)
- onPause方法中釋放相機(jī)
初始化相機(jī)非常簡(jiǎn)單:
/**
* 初始化相機(jī)
*
* @return camera
*/
private Camera getCamera() {
Camera camera;
try {
camera = Camera.open();
} catch (Exception e) {
camera = null;
}
return camera;
}
釋放相機(jī)也非常簡(jiǎn)單:
/**
* 釋放相機(jī)資源
*/
private void releaseCamera() {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
那么下面我們?cè)賮?lái)看如何把相機(jī)圖像設(shè)置到SurfaceView中進(jìn)行預(yù)覽:
/**
* 在SurfaceView中預(yù)覽相機(jī)內(nèi)容
*
* @param camera camera
* @param holder SurfaceHolder
*/
private void setStartPreview(Camera camera, SurfaceHolder holder) {
try {
camera.setPreviewDisplay(holder);
camera.setDisplayOrientation(90);
camera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
尼瑪卿叽,是不是也非常簡(jiǎn)單,camera的一個(gè)方法已經(jīng)幫我們自動(dòng)關(guān)聯(lián)了SurfaceView恳守。
PS 這里需要注意下這個(gè)方法camera.setDisplayOrientation(90)考婴,通過(guò)這個(gè)方法,我們可以調(diào)整攝像頭的角度催烘,不然默認(rèn)是橫屏蕉扮,圖像會(huì)顯示的比較奇怪。當(dāng)然颗圣,即使你設(shè)置的90喳钟,圖像也有可能比較奇怪,這是因?yàn)槟銢](méi)有對(duì)圖像進(jìn)行正確的縮放在岂,比例不對(duì)奔则。
通過(guò)上面的設(shè)置,我們已經(jīng)可以正常預(yù)覽攝像頭的圖像內(nèi)容了蔽午,下面我們就可以拍照了易茬。
唉,拍照真的也非常簡(jiǎn)單及老,就一句話:
mCamera.takePicture(null, null, mPictureCallback);
當(dāng)然抽莱,為了配合拍照,我們需要做一些設(shè)定骄恶,設(shè)置拍照的參數(shù)食铐,并且給拍照之后的動(dòng)作設(shè)定一個(gè)回調(diào):
參數(shù):
Camera.Parameters params = mCamera.getParameters();
params.setPictureFormat(ImageFormat.JPEG);
params.setPreviewSize(800, 400);
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
mCamera.setParameters(params);
// 使用自動(dòng)對(duì)焦功能
mCamera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
mCamera.takePicture(null, null, mPictureCallback);
}
});
回調(diào):
/**
* Camera回調(diào),通過(guò)data[]保持圖片數(shù)據(jù)信息
*/
Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
File pictureFile = getOutputMediaFile();
if (pictureFile == null) {
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
Intent intent = new Intent(CustomCamera.this, CameraResult.class);
intent.putExtra("picPath", pictureFile.getAbsolutePath());
startActivity(intent);
CustomCamera.this.finish();
} catch (IOException e) {
e.printStackTrace();
}
}
};
在回調(diào)中僧鲁,我們將拍攝好的圖片地址傳遞給用于展示的ImageView虐呻。這樣就完成了相機(jī)的拍攝與圖片的展示。
處理圖像變形
由于我們自己在布局中創(chuàng)建了一個(gè)SurfaceView寞秃,而且我們之間讓他match_parent了斟叼,所以,圖像在preview的時(shí)候春寿,肯定是會(huì)有拉伸的朗涩。那么如何處理這些變形呢?
我們可以通過(guò)改變SurfaceView大小的方式來(lái)實(shí)現(xiàn)绑改,在Android API Demo中谢床,Google也給我們提供了這樣一個(gè)實(shí)例:
路徑如下:
android-22/legacy/ApiDemos/src/com/example/android/apis/graphics/CameraPreview.java
Google就是通過(guò)設(shè)置新的大小來(lái)適應(yīng)預(yù)覽區(qū)域大小的方式來(lái)解決變形問(wèn)題的兄一,所以說(shuō),內(nèi)事不懂看源碼萤悴,外事不懂看Demo。
自定義取景畫面
聽上去非常高大上皆的,其實(shí)覆履,真的非常簡(jiǎn)單,你只需要用一個(gè)FrameLayout把用來(lái)Preview的SurfaceView包起來(lái)就OK了费薄,下面你想加什么硝全,就直接在FrameLayout中加吧,like this:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/ll">
<SurfaceView
android:id="@+id/sv_camera"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="拍照區(qū)域"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="center"
android:src="@drawable/demo"/>
</FrameLayout>
不光了ImageView楞抡,ViewPager也可以伟众,這樣甚至可以做一個(gè)可切換的水印相機(jī)了。是不是非常簡(jiǎn)單召廷,而且加入的一切都是可操作的凳厢,加動(dòng)效、顏色竞慢,分分鐘搞定先紫。
以上。
起步之后筹煮,我們要開始跑了遮精。
代碼下載,請(qǐng)移步全球最大同性程序猿交友社區(qū):
https://github.com/xuyisheng/CameraGuide
后續(xù)篇章也會(huì)在此repo中更新败潦。