Zxing的简单使用及优化

随着二维码使用的流行,现在基本上什么APP都会用到二维码功能,所以不管怎样,在项目中集成二维码功能都是非常有必要的。

ZXing

ZXing是由谷歌开发的一款条码,二维码扫描工具
官方github地址: https://github.com/zxing/zxing

这个工程非常庞大包含很多项目,这里主要说一下关于android方面的。

说明

首先需要说明,因为工作的关系,APP的开发一般使用Eclipse,当然偶尔也会用AS。所以这里的开发环境是基于Eclipse的。由于官方android部分,包含了剪切板,扫描历史,分享,扫描二维码等等功能。如果只使用扫码功能,就需要剔除其他的功能。

这里奉上一个自用的库,里面已经包含了最新的zxing.jar包,并且进行了一些界面修改,因为我电脑的操作系统系繁体中文的,所以要在简体操作系统上使用记得要将编码格式改为GBK
链接在此,点击我下载
效果图:
效果图

简单的使用

clone项目,并导入Eclipse后
项目目录
选中项目,快捷键Alt+Enter,打开项目属性窗口,将项目设置为库
属性配置
再新建一个安卓项目,重复操作打开项目属性窗口,点击Add按钮将库项目添加到新项目中去。
在新项目中添加按键监听事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Button scanBarCodeButton = (Button) this.findViewById(R.id.btn_scan_barcode);
scanBarCodeButton.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
//打开扫描界面扫描条形码或二维码
Intent openCameraIntent = new Intent(BarCodeTestActivity.this,CaptureActivity.class);
startActivityForResult(openCameraIntent, 0);
}
});

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//处理扫描结果(在界面上显示)
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
String scanResult = bundle.getString("result");
resultTextView.setText(scanResult);
}
}

到这里就可以使用二维码扫描功能了,不要忘记在AndroidManifest.xml添加权限和对应Activity。

常用功能优化

横屏竖屏修改

1.修改CameraConfigurationManager类中的setDesiredCameraParameters方法

1
2
3
4
5
6
7
8
9
10
11
void setDesiredCameraParameters(Camera camera) {
Camera.Parameters parameters = camera.getParameters();
Log.d(TAG, "Setting preview size: " + cameraResolution);
parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
setFlash(parameters);
setZoom(parameters);

//**90度豎屏 0為橫屏
camera.setDisplayOrientation(90);
camera.setParameters(parameters);
}

2.修改CameraManager类中的getFramingRectInPreview方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public Rect getFramingRectInPreview() {
if (framingRectInPreview == null) {
Rect rect = new Rect(getFramingRect());
Point cameraResolution = configManager.getCameraResolution();
Point screenResolution = configManager.getScreenResolution();
//**橫屏
//rect.left = rect.left * cameraResolution.x / screenResolution.x;
//rect.right = rect.right * cameraResolution.x / screenResolution.x;
//rect.top = rect.top * cameraResolution.y / screenResolution.y;
//rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;

//**豎屏
rect.left = rect.left * cameraResolution.y / screenResolution.x;
rect.right = rect.right * cameraResolution.y / screenResolution.x;
rect.top = rect.top * cameraResolution.x / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
framingRectInPreview = rect;
}
return framingRectInPreview;
}

3.修改DecodeHandler类中的decode方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private void decode(byte[] data, int width, int height) {
long start = System.currentTimeMillis();
Result rawResult = null;

//*************************竖屏解码段*****************************
byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
rotatedData[x * height + height - y - 1] = data[x + y * width];
}
int tmp = width; // Here we are swapping, that's the difference to #11
width = height;
height = tmp;

PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height);
//**************************************************************

//*****橫屏解碼段*****
//PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(data, width, height);

......
}

竖屏拉伸问题

改为竖屏显示时,常常伴有画面拉伸的现象,解决该问题可以修改CameraConfigurationManager类中的initFromCameraParameters方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void initFromCameraParameters(Camera camera) {
Camera.Parameters parameters = camera.getParameters();
previewFormat = parameters.getPreviewFormat();
previewFormatString = parameters.get("preview-format");
Log.d(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString);
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
screenResolution = new Point(display.getWidth(), display.getHeight());
Log.d(TAG, "Screen resolution: " + screenResolution);

//---------------------竖屏拉伸解决函数-----------------
Point screenResolutionForCamera = new Point();
screenResolutionForCamera.x = screenResolution.x;
screenResolutionForCamera.y = screenResolution.y;
// preview size is always something like 480*320, other 320*480
if (screenResolution.x < screenResolution.y) {
screenResolutionForCamera.x = screenResolution.y;
screenResolutionForCamera.y = screenResolution.x;
}
cameraResolution = getCameraResolution(parameters, screenResolutionForCamera);
//-----------------------------------------------------
Log.d(TAG, "Camera resolution: " + screenResolution);
}

