Android实现基于ZXing快速集成二维码扫描功能
二维码扫描现在是一直比较多的应用场景,android的开源项目ZXing提供了完整、成熟的解决方案,如果仅仅是出于快速开发的目的,可以根据自己的项目需要,把ZXing官方提供的项目稍加裁剪,就可以快速的集成到自己的项目中。下面详细演示和介绍如何实现基于ZXing官方提供的源码,快速集成二维码扫描功能到自己项目中的解决方案。
(第1步):到ZXing官方主页下载最新的项目代码包,ZXing在github的官方主页:https://github.com/zxing,下载后解压。解压后根目录下有若干项目目录,其中的:android就是我们需要的项目,把它导入到Eclispse中。
(第2步):ZXing的Android项目需要引用两个关键的库文件:android-core-x.x.x.jar和 core-x.x.x.jar,其中x.x.x表示版本号。截止发表本博文时候,版本已经是3.2.0了。这两个关键的android-core-x.x.x.jar 和 core-x.x.x.jar 文件,实际上都可以从第一步下载得到的源代码中自己编译生成,网上也有编译的具体方案,但简单期间,也可以从ZXing的官方直接下载已经编译好的文件,其中android-core的下载链接是:http://repo1.maven.org/maven2/com/google/zxing/android-core/,另外一个ZXing的core下载链接是:http://repo1.maven.org/maven2/com/google/zxing/core/ ,选择最新版本的库文件或者自己需要的版本号,下载后,和其他android项目中导入库文件类似,导入到Android项目中的libs目录下,如果没有libs,新建一个名为libs的目录,把两个库文件放进去即可。
(第3步):这一步作为演示,我们自己新建一个MainActiviy,作为项目的启动器Activity,App将启动我们自己的MainActivity。观察ZXing官方提供的AndroidManifest.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <!-- Copyright (C) 2008 ZXing authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.google.zxing.client.android" android:versionName="4.7.3" android:versionCode="103" android:installLocation="auto"> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.FLASHLIGHT"/> <uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="21"/> <!-- Don't require camera, as this requires a rear camera. This allows it to work on the Nexus 7 --> <uses-feature android:name="android.hardware.camera" android:required="false"/> <uses-feature android:name="android.hardware.camera.front" android:required="false"/> <!-- TODO replace above two with next line after Android 4.2 --> <!-- <uses-feature android:name="android.hardware.camera.any"/> --> <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/> <uses-feature android:name="android.hardware.camera.flash" android:required="false"/> <uses-feature android:name="android.hardware.screen.landscape"/> <uses-feature android:name="android.hardware.wifi" android:required="false"/> <!-- This excludes Google TV, which is unfortunately included by virtue of not requiring a camera --> <uses-feature android:name="android.hardware.touchscreen"/> <!-- TODO make this not required again after android.hardware.camera.any is available --> <supports-screens android:xlargeScreens="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true" android:anyDensity="true"/> <application android:icon="@drawable/launcher_icon" android:logo="@drawable/launcher_icon" android:label="@string/app_name" android:allowBackup="true"> <activity android:name=".CaptureActivity" android:screenOrientation="sensorLandscape" android:clearTaskOnLaunch="true" android:stateNotNeeded="true" android:theme="@style/CaptureTheme" android:windowSoftInputMode="stateAlwaysHidden"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> <intent-filter> <action android:name="com.google.zxing.client.android.SCAN"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> <!-- Allow web apps to launch Barcode Scanner by linking to http://zxing.appspot.com/scan. --> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/> <data android:scheme="http" android:host="zxing.appspot.com" android:path="/scan"/> </intent-filter> <!-- We also support a Google Product Search URL. --> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/> <data android:scheme="http" android:host="www.google.com" android:path="/m/products/scan"/> </intent-filter> <!-- And the UK version. --> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/> <data android:scheme="http" android:host="www.google.co.uk" android:path="/m/products/scan"/> </intent-filter> <!-- Support zxing://scan/?... like iPhone app --> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/> <data android:scheme="zxing" android:host="scan" android:path="/"/> </intent-filter> </activity> <activity android:name=".PreferencesActivity" android:label="@string/preferences_name" android:stateNotNeeded="true"/> <activity android:name=".encode.EncodeActivity" android:stateNotNeeded="true"> <intent-filter> <action android:name="com.google.zxing.client.android.ENCODE"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> <!-- This allows us to handle the Share button in Contacts. --> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/x-vcard"/> </intent-filter> <!-- This allows us to handle sharing any plain text . --> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> </intent-filter> </activity> <activity android:name=".book.SearchBookContentsActivity" android:label="@string/sbc_name" android:stateNotNeeded="true" android:screenOrientation="sensorLandscape"> <intent-filter> <action android:name="com.google.zxing.client.android.SEARCH_BOOK_CONTENTS"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> <activity android:name=".share.ShareActivity" android:stateNotNeeded="true" android:screenOrientation="user"> <intent-filter> <action android:name="com.google.zxing.client.android.SHARE"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> <activity android:name=".history.HistoryActivity" android:label="@string/history_title" android:stateNotNeeded="true"/> <activity android:name=".share.BookmarkPickerActivity" android:label="@string/bookmark_picker_name" android:stateNotNeeded="true"/> <activity android:name=".share.AppPickerActivity" android:label="@string/app_picker_name" android:stateNotNeeded="true"/> <activity android:name=".HelpActivity" android:label="@string/menu_help" android:screenOrientation="user" android:stateNotNeeded="true"/> </application> </manifest>
其实ZXing官方的项目已经作为为第三方提供集成的代码了,比如其中的关键Activity:.\src\com\google\zxing\client\android\CaptureActivity.Java,在声明中已经提供好了从各种入口访问的Intent的Action。所以在我们自己新建的MainActivity中,直接隐式指定一个Intent的Action,启动之即可:
package com.google.zxing.client.android; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.widget.Toast; public class MainActivity extends Activity { private final int REQUEST_CODE = 0xa1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = new Intent(); //隐式指定 intent.setAction("com.google.zxing.client.android.SCAN"); //启动ZXing已经写好、且我们做小量修改后的CaptureActivity。 startActivityForResult(intent, REQUEST_CODE); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); //我们需要的结果返回 if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) { //result就是二维码扫描的结果。 String result = data.getStringExtra("RESULT"); Toast.makeText(getApplicationContext(), result, Toast.LENGTH_SHORT) .show(); } } }
因为我们启动ZXing的CaptureActivity不是目的,真正的目的是启动ZXing的CaptureActivity获得二维码扫描结果,因此以startActivityForResult()的方式启动。相应的,我们需要重写:protected void onActivityResult(int requestCode, int resultCode, Intent data),以回调等待传回结果。
(第4步):这一步是重点。在.\src\com\google\zxing\client\android\目录下的CaptureActivity.java中的方法: public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor);此方法是一个回调函数。ZXing项目中写好的扫描模块扫描后返回回调此方法,ZXing官方的原始 public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor)方法是这样的:
/** * A valid barcode has been found, so give an indication of success and show the results. * * @param rawResult The contents of the barcode. * @param scaleFactor amount by which thumbnail was scaled * @param barcode A greyscale bitmap of the camera data which was decoded. */ public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) { inactivityTimer.onActivity(); lastResult = rawResult; ResultHandler resultHandler = ResultHandlerFactory.makeResultHandler(this, rawResult); boolean fromLiveScan = barcode != null; if (fromLiveScan) { historyManager.addHistoryItem(rawResult, resultHandler); // Then not from history, so beep/vibrate and we have an image to draw on beepManager.playBeepSoundAndVibrate(); drawResultPoints(barcode, scaleFactor, rawResult); } switch (source) { case NATIVE_APP_INTENT: case PRODUCT_SEARCH_LINK: handleDecodeExternally(rawResult, resultHandler, barcode); break; case ZXING_LINK: if (scanFromWebPageManager == null || !scanFromWebPageManager.isScanFromWebPage()) { handleDecodeInternally(rawResult, resultHandler, barcode); } else { handleDecodeExternally(rawResult, resultHandler, barcode); } break; case NONE: SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); if (fromLiveScan && prefs.getBoolean(PreferencesActivity.KEY_BULK_MODE, false)) { Toast.makeText(getApplicationContext(), getResources().getString(R.string.msg_bulk_mode_scanned) + " (" + rawResult.getText() + ')', Toast.LENGTH_SHORT).show(); // Wait a moment or else it will scan the same barcode continuously about 3 times restartPreviewAfterDelay(BULK_MODE_SCAN_DELAY_MS); } else { handleDecodeInternally(rawResult, resultHandler, barcode); } break; } }
我们将精简此方法,定制自己所需要的内容,为满足我们自己项目中的需求,把此方法修改后的代码为:
public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) { inactivityTimer.onActivity(); lastResult = rawResult; ResultHandler resultHandler = ResultHandlerFactory.makeResultHandler(this, rawResult); boolean fromLiveScan = barcode != null; if (fromLiveScan) { historyManager.addHistoryItem(rawResult, resultHandler); // Then not from history, so beep/vibrate and we have an image to draw on beepManager.playBeepSoundAndVibrate(); drawResultPoints(barcode, scaleFactor, rawResult); } //在这里增加我们的代码,目的是:做最小量的修改,仅仅把ZXing提供的CaptureActivity作为一个中间使用的Activity集成到我们自己的项目。 //启动实现二维码扫描,返回一个结果就可以了。 //然后结束这个Activity。 Intent intent=new Intent(); //<key,value>形式存储二维码结果。 //rawResult.getText()即为二维码结果。 intent.putExtra("RESULT", rawResult.getText()); this.setResult(Activity.RESULT_OK, intent); this.finish(); /** 以下是ZXing提供的源码,根据项目需要可以删减使用。 简单期间,我们只需要二维码扫描后返回一个扫描的字符串结果。 所以在次暂时注释掉。 switch (source) { case NATIVE_APP_INTENT: case PRODUCT_SEARCH_LINK: handleDecodeExternally(rawResult, resultHandler, barcode); break; case ZXING_LINK: if (scanFromWebPageManager == null || !scanFromWebPageManager.isScanFromWebPage()) { handleDecodeInternally(rawResult, resultHandler, barcode); } else { handleDecodeExternally(rawResult, resultHandler, barcode); } break; case NONE: SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); if (fromLiveScan && prefs.getBoolean(PreferencesActivity.KEY_BULK_MODE, false)) { Toast.makeText(getApplicationContext(), getResources().getString(R.string.msg_bulk_mode_scanned) + " (" + rawResult.getText() + ')', Toast.LENGTH_SHORT).show(); // Wait a moment or else it will scan the same barcode continuously about 3 times restartPreviewAfterDelay(BULK_MODE_SCAN_DELAY_MS); } else { handleDecodeInternally(rawResult, resultHandler, barcode); } break; } **/ }
(第5步):这一步比较简单,是剩余的收尾工作,修改AndroidManifest.xml文件,把我们的MainActivity作为主Activity启动。把ZXing的CaptureActivity作为一个普通的Activity。
修改后的AndroidManifest.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <!-- Copyright (C) 2008 ZXing authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.google.zxing.client.android" android:versionName="4.7.3" android:versionCode="103" android:installLocation="auto"> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.FLASHLIGHT"/> <uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="21"/> <!-- Don't require camera, as this requires a rear camera. This allows it to work on the Nexus 7 --> <uses-feature android:name="android.hardware.camera" android:required="false"/> <uses-feature android:name="android.hardware.camera.front" android:required="false"/> <!-- TODO replace above two with next line after Android 4.2 --> <!-- <uses-feature android:name="android.hardware.camera.any"/> --> <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/> <uses-feature android:name="android.hardware.camera.flash" android:required="false"/> <uses-feature android:name="android.hardware.screen.landscape"/> <uses-feature android:name="android.hardware.wifi" android:required="false"/> <!-- This excludes Google TV, which is unfortunately included by virtue of not requiring a camera --> <uses-feature android:name="android.hardware.touchscreen"/> <!-- TODO make this not required again after android.hardware.camera.any is available --> <supports-screens android:xlargeScreens="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true" android:anyDensity="true"/> <application android:icon="@drawable/launcher_icon" android:logo="@drawable/launcher_icon" android:label="@string/app_name" android:allowBackup="true"> <!-- 新增的我们自己的MainActiviy作为启动Activity --> <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".CaptureActivity" android:screenOrientation="sensorLandscape" android:clearTaskOnLaunch="true" android:stateNotNeeded="true" android:theme="@style/CaptureTheme" android:windowSoftInputMode="stateAlwaysHidden"> <!-- 把ZXing官方提供的作为 main activity启动的CaptureActivity作为一个普通Activiy。 <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> --> <intent-filter> <action android:name="com.google.zxing.client.android.SCAN"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> <!-- Allow web apps to launch Barcode Scanner by linking to http://zxing.appspot.com/scan. --> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/> <data android:scheme="http" android:host="zxing.appspot.com" android:path="/scan"/> </intent-filter> <!-- We also support a Google Product Search URL. --> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/> <data android:scheme="http" android:host="www.google.com" android:path="/m/products/scan"/> </intent-filter> <!-- And the UK version. --> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/> <data android:scheme="http" android:host="www.google.co.uk" android:path="/m/products/scan"/> </intent-filter> <!-- Support zxing://scan/?... like iPhone app --> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/> <data android:scheme="zxing" android:host="scan" android:path="/"/> </intent-filter> </activity> <activity android:name=".PreferencesActivity" android:label="@string/preferences_name" android:stateNotNeeded="true"/> <activity android:name=".encode.EncodeActivity" android:stateNotNeeded="true"> <intent-filter> <action android:name="com.google.zxing.client.android.ENCODE"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> <!-- This allows us to handle the Share button in Contacts. --> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/x-vcard"/> </intent-filter> <!-- This allows us to handle sharing any plain text . --> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> </intent-filter> </activity> <activity android:name=".book.SearchBookContentsActivity" android:label="@string/sbc_name" android:stateNotNeeded="true" android:screenOrientation="sensorLandscape"> <intent-filter> <action android:name="com.google.zxing.client.android.SEARCH_BOOK_CONTENTS"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> <activity android:name=".share.ShareActivity" android:stateNotNeeded="true" android:screenOrientation="user"> <intent-filter> <action android:name="com.google.zxing.client.android.SHARE"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> <activity android:name=".history.HistoryActivity" android:label="@string/history_title" android:stateNotNeeded="true"/> <activity android:name=".share.BookmarkPickerActivity" android:label="@string/bookmark_picker_name" android:stateNotNeeded="true"/> <activity android:name=".share.AppPickerActivity" android:label="@string/app_picker_name" android:stateNotNeeded="true"/> <activity android:name=".HelpActivity" android:label="@string/menu_help" android:screenOrientation="user" android:stateNotNeeded="true"/> </application> </manifest>
备注:此文仅仅是最简单的一个示例,演示了如何在自己的项目中在ZXing官方项目已提供的完整代码基础上,做最小量的改动,将二维码扫描功能快速集成到自己的项目中为我所用,若需要更多的细节调整,则需要深入定制和改编源代码。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
- 关于导入导出sequence(r4笔记第11天)
- Spring+SpringMVC+MyBatis整合进阶篇(四)RESTful实战(前端代码修改)
- Nodejs cluster模块深入探究
- org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression
- 巧用分析函数循序渐进解决实际问题 (r4笔记第10天)
- 支持多用户web终端实现及安全保障(nodejs)
- 你看到的最直白清晰的CNN讲解
- oracle中的数组(第一篇)(r4笔记第9天)
- org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method service()
- GITCHAT系列2:个性化推荐
- org.springframework.expression.spel.SpelEvaluationException: EL1011E: Method call: Attempted to call
- 【Keras】完整实现‘交通标志’分类、‘票据’分类两个项目,让你掌握深度学习图像分类
- PaddlePaddle发布新版API,简化深度学习编程
- 13(01)总结StringBuffer,StringBuilder,数组高级,Arrays,Integer,Character
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- TRTC Android端开发接入学习之视频会议(八)
- MySQL锁都分不清,怎么面试进大厂?
- Kubernetes Controller高可用诡异的15mins超时
- 这几项超好用的云开发扩展能力,别说你还不知道!
- Ubuntu上一键卸载安装mysql脚本
- Python-批量修改图片全部颜色,批量修改图片的指定颜色,马甲包一键换主题UI
- 【SpringBoot DB 系列】h2databse 集成示例 demo
- MySQL 案例:analyze,慢查询,与查询无响应
- 万字图解Java多线程
- 金九银十要来了?不要慌,这些Android BAT高级面试题刷一刷
- 【SpringBoot DB 系列】Jooq 初体验
- Android轻量级APM性能监测方案
- 保持 Go 模块兼容
- Go 模块:v2 及更高版本
- 发布 Go Modules