Android 实现二维码扫描

您所在的位置:网站首页 二维码扫一扫一键报警怎么设置声音 Android 实现二维码扫描

Android 实现二维码扫描

2024-07-13 15:21:56| 来源: 网络整理| 查看: 265

这个是封装后的。 简单调用可查看下面这文章。本文后半部为:自定义扫描页面

https://www.jianshu.com/p/de972f5652db

 

Android中二维码扫描的最常用库是zxing和zbar,zxing项目地址为https://github.com/zxing/zxing,目前还有多个人在维护。zbar主要用C来写的,对速度有要求的可使用zbar,但目前没有在维护,项目地址:https://github.com/ZBar/ZBar。

zxing自带闪光灯控制方法

decoratedBarcodeView.setTorchOn(); decoratedBarcodeView.setTorchOff();

这个是原始方法,了解一下,有需要再深入学习

https://www.jianshu.com/p/b85812b6f7c1

 

第三方ZXing库zxing-android-embedded使用及自定义扫描页面 一、关于ZXing

现在一维码二维码在我们的日常生活中使用如此的广泛,所以拥有扫码功能的APP变得非常普遍,一个安卓APP需要扫码功能就要用到zxing了,zxing是谷歌开源的让开发者更方便使用摄像头的库,而我们常用的扫码功能就是其中之一。

github地址:https://github.com/zxing/zxing

但是因为zxing的功能太强大了,包含了很多我们用不上的功能,所以一般都会抽取其中的扫码功能单独使用,这个抽取的过程还是有点麻烦的,但是已经有很多开发者为我们省去了这个过程,现在就来介绍一个很棒的第三方zxing库:zxing-android-embedded

二、第三方zxing库zxing-android-embedded

github地址:https://github.com/journeyapps/zxing-android-embedded

使用方式也很简单,首先在gradle中添加依赖

compile 'com.journeyapps:zxing-android-embedded:3.5.0'

然后在Activity或fragment中调用即可:

new IntentIntegrator(this) .setDesiredBarcodeFormats(IntentIntegrator.ONE_D_CODE_TYPES)// 扫码的类型,可选:一维码,二维码,一/二维码 .setPrompt("请对准二维码")// 设置提示语 .setCameraId(0)// 选择摄像头,可使用前置或者后置 .setBeepEnabled(false)// 是否开启声音,扫完码之后会"哔"的一声 .setBarcodeImageEnabled(true)// 扫完码之后生成二维码的图片 .initiateScan();// 初始化扫码

扫完码之后会在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) { Toast.makeText(this, "Cancelled", Toast.LENGTH_LONG).show(); } else { Toast.makeText(this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show(); } } else { super.onActivityResult(requestCode, resultCode, data); } }

以上就是简单的使用方式了

三、自定义界面

我们开发的时候可能会想要自定义界面,那么就需要我们修改代码了,自定义界面可以实现的: 1. 默认的的扫码是横屏的,自定义后可以竖屏扫码 2. 修改扫码框布局,可以添加其他View

其实在设置IntentIntegrator时会有一个方法:setCaptureActivity(Activity),这个方法就是用来设置扫码界面的Activity,当不设置的时候会默认调用库作者自己写的CaptureActivity,我们可以一起看一下这个Activity里面做了什么:

public class CaptureActivity extends Activity { private CaptureManager capture; private DecoratedBarcodeView barcodeScannerView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); barcodeScannerView = initializeContent(); capture = new CaptureManager(this, barcodeScannerView); capture.initializeFromIntent(getIntent(), savedInstanceState); capture.decode(); } ...省略代码 }

这里有2个很重要的成员变量:CaptureManager和DecoratedBarcodeView,从他们的名字可以看出: 1. CaptureManager是用来拉起扫码和处理扫码结果的类 2. DecoratedBarcodeView则是一个显示扫码界面的自定义View

有了这个了解之后我们要自定义扫码界面就很明了了,再一起简单看一下DecoratedBarcodeView是个啥:

