前言

现在的应用中二维码扫描已经成为一个应用必不可少的功能,现在大部分Android二维码扫描都是基于zxing和Zbar,这文章就来介绍一下基于zxing的二维码扫描。先看下效果图


虽然现在的关于二维码的文章有很多,但是很多都是交我们怎么使用,在使用的时候我也遇到了很多问题,比如。UI界面太丑,没有用的文件太多,扫描太慢,版本太老。本Demo使用的是3.x的是比较新的版本。我用的小米2A(api=19)亲测在正常情况下扫描时间和QQ微信差不多,我的界面是类防QQ的也是比较美观的。那么我们看看如何使用和文件的作用。


zxing

zxing官网 这是zxing官网。想了解更多的可以去官网,里面有文档不过在我看来。第三方库的使用我们没有必要完全了解,整体上我们了解所需要功能即可,这样减少了学习时间。


为什么选择zxing

  • google的开源项目,高可定制性
  • 可以识别多种码,不仅仅是二维码
  • 不依赖第三方库,使用起来简单

zxing的使用

  • zixng JAR
    我们可以去官网,如果是Android Studio的话也可以在线搜索zxing jar,导入完成后别忘了ADD library。
  • res文件

    • drawwable
      文件中主要放的是一些我们显示界面的图片和一些点击按钮的background
    • layout
      activity_qrcode_capture_layout.xml是zxing扫描的主界面,另外两个布局就是我们在开始图片中看到的,一个头部,一个脚部的布局
    • colors,dis,strings,styles,raw,xml
      这些是zxing中一些类的资源文件和我们自定义布局的一些资源文件,不导入会报错,raw是我们扫描完成后的音效,我们也可以根据需求改成自己的音效(但是要注意的是文件格式和名字尽量要相同,避免出错和资源找不到),xml就是zxing用到的资源文件,我们直接复制过来就行。

    PS:如果你是从官网拷贝,那么你自需要拷贝和我一样的就行。我这里只是多了一些drawwable中的布局图片和头部脚部2个布局

  • 关键类

    • app
      CaptureActivity 主要是我们的扫码界面,在这里我们引入我们自己的头部脚部布局,并给控件点击事件,在这个类中我们重点看这几个方法:
    /*** 闪光灯点击事件*/private OnClickListener click = new OnClickListener() {@Overridepublic void onClick(View v) {int id = v.getId();if (id == R.id.button_back) {//返回按钮finish();} else if (id == R.id.flash_btn) {//打开关闭闪光灯if (!isFlash) {CameraManager.get().turnLightOn();} else {CameraManager.get().turnLightOff();}isFlash = !isFlash;} else if (id == R.id.photo_btn) {//扫描二维码图片// 打开手机中的相册Intent innerIntent = new Intent(Intent.ACTION_GET_CONTENT); // "android.intent.action.GET_CONTENT"innerIntent.setType("image/*");//封装intentIntent wrapperIntent = Intent.createChooser(innerIntent, "选择二维码图片");startActivityForResult(wrapperIntent, REQUEST_CODE);} else if (id == R.id.qrcode_btn) {// 跳转到生成二维码页面Bitmap b = createQRCode();Intent intent = getIntent();intent.putExtra("QR_CODE", b);setResult(200, intent);finish();}}};

所有扫码界面的点击事件都在这个Activity中,可以看到zxing给我做了比较好的封装,只需要2行代码我们就可以控制闪光灯的开关。扫描图片二维码的点击事件也比较简单我们自需要打开相册。并用startActivityForResult启动相册。

 @Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (resultCode == RESULT_OK) {//图片选择返回uri = data.getData();//获取图片Uri//启动线程完成图片扫码new Thread(new Runnable() {@Overridepublic void run() {Result result = scanningImage(uri);if (result == null) {Looper.prepare();Toast.makeText(getApplicationContext(), "图片格式有误", Toast.LENGTH_SHORT).show();Looper.loop();} else {// 数据返回,在这里去处理扫码结果String recode = (result.toString());Intent data = new Intent();data.putExtra("result", recode);//返回启动扫码界面的ActivitysetResult(300, data);finish();}}}).start();}}

这个方法主要是处理上个方法打开相册选取图片后结果返回的处理。可以看到,我们拿到结果将扫码扫码界面finish()掉,并通过setResult()方法将数据交给跳转我们的扫码界面的活动去处理。 下面我们再来看下生成二维码

  • 生成二维码:可以看到生成二维码主要是调用了createQRCode();这个方法,那我来看下这个方法:
private Bitmap createQRCode() {int QR_WIDTH = 100;//生成二维码的宽int QR_HEIGHT = 100;//生成二维码的高try {// 需要引入core包QRCodeWriter writer = new QRCodeWriter();String text = Util.getIMEI(this);if (text == null || "".equals(text) || text.length() < 1) {return null;}// 把输入的文本转为二维码BitMatrix martix = writer.encode(text, BarcodeFormat.QR_CODE, QR_WIDTH, QR_HEIGHT);System.out.println("w:" + martix.getWidth() + "h:" + martix.getHeight());Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>();hints.put(EncodeHintType.CHARACTER_SET, "utf-8");BitMatrix bitMatrix = new QRCodeWriter().encode(text, BarcodeFormat.QR_CODE, QR_WIDTH, QR_HEIGHT, hints);int[] pixels = new int[QR_WIDTH * QR_HEIGHT];for (int y = 0; y < QR_HEIGHT; y++) {for (int x = 0; x < QR_WIDTH; x++) {if (bitMatrix.get(x, y)) {pixels[y * QR_WIDTH + x] = 0xff000000;} else {pixels[y * QR_WIDTH + x] = 0xffffffff;}}}// 生成的二维码Bitmap bitmap = Bitmap.createBitmap(QR_WIDTH, QR_HEIGHT, Bitmap.Config.ARGB_8888);bitmap.setPixels(pixels, 0, QR_WIDTH, 0, 0, QR_WIDTH, QR_HEIGHT);return bitmap;} catch (WriterException e) {e.printStackTrace();}return null;}

它主要就是将一个文本生成了一个宽高为100*100的bitmap。那我们就把这个方法简单就修改下,宽高由我们传入。文本也是由我们自己来决定。因为生成二维码并不依赖扫描的Activity,所以不管在那里我们只要调用createQRcode,就能生成二维码。

  • camera 主要是一些相机的管理类,FlashlightManager闪光灯的管理类,CameraManager相机的管理类,比如刚才我们的开关闪光灯。
  • decode 这里面主要是解码,因为解码也是比较耗时的炒作,所以我们是放在线程中去执行并通过handle来进行消息传递。这里重要的类是CaptureActivityHandler。处理的结果都是通过这个类传给我们的Activity的。既然是handle,那么我们就来看下handleMessage()方法
 @Overridepublic void handleMessage(Message message) {if (message.what == R.id.auto_focus) {// Log.d(TAG, "Got auto-focus message");// When one auto focus pass finishes, start another. This is the// closest thing to// continuous AF. It does seem to hunt a bit, but I'm not sure what// else to do.if (state == State.PREVIEW) {CameraManager.get().requestAutoFocus(this, R.id.auto_focus);}} else if (message.what == R.id.restart_preview) {Log.d(TAG, "Got restart preview message");restartPreviewAndDecode();} else if (message.what == R.id.decode_succeeded) {Log.d(TAG, "Got decode succeeded message");state = State.SUCCESS;Bundle bundle = message.getData();Bitmap barcode = bundle == null ? null : (Bitmap) bundle.getParcelable(DecodeThread.BARCODE_BITMAP);activity.handleDecode((Result) message.obj, barcode);Result result = (Result) message.obj;Intent mIntent = new Intent();mIntent.putExtra("SCAN_RESULT", result.getText());activity.setResult(Activity.RESULT_OK, mIntent);activity.finish();} else if (message.what == R.id.decode_failed) {// We're decoding as fast as possible, so when one decode fails,// start another.state = State.PREVIEW;CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode);} else if (message.what == R.id.return_scan_result) {Log.d(TAG, "Got return scan result message");activity.setResult(Activity.RESULT_OK, (Intent) message.obj);activity.finish();} else if (message.what == R.id.launch_product_query) {Log.d(TAG, "Got product query message");String url = (String) message.obj;Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);activity.startActivity(intent);}}

可以看到处理的结果都在这里。有成功失败和其他一些情况的处理,这里我们主要看成功时会返回resultCode=RESULT_OK,扫描的结果放在inent中key=SCAN_RESULT,这样我们就可以根据intent携带的key和value去做们相应的处理

BeepManager是控制我们的消息音。

BeepManager manager = new BeepManager(activity);//开启提示音manager.playBeepSoundAndVibrate();
  • encode,util,就是编码和工具类,这里我们就不看了
  • view中我们重点来看下ViewfinderView。这个就是我们扫码界面的主布局。他和我们自己添加的头部和脚部不同,它不是通过引入布局,而是自定义view画上去的。既然是画上去的那么就去看看onDraw()方法做了什么
 @Overridepublic void onDraw(Canvas canvas) {/*** 想修改扫描框的位置修改CameraManager中的参数*/Rect frame = CameraManager.get().getFramingRect();if (frame == null) {return;}if (!isFirst) {isFirst = true;slideTop = frame.top;slideBottom = frame.bottom;}int width = canvas.getWidth();int height = canvas.getHeight();/*** 画白线矩形*/paint.setStyle(Paint.Style.STROKE);paint.setColor(Color.WHITE);path.moveTo(frame.left + CORNER_OFFEST, frame.top + CORNER_OFFEST);path.lineTo(frame.right - CORNER_OFFEST, frame.top + CORNER_OFFEST);path.lineTo(frame.right - CORNER_OFFEST, frame.bottom - CORNER_OFFEST);path.lineTo(frame.left + CORNER_OFFEST, frame.bottom - CORNER_OFFEST);path.close();canvas.drawPath(path, paint);// Draw the exterior (i.e. outside the framing rect) darkenedpaint.setColor(resultBitmap != null ? resultColor : maskColor);paint.setStyle(Paint.Style.FILL);/*** 画上下左右4个位置的半透明黑色布局*/canvas.drawRect(0, 0, width, frame.top, paint);canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1,paint);canvas.drawRect(0, frame.bottom + 1, width, height, paint);if (resultBitmap != null) {// Draw the opaque result bitmap over the scanning rectanglepaint.setAlpha(OPAQUE);canvas.drawBitmap(resultBitmap, null, frame, paint);} else {/*** 画4个角的小矩形,每个角2个矩形 共8个*/paint.setColor(getResources().getColor(R.color.view_rect));canvas.drawRect(frame.left, frame.top, frame.left + ScreenRate,frame.top + CORNER_WIDTH, paint);canvas.drawRect(frame.left, frame.top, frame.left + CORNER_WIDTH,frame.top + ScreenRate, paint);canvas.drawRect(frame.right - ScreenRate, frame.top, frame.right,frame.top + CORNER_WIDTH, paint);canvas.drawRect(frame.right - CORNER_WIDTH, frame.top, frame.right,frame.top + ScreenRate, paint);canvas.drawRect(frame.left, frame.bottom - CORNER_WIDTH, frame.left+ ScreenRate, frame.bottom, paint);canvas.drawRect(frame.left, frame.bottom - ScreenRate, frame.left+ CORNER_WIDTH, frame.bottom, paint);canvas.drawRect(frame.right - ScreenRate, frame.bottom- CORNER_WIDTH, frame.right, frame.bottom, paint);canvas.drawRect(frame.right - CORNER_WIDTH, frame.bottom- ScreenRate, frame.right, frame.bottom, paint);/*** 定义每秒的移动速度*/slideTop += SPEEN_DISTANCE;if (slideTop >= frame.bottom) {slideTop = frame.top;}lineRect.left = frame.left;lineRect.right = frame.right;lineRect.top = slideTop;lineRect.bottom = slideTop + 18;canvas.drawBitmap(((BitmapDrawable) (getResources().getDrawable(R.drawable.fle))).getBitmap(), null, lineRect,paint);paint.setColor(Color.WHITE);paint.setTextSize(TEXT_SIZE * density);paint.setAlpha(0x40);paint.setTypeface(Typeface.create("System", Typeface.BOLD));String text = getResources().getString(R.string.scan_text);float textWidth = paint.measureText(text);canvas.drawText(text,(width - textWidth) / 2,(float) (frame.bottom + (float) TEXT_PADDING_TOP * density),paint);Collection<ResultPoint> currentPossible = possibleResultPoints;Collection<ResultPoint> currentLast = lastPossibleResultPoints;if (currentPossible.isEmpty()) {lastPossibleResultPoints = null;} else {possibleResultPoints = new HashSet<ResultPoint>(5);lastPossibleResultPoints = currentPossible;paint.setAlpha(OPAQUE);paint.setColor(resultPointColor);for (ResultPoint point : currentPossible) {canvas.drawCircle(frame.left + point.getX(), frame.top+ point.getY(), 6.0f, paint);}}if (currentLast != null) {paint.setAlpha(OPAQUE / 2);paint.setColor(resultPointColor);for (ResultPoint point : currentLast) {canvas.drawCircle(frame.left + point.getX(), frame.top+ point.getY(), 3.0f, paint);}}//不断重绘 完成光标移动postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top,frame.right, frame.bottom);}}

注释比较清晰,掌握基本的自定义View只是就可以画出来我们展示的效果。
最后我们在看下我们MainActivity的调用:

 @Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == 200) {if (resultCode == Activity.RESULT_OK) {//还记得我们在讲CaptureActivityHandler时 成功就会发送SCAN_RESULT这个key值的intent嘛?String code = data.getStringExtra("SCAN_RESULT");Log.d(TAG, "onActivityResult:----> " + code);if (code.contains("http") || code.contains("https")) {//二维码mTextView.setText(code);} else if ((!code.contains("http") || !code.contains("https")) && code != null && TextUtils.isEmpty(code)) {//条形码数字//这里注意:如果你扫描的是商品条形码。返回的条形码数字//这里如果你使用webview是无法解析的,一般我们需要查询//的api接口或是数据库查找才能展示我们想要的结果 mTextView.setText(code);} else {Toast.makeText(this, "error", Toast.LENGTH_SHORT).show();}}if (resultCode == 300) {//扫描图片String code = data.getStringExtra("result");Log.d(TAG, "onActivityResult:---->result " + code);mTextView.setText(code);}if (resultCode == 200) {//生成二维码回调Bitmap bitmap = Util.createQRCode(dip2px(this, 200), dip2px(this, 200), "https://www.baidu.com/");mImageView.setImageBitmap(bitmap);}}}

好了,zxing的使用和相关类的功能我们有了一个大致的了解。回头我们在看看zxing的优点:可以看到封装性比较好我们不需要多做什么处理,我们需要什么功能就在基础上加什么功能,源码都给我们了,我们也知道每个类是什么作用还不是想怎么改怎么改嘛(高可定制性),还有在使用第三方库的时候我们比较担心的就是我们需要一个库的功能,但是这个库却依赖很多其他的库,当其他库发生改动的时候我们需要的库也要改这就很烦。


结束

在这里我只是做一个抛砖引玉的作用,不管你想要什么样的UI还是什么样的布局,我在方法中加了注释不喜欢那里将代码删除写上自己想要的效果就好。毕竟我还是个小白菜鸟。如果你也在学习的路上,想在你自己应用加上扫码功能,希望这篇文章能给你一些帮助。如果像直接使用下面我给出了源码,可以直接放入项目中也可导入library。
写的不好大家多多谅解。如有错误真心希望大家提出来。最后希望大家一起进步。加油!!!


源码地址

基于zxing的二维码扫描相关推荐

  1. 基于zxing的二维码扫描(软件界面)

    原文地址:https://www.jianshu.com/p/ee2ee7677831 前言 现在的应用中二维码扫描已经成为一个应用必不可少的功能,现在大部分Android二维码扫描都是基于zxing ...

  2. Android 基于Zxing的二维码扫描优化

    最近公司项目App中要集成二维码扫描来适应在户外工作的时候,对码头集装箱等上面贴的A4纸张打印的二维码进行识别, 一般App二维码集成后,能扫出来就不管了,但是我们在集成成功后,根据用户反馈,在户外的 ...

  3. 修正Android基于ZXing的二维码扫描——横竖屏自由切换

    概述: 此博客是基于开源的框架ZXing.ZXing用Java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的端口.ZXing可以实现使用手机的内置的摄像头完成条形码的扫描及解码.该 ...

  4. 基于zxing生成二维码

    目录 1.二维码的原理 2.基于zxing生成二维码 3.逻辑及其应用 完整代码:https://github.com/122537067/zxingQRcodeLogoColor 效果图: 1.二维 ...

  5. JAVA实现基于ZXing的二维码自动生成与图片合成

    JAVA实现基于ZXing的二维码自动生成与图片合成 近日做项目需要生成带有信息的二维码,并嵌入到一张图片中.实现思路采用Zxing生成二维码,java图形库进行图片的嵌入. 生成二维码 ZXing是 ...

  6. Android实战——Zxing实现二维码扫描

    Zxing实现二维码扫描 前言: 本篇文章从初学者的角度出发,从一个不知道对二维码扫描怎么下手的工作者,需要一个简单的扫描功能的话,可以阅读该篇文章.作为Google开源框架Zxing,里面的文件很大 ...

  7. 基于ZXing的二维码,你可以这样改造它

    概述: 如果你下载了ZXing的源码和ZXing的core.jar包,那么你现在就可以进行如下的一系列修改来自定义你的二维码扫描了. 本文链接:http://blog.csdn.net/lemon_t ...

  8. 【IOS】集成zxing(二维码扫描)

    现在zxing已经到了2.2版本,以前的集成方式出了点问题.下面我做出一点修正. 以前的版本的集成方法,参考:http://blog.devtang.com/blog/2012/12/23/use-z ...

  9. Android:实际运用Zxing集成二维码扫描 及 自定义扫码界面(demo源码)

    二维码扫描,各大主流App必不可少的功能,而且google已将轮子替我们造好,直接拿来使用即可.以下是教学如何将Zxing开源库集成到自己项目中,并且自定义扫码界面,后期可根据自己的业务需求进行修改, ...

最新文章

  1. 在Ubuntu中用anaconda快速安装opencv3
  2. luogu P4183 Cow at Large P (暴力吊打点分治)(内有时间复杂度证明)
  3. caffe 练习1:training LeNet on MNIST with Caffe/ 用LeNet识别手写字符集 Mnist------by 香蕉麦乐迪
  4. fantouch os Android 7,Funtouch OS 3.1 with Android 7.1升级计划
  5. uart串口通信_听说UART与STM32的HAL库更配哦
  6. 计算机加经济学加自动化,MIT经济学家戳破机器人真相:除了能取代你,价值微乎其微...
  7. 一个函数要使用另一个函数中的数据
  8. 不借助第三方变量实现两个整数变量值的互换
  9. Java通过银行卡号获取卡属银行(含校验)
  10. 《C陷阱与缺陷》 阅读总结
  11. 机器学习算法初识—二分k均值算法
  12. 台式计算机套什么定额,计算机电缆套定额
  13. 【 C++ OpenCV画旋转矩形 并返回四个顶点 】
  14. php 依赖安装顺序6,构建PHP框架:第6部分-依赖倒置,控制倒置,哦,天哪!
  15. pythonRuntimeError: Cannot re-initialize CUDA in forked subprocess. To use CUDA with multiprocessing
  16. BP神经网络隐含层节点数的确定
  17. 如何快速定位自己热爱的工作
  18. java实现九宫格解锁_Java计算手机九宫格锁屏图案连接9个点的方案总数
  19. sqlmap详细教程
  20. ZigBee协议栈简介和流程

热门文章

  1. Android 手机直播聚合
  2. NLP | Word2Vec之基于Negative Sampling的 CBOW 和 skip-gram 模型
  3. 基于阿里云生态构建网聚宝业务监控系统
  4. 动态使用element-plus 的图标
  5. Arduino - DIY自动浇花系统(土壤湿度传感器+水泵)
  6. 设备加密有什么作用?
  7. 使用ffmpeg将裸码文件转成wav文件
  8. excel数学分析相关知识
  9. 利用Http实现屏幕监控
  10. 射频功放学习之MATLAB绘制ADS S11参数曲线