Android扫码之Zxing应用分析



一、引言

如今扫码功能已深入千家万户,特别是二维码的应用,如微信、支付宝等移动应用的普及。作为开发人员,当然会关注背后的技术实现,对于Android来说,选择一个好的二维码轮子,可以达到事半功倍的效果。目前比较常见的二维码扫描库就是了Zxing(现在基本上都是使用它来做项目),Zxing是Google官方的开源项目,有专门的维护,java编写。

二、Zxing介绍

Zxing是一个开放源码的,用java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的接口;Zxing可以实现使用手机的内置的摄像头完成条形码和二维码的扫描与解码;Zxing可以实现条形码和二维码的编码与解码。

Zxing目前支持的的格式如下:

1D product 1D industrial 2D
UPC-A Code 39 QR Code
UPC-E Code 93 Data Matrix
EAN-8 Code 128 Aztec (beta)
EAN-13 Codabar PDF 417 (beta)
ITF MaxiCode
RSS-14
RSS-Expanded -

GitHub:Zxing
GitHub:Zxing-android-embedded

总之Zxing 是个很厉害的库,支持各种平台等等,但有关 Android 的信息比较少,搜索了一下 Android Zxing,还有一个 Zxing-android-embedded 的仓库,这个库是一个基于 Zxing 的 Android 二维码解码库,使用起来还是很方便的。

三、Zxing使用

3.1 库的导入

Android上Zxing的导入有两种方式:

3.1.1 直接complie导入

将已经弄好Zxing的工程作为当前工程的依赖库,在需要使用的Project或Module的build.gradle直接complie或implementation导入:

    implementation 'com.google.zxing:core:3.3.3'

    implementation 'com.journeyapps:zxing-android-embedded:3.6.0'
3.1.2 以lib库导入

把Zxing项目clone下来。接着在一个项目里面选择file->new->import Module方式来导入Zxing项目作为自己项目的library。

因为ZXing的项目是非常庞大的,功能也非常多,但是我们不需要这么多,我们只关心Android部份,选择Zxing包下面的android包,这个android包也就是Zxing的sample;android-integration库和android-core库是辅助库,后期这两个可以不要;核心库是core,还是要的。

3.2 解码Decode

扫描二维码自然不能少的就是相机的调用了,在AndroidManifest文件里,需要有相关权限的声明,如相机,震动和闪光灯的权限。

    <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"/>

扫描的样式直接使用库里定义的:

    Intent openCameraIntent = new Intent(this,CaptureActivity.class);
    startActivityForResult(openCameraIntent, 0);


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == RESULT_OK) {
            Bundle bundle = data.getExtras();
            String scanResult = bundle.getString("result");
            resultTextView.setText(scanResult);
        }
    }

当然了,扫描的样式还可以自定义的:

    private void scanner(){
        IntentIntegrator integrator = new IntentIntegrator(this);
        integrator.initiateScan();
    }

    // 你也可以使用简单的扫描功能,但是一般扫描的样式和行为都是可以自定义的,这里就写关于自定义的代码了
    // 你可以把这个方法作为一个点击事件
    public void customScan(){
        new IntentIntegrator(this)
        .setOrientationLocked(false)
        .setCaptureActivity(CustomScanActivity.class) // 设置自定义的activity是CustomActivity
        .initiateScan(); // 初始化扫描
    }

    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
        if (scanResult != null) {
            // handle scan result
            Toast.makeText(this,scanResult.toString(),Toast.LENGTH_SHORT).show();
        }
        // else continue with any other code you need in the method
    }

3.3 编码Encode

传入一个字符串,生成一个二维码的Bitmap,可以用于显示:

            // 配置参数
            Map<EncodeHintType, Object> hints = new HashMap<>();
            hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
            // 容错级别 这里选择最高H级别
            hints.put(EncodeHintType.ERROR_CORRECTION, level);
            hints.put(EncodeHintType.MARGIN, margin);
            // 图像数据转换,使用了矩阵转换 参数顺序分别为:编码内容,编码类型,生成图片宽度,生成图片高度,设置参数
            BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, widthPix, heightPix, hints);
            int[] pixels = new int[widthPix * heightPix];
            // 下面这里按照二维码的算法,逐个生成二维码的图片,
            // 两个for循环是图片横列扫描的结果
            for (int y = 0; y < heightPix; y++) {
                for (int x = 0; x < widthPix; x++) {
                    if (bitMatrix.get(x, y)) {
                        pixels[y * widthPix + x] = 0xff000000; // 黑色
                    } else {
                        pixels[y * widthPix + x] = 0x00ffffff;// 白色
                    }
                }
            }
            // 生成二维码图片的格式,使用ARGB_8888
            Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888);
            bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix);
            return bitmap;

如果使用 Zxing Android Embedded 的话,代码如下:

        Bitmap bitmap = null;
        BitMatrix result = null;
        MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
        try {
            result = multiFormatWriter.encode(str, BarcodeFormat.QR_CODE, 200, 200);
            // 使用 Zxing Android Embedded 要写的代码
            BarcodeEncoder barcodeEncoder = new BarcodeEncoder();
            bitmap = barcodeEncoder.createBitmap(result);
        } catch (WriterException e){
            e.printStackTrace();
        } catch (IllegalArgumentException iae){
            return null;
        }

四、总结

Zxing功能太强大了,而我们通常只是简单的应用,若要更深入了解,需要看其源码(该库基本没有提供说明文档或API),得慢慢看慢慢琢磨,实在想不明白的地方,就别去纠结了,过段时间再去看当时迷惑的地方,可能就会想明白了。遇到错误,没什么,不可怕,看看提示的错误是什么,冷静分析问题,解决问题。


常见问题:

1、基于zxing的二维码扫码可能会出现扫码速率比较低的问题。

zxing源码是截取的扫码聚焦框里面的图像数据信息来解码,这里可以改成获取全屏的图像信息。实现代码如下:

public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
    return new PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height, false);
}

尽量减少支持的扫码类型。zxing源码默认是支持所有的扫码类型。我们项目中使用的话,一般不需要支持这么多。仅支持BarcodeFormat.QR_CODE(二维码)、BarcodeFormat.CODE_128(一维码)就可以应对很多场景了。

添加 hints.put(DecodeHintType.TRY_HARDER, true);语句,能够提高扫码精确度,准确率。