public class DecoratedBarcodeView extends FrameLayout { private BarcodeView barcodeView; private ViewfinderView viewFinder; private TextView statusView; ...省略代码 } BarcodeView就是背景ViewfinderView就是扫描框TextView为下方提示文字

上个图:这里写图片描述

了解了这三个View的作用之后我们就可以开始我们的自定义了,而这三个View具体怎么去扫码与解析并不是我们关心的重点我们直接跳过。自定义界面的步骤:

新建一个Activity把CaptureManager和DecoratedBarcodeView复制到我们自定义的Activity中设置setCaptureActivity(CustomCaptureActivity.class)为我们自己的Activity别忘了把自定义的Activity加入到AndroidManifest.xml中注册

上代码,我自定义的activity:

public class CustomCaptureActivity extends Activity { private CaptureManager capture; private DecoratedBarcodeView barcodeScannerView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_custom_capture);// 自定义布局 barcodeScannerView = (DecoratedBarcodeView) findViewById(R.id.dbv_custom); capture = new CaptureManager(this, barcodeScannerView); 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 barcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); } }

自定义的布局:

调用方式:

new IntentIntegrator(this) // 自定义Activity,重点是这行---------------------------- .setCaptureActivity(CustomCaptureActivity.class) .setDesiredBarcodeFormats(IntentIntegrator.ONE_D_CODE_TYPES)// 扫码的类型,可选:一维码,二维码,一/二维码 .setPrompt("请对准二维码")// 设置提示语 .setCameraId(0)// 选择摄像头,可使用前置或者后置 .setBeepEnabled(false)// 是否开启声音,扫完码之后会"哔"的一声 .setBarcodeImageEnabled(true)// 扫完码之后生成二维码的图片 .initiateScan();// 初始化扫码

这是我修改过后的界面:这里写图片描述

大家可以根据自己的需要定制自己想要的界面。

四、扫码后不结束扫码界面Activity

我们在使用zxing-android-embedded时会发现一点,那就是每次扫完码之后都会结束掉扫码界面Activity,然后在上一个Activity的onActivityResult获取扫码的结果,一般情况下这样的操作是可以满足我们项目中的需求,但是作者最近在开发的时候遇到了一个需求,需要在扫完码之后在不退出Activity的情况下弹出一个Dialog,然后在Dialog消失之后再扫码。找了一圈发现zxing-android-embedded没有提供这样的方法可以满足扫码后不结束扫码Activity。没办法逼得我去查阅源码看能否从源码下手,这里总结一下需求: 1. 首先扫码结束后不能结束扫码Activity 2. 在扫码Activity中能获取到扫码的结果 3. 获取到结果之后弹出Dialog,此时扫码应该是处于暂停状态,否则会出现未知问题 4. Dialog消失之后应该重新激活扫码

有了以上的需求,我们就从调用扫码的地方开始看起:

new IntentIntegrator(this) .setCaptureActivity(CustomCaptureActivity.class)// 自定义Activity .setDesiredBarcodeFormats(IntentIntegrator.ONE_D_CODE_TYPES)// 扫码的类型,可选:一维码,二维码,一/二维码 .setPrompt("请对准二维码")// 设置提示语 .setCameraId(0)// 选择摄像头,可使用前置或者后置 .setBeepEnabled(false)// 是否开启声音,扫完码之后会"哔"的一声 .setBarcodeImageEnabled(true)// 扫完码之后生成二维码的图片 .initiateScan();// 初始化扫码

构造方法中把当前的Activity设置到IntentIntegrator中,作者demo中对应的是MainActivity

public IntentIntegrator(Activity activity) { this.activity = activity; }

然后关键的第二行,设置扫码Activity

public IntentIntegrator setCaptureActivity(Class captureActivity) { this.captureActivity = captureActivity; return this; }

从第三行开始就是对一些属性的设置了,这些不是我们关心的重点,所以我们跳过。说了这么久还没有提到过IntentIntegrator这个类,这个类其实是我们调用扫码的一个入口,各种参数都是在这个类中设置,还记得我们的需求是扫完码之后不结束扫码的Activity码?我们把重心放到captureActivity上,但是结果却是让人失落的,这个类中并没有captureActivity相关的操作,不过在initiateScan()方法中我们可以找到一些蛛丝马迹。

