前段时间,因为开发需求要求加上二维码扫描的功能,所以我也是通过网上一系列的资料学习实现了这个功能,很感谢网上一些大牛提供的学习博客,特别是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); } }