二维码框架Zxing的使用及自定义
项目需要一个二维码扫描功能
关于二维码的扫描,我之前就听说过ZXing了(虽然从来没用过)
所以既然这次要用它了,自然是得搜索一番资料咯。
在开始介绍之前,我先说一下我的使用情况吧,网上的博客里面基本上都把Zxing框架自己封装了,或者在教程中需要添加library然后修改源文件。否则你就无法自定义ZXing,并且你还会遇到某些问题——比如二维码扫描图片是横屏的。
幸运的是这些问题我都遇到了!!!
还好,我花了几个小时,终于是解决了上面这些问题,不用更改源码,也是可以对二维码扫描页面自定义的。
首先,贴上ZXing的android端GitHub地址:
好吧,接下来就开始介绍如何使用了。
一、ZXing框架的集成
在你的项目的根目录下的build.gradle中需要配置这个:
repositories {
jcenter()
}
然后是app目录下的build.gradle,配置如下:
dependencies {
compile('com.journeyapps:zxing-android-embedded:3.6.0') { transitive = false }
compile 'com.android.support:appcompat-v7:25.3.1' //如果V7已经配置过,那就不要重复添加了
compile 'com.google.zxing:core:3.3.0'
}
下面在AndroidManifest.xml中添加,表示开启硬件加速,其实没必要,因为一般硬件加速都是默认开启的
<application android:hardwareAccelerated="true" ... >
二、ZXing框架的使用
配置完成后就可以使用了。
使用的方式非常简单,在github的说明文档中,仅仅需要一行代码就可以打开二维码的扫描界面了:
new IntentIntegrator(this).initiateScan();
可以在一个Button的点击事件中添加这行代码,然后你只需要在当前的Activity中重写onActivityResult去获取结果就ok了。
不过。。。
如果你以为事情真的那么简单那你就大错特错了,下面我们看一下扫码的页面:
看完以上页面你会冒出两个问题:
1.要如何去自定义扫码界面?
2.怎么把界面竖过来?
好了,接下来就着手去准备解决这两个问题!
1、自定义扫码界面
在ZXing的github说明文档有提到过如何去自定义界面:
官方的建议是看例子,然后看源码。
下面是介绍如何去自定义扫描界面的文档地址:
之前有提到过ZXing配置好后一行代码就可以开启二维码扫描页面,在知道如何自定义界面之前我们可以了解一下这一行代码是怎么打开扫描页面的。
new IntentIntegrator(this).initiateScan();
上面的代码中,this就是当前Activity的引用,而IntentIntegrator类中的initiateScan()方法其实就是调用了startActivityForResult()方法:
public final void initiateScan() {
startActivityForResult(createScanIntent(), requestCode);
}
上面方法中的createScanIntent()返回一个从当前Activity跳转到另外一个Activity的Intent
public Intent createScanIntent() {
Intent intentScan = new Intent(activity, getCaptureActivity());
...
return intentScan;
}
而要跳转的Activity也就是二维码的扫描界面了,它实际上就是CaptureActivity:
public Class<?> getCaptureActivity() {
if (captureActivity == null) {
captureActivity = getDefaultCaptureActivity();
}
return captureActivity;
}
protected Class<?> getDefaultCaptureActivity() {
return CaptureActivity.class;
}
所以我们想自定义二维码扫描界面,就需要从CaptureActivity入手,接下来就是自定义界面的重点了!
CaptureActivity的代码非常少,连一百行都不到,之前看过的许多博客都是写一个类继承CaptureActivity,但其实这样也不够方便,我的做法是直接创建一个Activity,将CaptureActivity中的代码复制过来,然后稍作改动即可,接下来我创建一个活动ScanQRCodeActivity:
public class ScanQRCodeActivity extends AppCompatActivity {
private CaptureManager capture;
private DecoratedBarcodeView bv_barcode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan_qrcode);
bv_barcode = (DecoratedBarcodeView) findViewById(R.id.bv_barcode);
capture = new CaptureManager(this, bv_barcode);
capture.initializeFromIntent(getIntent(), savedInstanceState);
capture.decode();
}
@Override
protected void onResume() {
super.onResume();
capture.onResume();
}
@Override
protected void onPause() {
super.onPause();
capture.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
capture.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
capture.onSaveInstanceState(outState);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
capture.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return bv_barcode.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
}
}
上面的代码与CaptureActivity相差无几,只不过把布局界面替换了一下,下面是布局界面R.layout.activity_scan_qrcode:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Activity.ScanQRCodeActivity">
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:id="@+id/bv_barcode"
app:zxing_use_texture_view="false"
app:zxing_preview_scaling_strategy="centerCrop"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.constraint.ConstraintLayout>
在这个活动的布局界面中,我们只添加了一个DecoratedBarcodeView,它就是扫描界面的核心,之后如果想自定义布局,从这个活动的layout入手即可。
然后,我们就可以用我们自己创建的这个活动去代替CaptureActivity了,开心的是启动二维码扫描界面依旧是一行,不过现在它变成这样了:
new IntentIntegrator(this).setCaptureActivity(ScanQRCodeActivity.class).initiateScan();
上面的方法中多了一个setCaptureActivity()方法,这个方法看源码非常简单了,调用这个方法可以把默认的CaptureActivity替换为我们自定义的ScanQRCodeActivity。
接下来你只需要重写当前活动的onActivityResult()方法,即可正常使用扫码工具了,具体用法就是:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if(result != null) {
if(result.getContents() == null) {
Log.d(getClass().getName(), "Cancelled");
Toast.makeText(this, "扫描结果为空", Toast.LENGTH_LONG).show();
} else {
Log.d(getClass().getName(), "Scanned: " + result.getContents());
Toast.makeText(this, result.getContents(), Toast.LENGTH_LONG).show();
}
}
}
这样,自定义问题就解决了,接下来就是如何做到竖屏扫描
2、竖屏显示扫码界面
这个问题的解决方案其实在github的说明文档中也有,不过存在一点小问题,下面是完善做法:
由于之前我们自定义了一个活动ScanQRCodeActivity,将其在AndroidManifest中配置改为:
<activity android:name=".Activity.ScanQRCodeActivity"
android:screenOrientation="fullSensor"
xmlns:tools="http://schemas.android.com/tools"
tools:replace="screenOrientation"/>
接下来看一下效果即可:
大功告成!
接下来,就可以使用Zxing扫码了!
But。。。
实际使用的时候需求不会是这么简单的,下面有两个主要的需要,看一下如何不更改源码去实现它们:
需求一:扫码结束后依旧停留在当前页面
从上面的情况来看,扫码返回的结果是在上一个Activity中获得的,所以每次扫码结束后就会退出扫码界面,那么,如何解决这个问题呢?
其实很简单
我们已经知道了,实现扫码的View是DecoratedBarcodeView,在这个视图的方法中,有一个回调方法——BarcodeCallback
重点来啦,BarcodeCallback回调方法其实就是获取扫描结果的核心,只需要给DecoratedBarcodeView设置该回调就可以在当前Activity获取扫描结果:
private BarcodeCallback barcodeCallback = new BarcodeCallback() {
@Override
public void barcodeResult(BarcodeResult result) {
if (result != null){
Log.e(getClass().getName(), "获取到的扫描结果是:" + result.getText());
}
}
@Override
public void possibleResultPoints(List<ResultPoint> resultPoints) {
}
};
设置回调:
bv_barcode.decodeSingle(barcodeCallback);
上面的decodeSingle()方法表示只扫描一次,也就是获取到结果后就不再进行扫描。
到这里,第二个需求就来了
需求二:在当前页面进行多次扫描
DecoratedBarcodeView的设置回调方法里共有两种方法去设置回调,分别是:
bv_barcode.decodeSingle(barcodeCallback);
bv_barcode.decodeContinuous(barcodeCallback);
其实想要连续扫描,使用第二个方法即可,不过万万没想到,如果你使用了decodeContinuous()去设置回调的话,那你扫描成功的一瞬间,你会获得重复N次的扫描结果,显然,这种体验是蛋疼的,接下来又是亮点,要怎么去处理这个问题呢?
解决问题的方法依旧很简单,你只需要注意到我之前在Activity生命周期中有写到的这两个方法:
bv_barcode.resume();
bv_barcode.pause();
看到这里,想必你已经知道接下来该怎么做了
没错,在获取扫码结果回调的方法中调用:
bv_barcode.pause();
然后再你处理结果后再调用:
bv_barcode.resume();
比如我这样写:
private BarcodeCallback barcodeCallback = new BarcodeCallback() {
@Override
public void barcodeResult(BarcodeResult result) {
bv_barcode.pause();
if (result != null){
Log.e(getClass().getName(), "获取到的扫描结果是:" + result.getText());
//可以对result进行一些判断,比如说扫描结果不符合就再进行一次扫描
if (result.getText().contains("符合我的结果")){
//符合的可以不在扫描了,当然你想继续扫描也是可以的
} else {
bv_barcode.resume();
}
}
}
@Override
public void possibleResultPoints(List<ResultPoint> resultPoints) {
}
};
接下来看一看最终简易版的实现代码吧:
public class MainActivity extends AppCompatActivity{
private DecoratedBarcodeView bv_barcode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bv_barcode = findViewById(R.id.bv_barcode);
bv_barcode.decodeContinuous(barcodeCallback);
}
@Override
protected void onResume() {
super.onResume();
bv_barcode.resume();
}
@Override
protected void onPause() {
super.onPause();
bv_barcode.pause();
}
private BarcodeCallback barcodeCallback = new BarcodeCallback() {
@Override
public void barcodeResult(BarcodeResult result) {
bv_barcode.pause();
if (result != null){
Log.e(getClass().getName(), "获取到的扫描结果是:" + result.getText());
//可以对result进行一些判断,比如说扫描结果不符合就再进行一次扫描
if (result.getText().contains("符合我的结果")){
//符合的可以不在扫描了,当然你想继续扫描也是可以的
} else {
bv_barcode.resume();
}
}
}
@Override
public void possibleResultPoints(List<ResultPoint> resultPoints) {
}
};
}
好的,不修改源码实现自定义Zxing二维码扫描的学习就到此结束了,有什么问题请留言交流。
推荐阅读
-
php生成二维码的几种方式整理及使用实例_PHP
-
php生成二维码的几种方式整理及使用实例_php技巧
-
在C#及.NET框架中使用StringBuilder类操作字符串的技巧
-
PHP的Laravel框架中使用消息队列queue及异步队列的方法
-
ABP框架中导航菜单的使用及JavaScript API获取菜单的方法
-
小议Python中自定义函数的可变参数的使用及注意点
-
iOS App开发中使用及自定义UITableViewCell的教程
-
在C#及.NET框架中使用StringBuilder类操作字符串的技巧
-
ABP框架中导航菜单的使用及JavaScript API获取菜单的方法
-
小议Python中自定义函数的可变参数的使用及注意点