public final void initiateScan() { startActivityForResult(createScanIntent(), REQUEST_CODE); }

这里只有简单的一行代码,让我们先看一下createScanIntent()中做了什么:

public Intent createScanIntent() { Intent intentScan = new Intent(activity, getCaptureActivity()); intentScan.setAction(Intents.Scan.ACTION); // check which types of codes to scan for if (desiredBarcodeFormats != null) { // set the desired barcode types StringBuilder joinedByComma = new StringBuilder(); for (String format : desiredBarcodeFormats) { if (joinedByComma.length() > 0) { joinedByComma.append(','); } joinedByComma.append(format); } intentScan.putExtra(Intents.Scan.FORMATS, joinedByComma.toString()); } intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); attachMoreExtras(intentScan); return intentScan; }

这段代码我们只需要关心第一行即可,从第一行我们可以知道这个方法会返回一个Intent,这个Intent就是从activity跳转到我们自定义的captureActivity,并且携带了很多设置的参数,让我返回到startActivityForResult()中:

protected void startActivityForResult(Intent intent, int code) { if (fragment != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { fragment.startActivityForResult(intent, code); } } else if (supportFragment != null) { supportFragment.startActivityForResult(intent, code); } else { activity.startActivityForResult(intent, code); } }

因为我们的扫码有可能是从fragment中调用的,所以框架的作者在这里分别做了对fragment和activity中调用扫码的处理,这里我们关心activity即可,很可惜,IntentIntegrator中没有我们能下手的地方,但是我们了解到扫码的操作其实都是在captureActivity做的,让我们把重点放到captureActivity中。

其实在第三节中我们我们已经看过这个Activity了,但是我们当时只关心自定义界面,所以忽略了一个重要的类:CaptureManager,这个类其实就是真正处理扫码的地方,所有的设置、界面、Activity的调用都会在CaptureManager完成,我们先看一下他的使用:

capture = new CaptureManager(this, barcodeScannerView); capture.initializeFromIntent(getIntent(), savedInstanceState); capture.decode();

构造方法中会把当前扫码Activity和扫码的View设置进去。

public CaptureManager(Activity activity, DecoratedBarcodeView barcodeView) { this.activity = activity; this.barcodeView = barcodeView; barcodeView.getBarcodeView().addStateListener(stateListener); handler = new Handler(); inactivityTimer = new InactivityTimer(activity, new Runnable() { @Override public void run() { Log.d(TAG, "Finishing due to inactivity"); finish(); } }); beepManager = new BeepManager(activity); }

而initializeFromIntent()方法主要是从Intent中取出我们所设置的各种扫码的参数,这里就不给出源码了。

/** * Start decoding. */ public void decode() { barcodeView.decodeSingle(callback); }

最后的decode()方法就是真正开始扫码的地方,我们可以看到这里barcodeView调用了decodeSingle方法开启扫码,还设置了一个回调进去,可以猜到这个callback就是用来接收扫码结果的回调了

private BarcodeCallback callback = new BarcodeCallback() { @Override public void barcodeResult(final BarcodeResult result) { barcodeView.pause(); beepManager.playBeepSoundAndVibrate(); handler.post(new Runnable() { @Override public void run() { returnResult(result); } }); } @Override public void possibleResultPoints(List resultPoints) { } };

在这个回调中看到一个returnResult(result),这个就是扫码之后返回结果的地方了,我们果然没有猜错

protected void returnResult(BarcodeResult rawResult) { Intent intent = resultIntent(rawResult, getBarcodeImagePath(rawResult)); activity.setResult(Activity.RESULT_OK, intent); closeAndFinish(); }

BarcodeResult就是我们的扫码结果了,第一行代码用来解析BarcodeResult并生成一个Intent,这个Intent就是用来携带扫码结果返回到onActivityResult的关键了,第三行代码的名字很明显就是用来结束Activity的方法了吧,跟进去看:

protected void closeAndFinish() { if(barcodeView.getBarcodeView().isCameraClosed()) { finish(); } else { finishWhenClosed = true; } barcodeView.pause(); inactivityTimer.cancel(); } private void finish() { activity.finish(); }

罪魁祸首就是这个finish()方法了!!就是它,它把我们的Activity结束掉了,找到源头了就可以下手了。我们把它给注释掉就结束不了我们的Activity了。但是现在Activity不结束了,但是我们要把结果给返回回去啊,那我们自己得加个回调了

public interface ResultCallBack { void callBack(int requestCode, int resultCode, Intent intent) } private ResultCallBack mResultCallBack; public void setResultCallBack(ResultCallBack resultCallBack) { this.mResultCallBack = resultCallBack; }

修改后的returnResult方法为下,这里我修改的是returnResult方法,而并没有修改上面的closeAndFinish方法,因为closeAndFinish方法不只是在返回扫码结构的时候调用了,所以为了防止出问题就修改是returnResult方法

/** * 修改此方法,实现扫码之后不返回界面 */ protected void returnResult(BarcodeResult rawResult) { Intent intent = resultIntent(rawResult, getBarcodeImagePath(rawResult)); activity.setResult(Activity.RESULT_OK, intent); if (barcodeView.getBarcodeView().isCameraClosed()) { if (null != mResultCallBack) { mResultCallBack.callBack(IntentIntegrator.REQUEST_CODE, Activity.RESULT_OK, intent); } // activity.finish(); 注释掉这行 } else { finishWhenClosed = true; } barcodeView.pause(); inactivityTimer.cancel(); }

到这里为止我们的修改就完了,其实改动非常小,就几行代码就可以实现扫完码之后不结束扫码Activity,但是重要的是思考的过程和阅读源码的过程,刚拿到那些需求然后发现第三方库实现不了的时候我也是非常慌的,也尝试过修改源码但是没有成功,最后一次静下心来慢慢翻看源码终于搞定了,这也是对我自己的一次提升吧,谢谢大家阅读文章,最后附上修改后的代码:

修改后的CustomCaptureActivity:

public class CustomCaptureActivity extends Activity { private CaptureManager capture; private DecoratedBarcodeView barcodeScannerView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_custom_capture);// 自定义布局 barcodeScannerView = (DecoratedBarcodeView) findViewById(R.id.dbv_custom); capture = new CaptureManager(this, barcodeScannerView); capture.initializeFromIntent(getIntent(), savedInstanceState); capture.setResultCallBack(new CaptureManager.ResultCallBack() { @Override public void callBack(int requestCode, int resultCode, Intent intent) { IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent); if (null != result && null != result.getContents()) { showDialog(result.getContents()); } } }); capture.decode(); } public void showDialog(String result) { // 弹出dialog的代码略... // 重新拉起扫描 capture.onResume(); 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 barcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); } }

修改后的CaptureManager:

public class CaptureManager{ ... 省略代码 /** * 修改此方法,实现扫码之后不返回界面 * * @author mark.liu * created at 2017-9-5 */ protected void returnResult(BarcodeResult rawResult) { Intent intent = resultIntent(rawResult, getBarcodeImagePath(rawResult)); activity.setResult(Activity.RESULT_OK, intent); if (barcodeView.getBarcodeView().isCameraClosed()) { if (null != mResultCallBack) { mResultCallBack.callBack(IntentIntegrator.REQUEST_CODE, Activity.RESULT_OK, intent); } // activity.finish(); 注释这一行 } else { finishWhenClosed = true; } barcodeView.pause(); inactivityTimer.cancel(); } public interface ResultCallBack { void callBack(int requestCode, int resultCode, Intent intent); } private ResultCallBack mResultCallBack; public void setResultCallBack(ResultCallBack resultCallBack) { this.mResultCallBack = resultCallBack; } ... 省略代码 }


【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