您的位置:首页 > 其它

站在巨人的肩膀上——二维码扫描功能的实现和优化过程

2016-03-28 21:15 417 查看
前段时间,因为开发需求要求加上二维码扫描的功能,所以我也是通过网上一系列的资料学习实现了这个功能,很感谢网上一些大牛提供的学习博客,特别是http://blog.csdn.net/xiaanming/article/details/10163203这篇博客对我的帮助很大,我也是在这个上面做了一些改进和优化,最后终于在我们的项目中加入了二维码扫描的功能。今天就来讲一下我的学习和实现过程:

1.首先,我在这个http://download.csdn.net/detail/xiaanming/5990219网站上下了上面那篇博客中提到的项目源码,它已经在原来的zxing源码上做了很多优化,而我在这个源码的基础上又做了一些处理,基本上解决了一些遗留的问题。

在这个项目中,其中ViewfinderView这个类是一个自定义控件,也就是扫描页用到的主要的控件,所以就先从这个自定义控件类入手进行修改。

(1)在我运行了这个项目后发现扫描框中总是会出现黄色的小点,其实是扫描点的颜色设置不对,也就是下面这个代码中的“resultPointColor”。

resultPointColor = resources.getColor(R.color.possible_result_points);
把上面这行代码中的possible_result_points设置成透明色即可。

(2)扫描线比较丑,因此我把它修改了成类微信的扫描线,也就是把绘制扫描线的那行代码替换一下,这个问题那篇博客的博主已经提到了,所以就跳过。

(3)在不同机型上扫描框的显示有比较大的不同,为了解决这适配问题,我在这个类加了一个dip转px的方法,代码如下:

<span style="font-size:12px;">public static int dip2px(Context context, float dipValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}</span>


然后在一些直接使用数字加上这个方法,例如四个边角的宽度:

<span style="font-size:12px;">/**
* 四个绿色边角对应的宽度
*/
private static final int CORNER_WIDTH = 10;</span>
改成下面这样:

/**
* 四个绿色边角对应的宽度
*/
private final int CORNER_WIDTH = dip2px(getContext(), 10);
(4)因为原来的代码其实只是画了四个绿色的直角,边框是由于除扫描框其他的区域灰色比较深造成的,所以我自己画了四个边框,代码如下,很容易理解:

// 画四个边框,左上右下
paint.setColor(Color.GRAY);
canvas.drawLine(frame.left, frame.top, frame.left, frame.bottom, paint);
canvas.drawLine(frame.left, frame.top, frame.right, frame.top, paint);
canvas.drawLine(frame.right - 1, frame.top, frame.right - 1, frame.bottom, paint);
canvas.drawLine(frame.left, frame.bottom - 1, frame.right, frame.bottom - 1, paint);

(5)因为我是想做成类似微信的扫描框,然后发现这个扫描框有点太大了,在代码中有这样一段注释和代码:

//中间的扫描框,你要修改扫描框的大小,去CameraManager里面修改
Rect frame = CameraManager.get().getFramingRect();


根据这句提示,我修改了CameraManager中的getFramingRect()方法,代码如下:

public Rect getFramingRect() {
Point screenResolution = configManager.getScreenResolution();
if (framingRect == null) {
if (camera == null) {
return null;
}
int width = screenResolution.x * 5 / 9;
int height = screenResolution.y * 3 / 10;
int leftOffset = (screenResolution.x - width) / 2;
int topOffset = (screenResolution.y - height) * 2 / 5;
framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
Log.d(TAG, "Calculated framing rect: " + framingRect);
}
return framingRect;
}


2.通过上面5个步骤,基本上这个扫描框就跟微信的差不多了,下面看一下扫描类中的处理:

因为我们项目是要求从扫描页跳转到一个链接显示的网页,所以我新建了一个SweepResultActivity类,并且加上了从相册选择二维码进行扫描的功能。先说一下扫描类的改动:

(1)处理扫描结果的方法

/**
* 处理扫描结果
* @param result
* @param barcode
*/
public void handleDecode(Result result, Bitmap barcode) {
if (inactivityTimer != null) {
inactivityTimer.onActivity();
}
playBeepSoundAndVibrate();
String resultString = result.getText();//扫描获取到的文字
Bitmap bitmap = barcode; // 扫描获取到的二维码图片
if (resultString.equals("")) {
Toast.makeText(SweepCodeActivity.this, getString(R.string.sweep_fail), Toast.LENGTH_LONG).show();
} else {
// 获取到文字和图片不为空
Toast.makeText(SweepCodeActivity.this, "扫描成功,链接为" + resultString, Toast.LENGTH_LONG).show();
Intent it = new Intent();
it.setClass(SweepCodeActivity.this, SweepResultActivity.class);
it.putExtra(SweepResultActivity.JUMP_TO_SWEEPRESULTACTIVITY_URL, resultString);
startActivity(it);
}
}
(2)添加从相册选的方法和图片返回的获取和处理