扫描框美化

绘制白色边框及绿色边角

1.扫描框是自定义组件,要改变样式需要修改ViewfinderView类的onDraw方法,首先添加白色边框方法和边角方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void drawFrame(Canvas canvas, Rect frame){
//**繪製白色邊框
paint.setColor(Color.WHITE);
canvas.drawRect(frame.left, frame.top, frame.right + 1, frame.top + 2, paint);
canvas.drawRect(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1, paint);
canvas.drawRect(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1, paint);
canvas.drawRect(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1, paint);
}
private void drawFrameCorner(Canvas canvas, Rect frame){
//**繪製4個綠色邊角
paint.setColor(Color.GREEN);
// 左上角
canvas.drawRect(frame.left, frame.top, frame.left + 20,frame.top + 10, paint);
canvas.drawRect(frame.left, frame.top, frame.left + 10,frame.top + 20, paint);
// 右上角
canvas.drawRect(frame.right - 20, frame.top, frame.right,frame.top + 10, paint);
canvas.drawRect(frame.right - 10, frame.top, frame.right,frame.top + 20, paint);
// 左下角
canvas.drawRect(frame.left, frame.bottom - 10, frame.left + 20,frame.bottom, paint);
canvas.drawRect(frame.left, frame.bottom - 20, frame.left + 10,frame.bottom, paint);
// 右下角
canvas.drawRect(frame.right - 20, frame.bottom - 10, frame.right,frame.bottom, paint);
canvas.drawRect(frame.right - 10, frame.bottom - 20, frame.right,frame.bottom, paint);
}

扫描线循环滚动

2.扫描线循环滚动关键代码段

1
2
3
4
5
6
7
int move = frame.top + slideTop;      //**掃描線循環滾動
if ((slideTop += SPEEN_DISTANCE) < frame.bottom - frame.top) {
canvas.drawRect(frame.left + 2, move-1, frame.right - 1, move+2, paint);
invalidate();
}else{
slideTop = 0;
}

3.修改ViewfinderView类的onDraw方法改变扫描框样式,只贴出关键部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Override
public void onDraw(Canvas canvas) {
......

if (resultBitmap != null) {
// Draw the opaque result bitmap over the scanning rectangle
//paint.setAlpha(OPAQUE);
paint.setAlpha(OPAQUE);
canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
}else{
//******************************扫描框修改*****************************
// Draw a two pixel solid black border inside the framing rect
drawFrame(canvas, frame); //**繪製白色邊框
drawFrameCorner(canvas, frame); //**繪製4個綠色邊角

// Draw a red "laser scanner" line through the middle to show decoding is active
paint.setColor(laserColor);
paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
int middle = frame.height() / 2 + frame.top;
int move = frame.top + slideTop; //**掃描線循環滾動
if ((slideTop += SPEEN_DISTANCE) < frame.bottom - frame.top) {
canvas.drawRect(frame.left + 2, move-1, frame.right - 1, move+2, paint);
invalidate();
}else{
slideTop = 0;
}
//***************************************************************
.....
}
}

实现重复扫描功能

有些应用可能会需要用到连续扫描的功能,例如当做扫描枪使用之类什么的。修改CaptureActivity的handleDecode方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public void handleDecode(Result result, Bitmap barcode) {
inactivityTimer.onActivity();
playBeepSoundAndVibrate();
String resultString = result.getText();
//FIXME
if (resultString.equals("")) {
Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();
}else {
Intent resultIntent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("result", resultString);
resultIntent.putExtras(bundle);
this.setResult(RESULT_OK, resultIntent);
}
hder.postDelayed(rePreview, 2500); // **解碼回調函數
}

/*
* 掃描界面回調
*/
private static Handler hder = new Handler();
Runnable rePreview = new Runnable() {
@Override
public void run() {
if(LoginActivity.connectSwitch){ connect(); }
CameraManager.get().startPreview();
handler.restartPreviewAndDecode();
}
};

关于回调方法,第二个参数是回调时间,部分机型不能设置太短,会造成程序崩溃
我对小米,索尼,华为,夏普等手机进行测试,2500毫秒是临界点,个人猜测是手机摄像头硬件的限制

1
hder.postDelayed(rePreview, 2500);