現(xiàn)在的工作需要用到camera模塊,所以打算分析Zxing中的camera實現(xiàn),來了解android的camera释漆。zxing有完整的camera控制。
廢話不多說剪决,開始camera的分析之旅灵汪!
一.源碼下載? https://github.com/zxing/zxing
二.CameraManager類,就是我們要分析的第一個類柑潦。
接下來看一下CameraManager這個類為我們提供了什么方法
1.openDriver(SurfaceHolder holder) ?打開攝像頭
2.isOpen() ?判斷攝像頭是否打開
3.closeDriver() 關(guān)閉攝像頭
4.startPreview() 啟動預(yù)覽
5.stopPreview() 關(guān)閉預(yù)覽
6.setTorch(boolean newSetting) 設(shè)置對焦
7.requestPreviewFrame(Handler handler, int message) 獲取預(yù)覽的圖片(重要的一個方法)
8.getFramingRect() 獲取矩形框(根據(jù)屏幕分辨率然后按照一個固定的比例來設(shè)置方框大小)
9.getFramingRectInPreview() 獲取掃描區(qū)域的矩形框(getFramingRect是UI的顯示峻凫,這是掃描的區(qū)域渗鬼,其實豎屏掃描的時候,形式是正方形的框荧琼,但是獲取到的圖片確實長方形的)
10.setManualCameraId(int cameraId) 設(shè)置camera 的Id
11.setManualFramingRect(int width, int height) ?設(shè)置矩形框的寬高(UI)
12.buildLuminanceSource(byte[] data, int width, int height) 對byte[] Source進(jìn)行統(tǒng)一的處理(不同設(shè)備譬胎,相機返回的Source是不一樣的)
三.關(guān)鍵代碼分析
1.打開攝像頭OpenCamera
OpenCameraInterface打開打開攝像頭的控制類,OpenCamera類存放打開攝像頭的對象命锄,CameraFacing類,枚舉堰乔,兩個參數(shù),前置和后置攝像頭脐恩。staticOpenCameraopen方法對各種攝像頭的情況做了判斷镐侯,例如相判斷該設(shè)備有沒有攝像頭,然后判斷打開的攝像頭是不是空驶冒,空就啟動后置攝像頭等苟翻,具體看下面的代碼。
public staticOpenCameraopen(intcameraId) {
intnumCameras = Camera.getNumberOfCameras();//獲取攝像頭的個數(shù)
if(numCameras ==0) {
return null;
}
boolean explicitRequest = cameraId >=0;
Camera.CameraInfo selectedCameraInfo =null;
intindex;
if(explicitRequest) {
index = cameraId;
selectedCameraInfo =newCamera.CameraInfo();
Camera.getCameraInfo(index,selectedCameraInfo);
}else{//啟動后置攝像頭
index =0;
while(index < numCameras) {//不同的設(shè)備骗污,攝像頭的個數(shù)不一樣
Camera.CameraInfo cameraInfo =newCamera.CameraInfo();
Camera.getCameraInfo(index,cameraInfo);
CameraFacing reportedFacing = CameraFacing.values()[cameraInfo.facing];
if(reportedFacing == CameraFacing.BACK) {
selectedCameraInfo = cameraInfo;
break;
}
index++;
}
}
Camera camera;
if(index < numCameras) {
Log.i(TAG,"Opening camera #"+ index);
camera = Camera.open(index);//打開攝像頭
}else{
if(explicitRequest) {
Log.w(TAG,"Requested camera does not exist: "+ cameraId);
camera =null;
}else{
Log.i(TAG,"No camera facing "+ CameraFacing.BACK+"; returning camera #0");
camera = Camera.open(0);
selectedCameraInfo =newCamera.CameraInfo();
Camera.getCameraInfo(0,selectedCameraInfo);
}
}
if(camera ==null) {
return null;
}
return newOpenCamera(index,
camera,
CameraFacing.values()[selectedCameraInfo.facing],
selectedCameraInfo.orientation);
}
2.CameraManager類獲取預(yù)覽圖片setOneShotPreviewCallback這個接口就是獲取預(yù)覽圖片崇猫,previewCallback是回調(diào)接口
public synchronized voidrequestPreviewFrame(Handler handler, intmessage) {
OpenCamera theCamera =camera;
if(theCamera !=null&&previewing) {
previewCallback.setHandler(handler,message);
theCamera.getCamera().setOneShotPreviewCallback(previewCallback);
}
}
3.CameraConfigurationManager類,相機的配置管理類initFromCameraParameters初始化方法需忿。根據(jù)不同的設(shè)備诅炉,還有橫豎屏的方向蜡歹,給Camera計算出最好的size。然后通過setDesiredCameraParameters方法設(shè)置Camera的配置參數(shù)涕烧,例如設(shè)置聚焦模式月而,照片的大小,場景澈魄,特效等等景鼠。
(
一些常用的配置:
1、setPictureFormat()方法用于設(shè)置相機照片的格式,其參數(shù)是一個字符型參數(shù)痹扇,位于PixelFormat類中铛漓,我們在這里選擇PixelFormat.JPEG。)
2鲫构、setSceneMode()方法用于設(shè)置相機場景類型浓恶,其參是是一個字符型參數(shù),位于Parameters類中结笨,以SCENE_MODE_開頭包晰。
3、setZoom()方法用于設(shè)置相機焦距炕吸,其參數(shù)是一個整型的參數(shù)伐憾,該參數(shù)的范圍是0到Camera.getParameters().getMaxZoom()。
4赫模、setPictureSize()方法用于設(shè)置相機照片的大小树肃,參數(shù)為整型。
5瀑罗、setWhiteBalance()胸嘴,方法用于設(shè)置相機照片白平衡,其參數(shù)是一個字符型參數(shù)斩祭,位于Parameters類中劣像,以WHITE_BALANCE開頭。
6摧玫、setJpegQuality()方法用于設(shè)置相機照片的質(zhì)量耳奕,其參數(shù)是一個整型參數(shù),取值范圍為1到100席赂。
7吮铭、setFlashMode()方法用于設(shè)置閃光燈的類型,其參數(shù)是一個字符型參數(shù)颅停,位于Parameters類中谓晌,以FLASH_MODE_開頭。
8癞揉、setColorEffect()方法用于設(shè)置照片顏色特效的類型纸肉,其參數(shù)是一個字符型參數(shù)溺欧,位于Parameters類中,以EFFECT_開頭柏肪。
)
接下來看一下代碼:
void initFromCameraParameters(OpenCamera camera) {//初始化配置
Camera.Parameters parameters = camera.getCamera().getParameters();
WindowManager manager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
intdisplayRotation = display.getRotation();
intcwRotationFromNaturalToDisplay;
switch(displayRotation) { //手機的方向
caseSurface.ROTATION_0:
cwRotationFromNaturalToDisplay =0;
break;
caseSurface.ROTATION_90:
cwRotationFromNaturalToDisplay =90;
break;
caseSurface.ROTATION_180:
cwRotationFromNaturalToDisplay =180;
break;
......
cameraResolution= CameraConfigurationUtils.findBestPreviewSizeValue(parameters,screenResolution); ? ? ? ? ? ? ? //尋找最好的PreviewSize
Log.i(TAG,"Camera resolution: "+cameraResolution);
bestPreviewSize= CameraConfigurationUtils.findBestPreviewSizeValue(parameters,screenResolution);
......
if(isScreenPortrait == isPreviewSizePortrait) { //根據(jù)橫豎屏給previewSizeOnScreen賦值
previewSizeOnScreen=bestPreviewSize;
}else{
previewSizeOnScreen=newPoint(bestPreviewSize.y,bestPreviewSize.x);
}
......
}
CameraConfigurationUtils.findBestPreviewSizeValue這個方法的實現(xiàn)邏輯:(代碼不貼了)
首先姐刁,查找手機支持的預(yù)覽尺寸集合,如果集合為空烦味,就返回默認(rèn)的尺寸聂使;否則,對尺寸集合根據(jù)尺寸的像素從小到大進(jìn)行排序谬俄;
其次柏靶,移除不滿足最小像素要求的所有尺寸;
在者溃论,在剩余的尺寸集合中屎蜓,剔除預(yù)覽寬高比與屏幕分辨率寬高比之差的絕對值大于0.15的所有尺寸;
最后钥勋,尋找能夠精確的與屏幕寬高匹配上的預(yù)覽尺寸炬转,如果存在則返回該寬高比;如果不存在算灸,則使用尺寸集合中最大的那個尺寸扼劈。如果說尺寸集合已經(jīng)在前面的過濾中被全部排除,則返回相機默認(rèn)的尺寸值菲驴。
附加:UI的簡單分析
1.CaptureActivity是掃描的UI實現(xiàn)测僵,數(shù)據(jù)的傳遞都是通過Handler實現(xiàn)的,設(shè)置都保存到Preference里面谢翎,PreferenceManager就是設(shè)置保存的管理類,ViewfinderView繼承View沐旨,camera的UI顯示就是通過這個View森逮。
CaptureActivity類的部分代碼
public voidonCreate(Bundle icicle) {
super.onCreate(icicle);
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//設(shè)置常亮
......
PreferenceManager.setDefaultValues(this,R.xml.preferences, false);//設(shè)置默認(rèn)的配置,文件放在xml文件夾下
}
看一下onResume方法
protected void onResume() {
//... ? 省略初始化的操作
//根據(jù)配置文件來和傳感器切換橫豎屏的顯示
if(prefs.getBoolean(PreferencesActivity.KEY_DISABLE_AUTO_ORIENTATION, true)) {
getCurrentOrientation();
}else{
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
}
resetStatusView();
//...
if(Intents.Scan.ACTION.equals(action)) {
// 判斷跳轉(zhuǎn)過來的時候是否帶上了寬度磁携,高度
if(intent.hasExtra(Intents.Scan.WIDTH) && intent.hasExtra(Intents.Scan.HEIGHT)) {
int width = intent.getIntExtra(Intents.Scan.WIDTH,0);
int height = intent.getIntExtra(Intents.Scan.HEIGHT,0);
if(width >0&& height >0) {
cameraManager.setManualFramingRect(width,height);
}
}
//... 省略一些傳參和賦值
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
if(hasSurface) {
initCamera(surfaceHolder);
//最后初始化相機(主要就是這兩個方法褒侧,打開相機和初始化Handler
//cameraManager.openDriver(surfaceHolder);
//handler=new CaptureActivityHandler(this,decodeFormats,decodeHints,characterSet,cameraManager);)
}
2.其他還有對圖片的處理類PlanarYUVLuminanceSource和HybridBinarizer類,不同的設(shè)備獲取的照片是不一樣的谊迄,所以必須要做處理
總結(jié):camera部分就分析到這里闷供,如果上述有錯誤,請指出统诺,謝謝歪脏!
此文由暴風(fēng)雨1024原創(chuàng),?轉(zhuǎn)載粮呢,請注明出處婿失,謝謝钞艇!