Button btnImagePick = (Button) findViewById(R.id.btn_title_more);
btnImagePick.setVisibility(View.VISIBLE);
btnImagePick.setText(getString(R.string.select_code_from_album));
btnImagePick.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
imagePick();
}
});
</pre><pre name="code" class="java">private Handler mHandler = new Handler(){

@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mProgress.dismiss();
switch (msg.what) {
case PARSE_BARCODE_SUCCESS:
handleDecode((Result) msg.obj, scanBitmap);
break;
case PARSE_BARCODE_FAIL:
Toast.makeText(SweepCodeActivity.this, (String) msg.obj, Toast.LENGTH_LONG).show();
break;
}
}
};

/**
* 从相册选取
*/
private void imagePick() {
try {
Intent intent = new Intent(Intent.ACTION_PICK, null);
// 指定调用相机拍照后照片的储存路径
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(intent, REQUEST_CODE);
} catch (Exception e) {
Toast.makeText(SweepCodeActivity.this, getString(R.string.edit_user_info_image_no_browser), Toast.LENGTH_LONG).show();
}
}

/**
* 处理扫描结果
* @param result
* @param barcode
*/
public void handleDecode(Result result, Bitmap barcode) {
if (inactivityTimer != null) {
inactivityTimer.onActivity();
}
playBeepSoundAndVibrate();
String resultString = result.getText();//扫描获取到的文字
Bitmap bitmap = barcode; // 扫描获取到的二维码图片
if (resultString.equals("")) {
Toast.makeText(SweepCodeActivity.this, getString(R.string.sweep_fail), Toast.LENGTH_LONG).show();
} else {
// 获取到文字和图片不为空
Toast.makeText(SweepCodeActivity.this, "扫描成功,链接为" + resultString, Toast.LENGTH_LONG).show();
Intent it = new Intent();
it.setClass(SweepCodeActivity.this, SweepResultActivity.class);
it.putExtra(SweepResultActivity.JUMP_TO_SWEEPRESULTACTIVITY_URL, resultString);
startActivity(it);
}
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_OK){
switch(requestCode){
case REQUEST_CODE:
//获取选中图片的路径
Cursor cursor = getContentResolver().query(data.getData(), null, null, null, null);
if (cursor.moveToFirst()) {
photo_path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
}
cursor.close();

mProgress = new ProgressDialog(SweepCodeActivity.this);
mProgress.setMessage(getString(R.string.sweeping));
mProgress.setCancelable(false);
mProgress.show();

new Thread(new Runnable() {
@Override
public void run() {
Result result = scanningImage(photo_path);
if (result != null) {
Message m = mHandler.obtainMessage();
m.what = PARSE_BARCODE_SUCCESS;
m.obj = result;
mHandler.sendMessage(m);
} else {
Message m = mHandler.obtainMessage();
m.what = PARSE_BARCODE_FAIL;
m.obj = getString(R.string.sweep_fail);
mHandler.sendMessage(m);
}
}
}).start();
break;
}
}
}

/**
* 扫描二维码图片的方法
*
* @param path
* @return
*/
public Result scanningImage(String path) {
if (TextUtils.isEmpty(path)) {
return null;
}
Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>();
hints.put(DecodeHintType.CHARACTER_SET, "UTF-8"); //设置二维码内容的编码

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // 先获取原大小
scanBitmap = BitmapFactory.decodeFile(path, options);
options.inJustDecodeBounds = false; // 获取新的大小
int sampleSize = (int) (options.outHeight / (float) 200);
if (sampleSize <= 0)
sampleSize = 1;
options.inSampleSize = sampleSize;
scanBitmap = BitmapFactory.decodeFile(path, options);
RGBLuminanceSource source = new RGBLuminanceSource(scanBitmap);
BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
QRCodeReader reader = new QRCodeReader();
try {
return reader.decode(bitmap1, hints);
} catch (NotFoundException e) {
e.printStackTrace();
} catch (ChecksumException e) {
e.printStackTrace();
} catch (FormatException e) {
e.printStackTrace();
}
return null;
}


这样SweepCodeActivity也算是完善了,不过运行的时候会发现会出现扫描的页面会变形,一个正方形的二维码会变高一些。

3.其实解决这个办法很简单,只要在CameraConfigurationManager类的initFromCameraParameters()中判断一下显示的高度和宽度大小就可以了,代码如下:

void initFromCameraParameters(Camera camera) {
Camera.Parameters parameters = camera.getParameters();
previewFormat = parameters.getPreviewFormat();
previewFormatString = parameters.get("preview-format");
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
screenResolution = new Point(width, height);
if (width < height) {
cameraResolution = getCameraResolution(parameters, new Point(height, width));
} else {
cameraResolution = getCameraResolution(parameters, screenResolution);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: