前言

最近一段时间研究了一下,android二维码扫码功能的实现。有些体会,在此总结一下。
实现二维码的扫码功能,在网上搜了一下,能够发现大部分都提到说使用Google开源的Zxing,现在基本上都是使用的Zxing来做的。所以我们的扫码功能也是基于Zxing来实现。(ZBar的话,看了下其仓库,都好几年没有维护了;微信的扫一扫是使用其自己团队研发的框架,还没有开源,所以没法使用)
关于Zxing扫码的使用,会分为两至三遍文章说明。第一篇的话,主要讲如何把Zxing跑起来,对其大致的工作流程有个了解(当初刚研究Zxing的时候,搞了半天没把sample跑出来,糗大了。。。。)。之后的博文会说明如何来自定义自己想要的扫码界面,扫码结果处理。最后再说说如何可以提高扫码效率。


开撸

根据我们上面所提的计划,现在先完成第一部分的内容。先把Zxing跑起来,看看是什么样子。之前一度天真的认为,Zxing会提供一些API文档,自己调用一下就可以了。事实上不是这样的,Zxing他没有提供API文档,也就是说,虽然我们可以在as上面编译Zxing的库,比如:compile 'com.google.zxing:core:3.3.0’之类的。但是我们不知道该怎么使用它啊。所以我们不能通过这种方式来实现。那我们怎么使用zxing呢?

步骤

首先我们去 Zxing仓库 把zxing项目clone下来。接着在一个项目里面选择file->new->import Module方式来导入Zxing项目作为自己项目的library。如图:

这里选择zxing包下面的android包。这个android包也就是zxing的sample。是一个eclipse项目(O__O "…)。然后就OK,Next,Finish一套~
然后我们发现导入后,项目结构变成了这样:

android 。。这个库的名字,我起的有点随便了~哈哈。。
发现引进来的库没有变成库模块,变成了应用模块,这就出现了两个应用模块,没事,这还只是刚开始,我们接着改。
首先,我们把android库里面的build.gradle文件中的
apply plugin: ‘com.android.application’ 语句改成 apply plugin: ‘com.android.library’,这就表示android从应用模块变成了库模块。我们同步一下,发现报错了。

错误显示,库文件不能设置application id,不设置那就删掉被,我们删掉gradle文件里的 applicationId “com.google.zxing.client.android” 。删掉后再同步一下,发现没错了。
接着我们在gradle文件中加入如下语句:

dependencies{compile 'com.google.zxing:android-core:3.3.0'compile 'com.google.zxing:core:3.3.0'compile 'com.google.zxing:android-integration:3.3.0'
}

这里,引用的android-integration库和android-core库是辅助库,后期我们自己设计自己的扫码库时,这两个可以不要,现在演示sample的话就先留着。核心库是core,必要的。这里这个android-integration库,我在网上发现也有大佬是对其进行源码修改,使得通过integration库里面的辅助类IntentIntegrator 来使用zxing自定义自己的扫码界面的时候方便了很多。也挺不错。会很方便,不需要管理面的源码逻辑。扯远了。。。
回过头来,咱继续。库引进来之后,等等还没完全引进,项目右击选择 open module settings。

这样之后,才算引入项目,同步后,悲催的发现,又出错了。

根据错误提示,咱把android库模块里面的AndroidManifest.xml文件打开,删除 android:icon="@drawable/launcher_icon"

<intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter>

此刻再同步项目,发现没有错误了。
接着咱就使用它呗,我们准备实现这样的逻辑,点击button按钮,跳转zxing 的扫码界面。先上代码:

package com.example.zxingtest;import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;public class MainActivity extends AppCompatActivity {private Button scanner;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);scanner = (Button) findViewById(R.id.scanner);scanner.setOnClickListener(mScannerListener);}private View.OnClickListener mScannerListener = new View.OnClickListener() {@Overridepublic void onClick(View v) {requestPermission();}};private void requestPermission() {if (ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED) {if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,Manifest.permission.CAMERA)) {Toast.makeText(MainActivity.this, "二维码扫码需要相机权限", Toast.LENGTH_SHORT).show();} else {ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CAMERA},0);}}else{goScanner();}}@Overridepublic void onRequestPermissionsResult(int requestCode,@NonNull String permissions[], @NonNull int[] grantResults) {switch (requestCode) {case 0: {// If request is cancelled, the result arrays are empty.if (grantResults.length > 0&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {goScanner();}return;}}}private void goScanner(){IntentIntegrator integrator = new IntentIntegrator(this);integrator.initiateScan();}public void onActivityResult(int requestCode, int resultCode, Intent intent) {IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);if (scanResult != null) {// handle scan resultToast.makeText(this,scanResult.toString(),Toast.LENGTH_SHORT).show();}// else continue with any other code you need in the method}
}

代码逻辑很简单,是点击button,然后在点击事件中去检查是否具有相机权限,没有的话去请求权限,有相机权限的话就直接走goScanner方法。这个方法里面涉及的内容,稍后再说,然后就是扫码结果的处理是在onActivityResult做处理,这边是选择toast弹出信息。流程就是这样,我们跑下项目。蛋疼的发现有报错咯~~

这里我们在出错的地方通过alt+enter快捷键吧switch语句,转化成if语句就行了。(根据lint的提示说在Android 库模块中,资源id不能用在switch语句中。。),此外还出现一个很蛋疼的问题,就是CalendarParsedResult类中出现,没有getStartTimestamp()方法和getEndTimestamp()方法。我特意看一下GitHub上面的Zxing项目中的CalendarParsedResult代码,发现有这两个方法啊。也是醉了。。这里我是把getStartTimestamp()替换为getStart().getTime(),getEndTimestamp()替换为getEnd().getTime()。这样就没问题了。
我们运行一下项目。点击button按钮,发现弹出了一个安装提示框?这泥马什么鬼?使用zxing还需要安装他的zxing扫码软件啊,不管了先安装试试,安装成功后,就跳到扫码界面 是这个样子:

(@ο@) 哇~竟然是横屏,样式也不是我想要的。。先别急,往下咱先测测功能。
我这边用了一个在线生成二维码的网站,生成二维码后,我们扫码二维码,结果首先会出现在扫二维码的界面上,然后会toast出扫码结果。

看结果出来了。这里我们就可以根据这个结果,做我们想要的处理。
效果我们看到了,接下来我们再回过头,看看我们遗留的问题,首先我们看上面代码的goScanner方法:

IntentIntegrator integrator = new IntentIntegrator(this);integrator.initiateScan();

代码很少就两句,我们进去看一下。

<p>A utility class which helps ease integration with Barcode Scanner via {@link Intent}s. This is a simple way to invoke barcode scanning and receive the result, without any need to integrate, modify, or learn the project's source code.</p>

这个总结的很到位,就通过IntentIntegrator可以很容易调用扫码功能,不需要知道什么源码,修改什么东西。是一个辅助类。
看一下他的构造方法:

比较简单,常规的赋值。我们看一下integrator.initiateScan()方法实现。

public final AlertDialog initiateScan() {return initiateScan(ALL_CODE_TYPES, -1);}

initiateScan是初始化一个支持已知的所有码格式的扫码器,其方法会调用initiateScan(ALL_CODE_TYPES, -1)方法。

public final AlertDialog initiateScan(Collection<String> desiredBarcodeFormats, int cameraId) {Intent intentScan = new Intent(BS_PACKAGE + ".SCAN");intentScan.addCategory(Intent.CATEGORY_DEFAULT);// check which types of codes to scan forif (desiredBarcodeFormats != null) {// set the desired barcode typesStringBuilder joinedByComma = new StringBuilder();for (String format : desiredBarcodeFormats) {if (joinedByComma.length() > 0) {joinedByComma.append(',');}joinedByComma.append(format);}intentScan.putExtra("SCAN_FORMATS", joinedByComma.toString());}// check requested camera IDif (cameraId >= 0) {intentScan.putExtra("SCAN_CAMERA_ID", cameraId);}String targetAppPackage = findTargetAppPackage(intentScan);if (targetAppPackage == null) {return showDownloadDialog();}intentScan.setPackage(targetAppPackage);intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);attachMoreExtras(intentScan);startActivityForResult(intentScan, REQUEST_CODE);return null;}

我们看下,上面代码的实现,首先方法的两个参数,一个是支持扫码类型的集合,我们之前调用该方法时传入的是ALL_CODE_TYPES变量,该变量被赋值为null,表示支持所有类型的码。然后cameraId传的是-1 ,表示默认的camera。他这里的话,就是可以让大家指定扫码类型,和camera。
接着看,他创建了一个intent,然后对desiredBarcodeFormats扫码支持类型做了判断,如果不为null,就把值传入intent,接着他调用findTargetAppPackage()方法,目的来找我们手机中有木有那啥,他家的二维码扫码软件,如果没有的话那就弹出一个下载提示框。(这就是我们之前软件运行时,出现下载安装提示框的原因),接着呢,就是对intent的一个设置,我们注重看一下attachMoreExtras()这个方法。

private void attachMoreExtras(Intent intent) {for (Map.Entry<String,Object> entry : moreExtras.entrySet()) {String key = entry.getKey();Object value = entry.getValue();// Kind of hackyif (value instanceof Integer) {intent.putExtra(key, (Integer) value);} else if (value instanceof Long) {intent.putExtra(key, (Long) value);} else if (value instanceof Boolean) {intent.putExtra(key, (Boolean) value);} else if (value instanceof Double) {intent.putExtra(key, (Double) value);} else if (value instanceof Float) {intent.putExtra(key, (Float) value);} else if (value instanceof Bundle) {intent.putExtra(key, (Bundle) value);} else {intent.putExtra(key, value.toString());}}

这个方法里面是对intent的封装,这里是把moreExtras里面的值设置给intent,那moreExtras又是什么呢?我们类中搜索到

private final Map<String,Object> moreExtras = new HashMap<String,Object>(3);

moreExtras是一个Hashmap,初始容量是3。我们随后看到这个方法:

public final void addExtra(String key, Object value) {moreExtras.put(key, value);}

官方的介绍如下:

Finally, you can use {@link #addExtra(String, Object)} to add more parameters to the Intent used to invoke the scanner. This can be used to set additional options not directly exposed by this simplified API.

也就是说,这是一个添加参数来控制scanner的提供给外界的方法。之前上文有说到看到一些大佬是通过对 IntentIntegrator源码修改,其方式比如说去掉alertdialog安装弹窗,对intent的配置等类似的修改来达到对扫码界面的控制。扯远了,收~ (关于扫码结果的回调是在onActivityResult方法,其中涉及到了parseActivityResult()方法,这个方法比较简单就步说了,看源码很容易懂。) 说了半天的intent的配置,那intent传递到哪里了呢?跟我们平时经常遇到的情况一样,intent传递到了activity里面。这里是CaptureActivity。这个activity就是我们之前演示项目的扫码界面。

就是上面这样子的。这里由于篇幅原因,就不细说CaptureActivity了,后续的博文我们再说。因为后面关于自定义自己想要的扫码界面,效果,就是要重写这个CaptureActivity。
我们这里看一下CaptureActivity中是怎么处理intent的。

@Overrideprotected void onResume() {super.onResume();...Intent intent = getIntent();String action = intent.getAction();String dataString = intent.getDataString();if (intent != null) {String action = intent.getAction();String dataString = intent.getDataString();if (Intents.Scan.ACTION.equals(action)) {// Scan the formats the intent requested, and return the result to the calling activity.source = IntentSource.NATIVE_APP_INTENT;decodeFormats = DecodeFormatManager.parseDecodeFormats(intent);decodeHints = DecodeHintManager.parseDecodeHints(intent);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);}}if (intent.hasExtra(Intents.Scan.CAMERA_ID)) {int cameraId = intent.getIntExtra(Intents.Scan.CAMERA_ID, -1);if (cameraId >= 0) {cameraManager.setManualCameraId(cameraId);}}String customPromptMessage = intent.getStringExtra(Intents.Scan.PROMPT_MESSAGE);if (customPromptMessage != null) {statusView.setText(customPromptMessage);}} else if (dataString != null &&dataString.contains("http://www.google") &&dataString.contains("/m/products/scan")) {// Scan only products and send the result to mobile Product Search.source = IntentSource.PRODUCT_SEARCH_LINK;sourceUrl = dataString;decodeFormats = DecodeFormatManager.PRODUCT_FORMATS;} else if (isZXingURL(dataString)) {// Scan formats requested in query string (all formats if none specified).// If a return URL is specified, send the results there. Otherwise, handle it ourselves.source = IntentSource.ZXING_LINK;sourceUrl = dataString;Uri inputUri = Uri.parse(dataString);scanFromWebPageManager = new ScanFromWebPageManager(inputUri);decodeFormats = DecodeFormatManager.parseDecodeFormats(inputUri);// Allow a sub-set of the hints to be specified by the caller.decodeHints = DecodeHintManager.parseDecodeHints(inputUri);}...

从这里我们可以看出来,在CaptureActivity的onResume方法回调中,对intent进行了处理,并根据intent里面的值对CaptureActivity类里面的一些变量进行设置,或是对扫码界面大小的调整等等进行对应的配置。里面细致的流程如果有兴趣可以看源码。

总结

到这,这篇主要目的是运行zxing项目和了解其大致流程的博文结束了,通过这篇博文我们也发现了,仅仅通过使用zxing其原生的库,代码。是不使用在我们的项目中的,会有很多问题,比如会提示安装下载后才能使用扫码,扫码横屏,扫码界面不是我们想要的等问题。这些问题会在后面的博文中 [置顶] Android 基于Zxing的扫码功能实现(二)都给解决掉。
通过引入这个zxing库,我们也算是有个经验,遇到错误,没什么,不可怕,看看提示的错误是什么,冷静分析问题,解决问题。


扫码加入我的个人微信公众号:Android开发圈 ,一起学习Android知识!!

Android 二维码的扫码功能实现(一)相关推荐

  1. Android 二维码扫码功能实现(Zxing集成,避坑启示)

    这两天想要实现一个能扫码的app,找了一下,发现zxing用的比较多,就在网上找了找怎么用zxing.我主要是按照宇宝守护神的博文"Android 二维码的扫码功能实现(一)"的步 ...

  2. Android二维码名片生成扫码识别

    功能描述: 填入联系人信息,动态生成二维码名片,长按二维码保存,打开扫码识别内容.app实现了在android6.0或以上动态权限申请功能,适合新手研究.搭建方法请看入门教程菜单中的androidst ...

  3. Android 二维码 生成和识别(附Demo源码)

    Posted on 2013-08-25 23:15 泡泡糖 阅读( 20424) 评论( 7) 编辑 收藏 今天讲一下目前移动领域很常用的技术--二维码.现在大街小巷.各大网站都有二维码的踪迹,不管 ...

  4. 解决Android二维码扫描ZXing竖屏拉伸变长闪退扫描区域小等问题

    Android 基于google Zxing实现二维码.条形码扫描,仿微信二维码扫描效果 这篇文章写的不错,但是按照这篇文章使用二维码的话会出现竖屏扫描图像拉伸变长的问题(有一个地方计算错误),解决拉 ...

  5. java实现生成二维码及扫码登录

    java实现生成二维码及扫码登录 1. 场景描述 2. 实现思路 3. 代码实现过程 3.1 pom.xml 3.2 二维码工具类 3.3 生成二维码并下载为图片 3.4 扫码登录 1. 场景描述   ...

  6. Android二维码扫描之ZXing快速项目集成

    <Android二维码扫描之ZXing快速项目集成> 二维码扫描现在是一直比较多的应用场景,Android的开源项目ZXing提供了完整.成熟的解决方案,如果仅仅是出于快速开发的目的,可以 ...

  7. 如何把视频转换生成二维码,扫码直接播放?

    如何把自己的视频生成二维码,扫一扫二维码,就能直接观看视频?还有,最好不要有任何广告? 能实现吗?能!用酷播云视频二维码,一键搞定! 官网地址:http://www.cuplayer.com/clou ...

  8. Android二维码扫描:基于barcodescanner

     Android二维码扫描:基于barcodescanner 二维码扫描现在成为一种非常常见的APP基础功能,附录1是我曾经用过的二维码/条形码扫描开源项目,但是附录1的项目集成和二次定制比较繁琐 ...

  9. Android 二维码扫描(仿微信界面),根据Google zxing

    Android 二维码扫描(仿微信界面),根据Google zxing Android项目开发中经常会用到二维码扫描,例如登陆.支付等谷歌方面已经有了一个开源库(地址: https://github. ...

  10. Android二维码扫描开发(一):实现思路与原理

    2019独角兽企业重金招聘Python工程师标准>>> Android二维码扫描开发(一):实现思路与原理 Android二维码扫描开发(二):YUV图像格式详解 Android二维 ...

最新文章

  1. 总结Unity 初学者容易犯的编译与运行时错误(第三部分)
  2. JAVA post和get方式请求远程HTTP接口
  3. 环境监控告警系统之TIM即时消息推送部署(二)
  4. will not add file alias already exists in index(git上传代码出错)
  5. hdu 4502 一维dp
  6. Spring Boot log4j多环境日志级别的控制
  7. 计算机创建文档教案,计算机基础知识教案
  8. 练习:查找指定目录(包括子目录)下的视频(格式为.mp4,.rmvb,.avi),并将目录存放在一个文件中...
  9. Java目前主流框架
  10. android -------- ConstraintLayout 宽高比和偏移量比(三)
  11. PHP 使用GD库合成带二维码的海报步骤以及源码实现
  12. 推荐 5 个免费高品质的图片资源网站
  13. 数独大师级技巧_零基础入手攻克专家级数独难题实战案例
  14. IAR6.3创建MSP430工程
  15. 蚁群算法(ACO)最短路径规划(MATLAB)
  16. Spectral clustering(谱聚类)算法的实现
  17. 计算机初学者需要知道的一些DOS命令
  18. 学习OpenCV2——Mat之通道的理解
  19. qnx 设备驱动开发_QNX简介
  20. 武汉交通职业学院计算机协会,2018年中国计算机学会(CCF)职业教育大会在苏州顺利落幕...

热门文章

  1. python导包问题解决方案(一字千金)
  2. Nvidia PhysX 学习文档1: Welcome to PhysX
  3. 如何在windows中配置自动生成崩溃文件(.dmp)
  4. Visual C# 操作 Excel 文件(二) 封裝成 MyExcel 類
  5. 打开本地html加载网页慢,电脑检查网速正常但打开网页慢如何解决
  6. 蓝桥杯:合唱队形(C语言)
  7. Ipad项目中用到的UIModalPresentationFormSheet,点击阴影部分dismiss 当前presented的controller
  8. ES系列:字段类型不对时,如何保存文档到索引
  9. 数字化是新物流的基础
  10. 浏览器标准模式和怪异模式之间的区别是什么?