[AS2.3.3]Zxing二维码的学习使用

前言

Zxing二维码是谷歌官方提供的扫码方法。
百度查的很多第三方库都是很老的Zxing代码生成的扫码库。虽然都能用,但是也有很多扫码很慢或者方法过老的情况。没有更新新的版本!

本篇就是探究一下扫码库和Zxing库的使用


Zxing的使用

首先说下,其实Zxing的二维码编码解码的代码都写在了github项目的core文件夹内,其实我们只需要core内的代码就可以实现二维码的编码和解码了。

谷歌官方Zxing github地址

那么我们可以将Zxing的github下的core代码全部下载下来然后放到使用的项目中,或者在项目的build.gradle中加入

dependencies {
    compile 'com.google.zxing:core:3.3.2'
}

二维码的介绍

二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设备自动识读以实现信息自动处理:它具有条码技术的一些共性:每种码制有其特定的字符集;每个字符占有一定的宽度;具有一定的校验功能等。同时还具有对不同行的信息自动识别功能、及处理图形旋转变化点。

我们可以认为二维码就是一串二进制数组保存的数据,然后再生成以正方形绘制图形。

Zxing生成二维码参数说明

Zxing将生成图形编码的方式抽象成了一个类com.google.zxing.Writer, 在实现类中不仅仅生成二维码,还可以生成条形码等其他图形编码。

在Writer我们可以看到2个方法

BitMatrix encode(String contents, BarcodeFormat format, 
                    int width, int height) 
    throws WriterException;

BitMatrix encode(String contents, BarcodeFormat format,
                   int width, int height, 
                   Map<EncodeHintType,?> hints) 
    throws WriterException;

参数说明

参数 说明
String contents 编码内容
BarcodeFormat format 编码方式(如:二维码、条形码…)
int width 宽度
int height 高度
Map hints 编码参数设置

我们在看看编码参数设置有哪些

参数 说明
ERROR_CORRECTION 容错率,指定容错等级,对于QRCode类型来源com.google.zxing.qrcode.decoder.ErrorCorrectionLevel,对于Aztec类型为Integer等…
CHARACTER_SET 编码集,类型为String,可写”utf-8”等…
DATA_MATRIX_SHAPE 指定生成的数据矩阵的形状,类型为com.google.zxing.datamatrix.encoder.SymbolShapeHint
MIN_SIZE 指定最小的条码大小(已废弃),新方法设置在com.google.zxing.datamatrix.DataMatrixWriter#encode(String, BarcodeFormat, int, int)
MAX_SIZE 指定最大的条码大小(已废弃),同上
MARGIN 生成条码的时候使用,指定边距,单位像素,受格式的影响,类型IntegerString代表的数字类型
PDF417_COMPACT 指定是否使用PDF417紧凑模式,类型为boolean
PDF417_COMPACTION 指定PDF417的紧凑类型,类型为com.google.zxing.pdf417.encoder.CompactionString
PDF417_DIMENSIONS 指定PDF417的最大最小行列数,类型为com.google.zxing.pdf417.encoder.Dimensions
AZTEC_LAYERS aztec编码相关,指定aztec所需的层数,类型IntegerString代表的数字类型
QR_VERSION 指定二维码版本,类型IntegerString代表的数字类型
GS1_FORMAT 指定数据编码应以GS1标准,类型为boolean

具体的详细说明在打开com.google.zxing.EncodeHintType的代码上面有详细的说明。

那么我们需要设置的二维码,正常只会用到ERROR_CORRECTIONCHARACTER_SETMARGINQR_VERSION这几个参数。编码集默认是”ISO-8859-1”需要设置成”utf-8”,后两个参数一般是使用默认
我们看下ERROR_CORRECTION容错等级分别是

参数 说明
L 7%的矫正率
M 15%的矫正率
Q 25%的矫正率
H 30%的矫正率

这个ERROR_CORRECTION设置就可以让我们的生成的二维码识别率提高。

Zxing生成二维码

接下来,我们试着使用Zxing代码生成二维码的使用
我们使用的是继承于Writercom.google.zxing.qrcode.QRCodeWriter方法中的encode来创建二维码

    public static BitMatrix encode(String QRcode)
            throws WriterException {
        Map<EncodeHintType,Object> hints = new HashMap();
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        return new QRCodeWriter().encode(QRcode, BarcodeFormat.QR_CODE,300,300,hints);
    }

这边方法回调给我们的却是一个com.google.zxing.common.BitMatrix类型的值。这边我们就需要将这个类型转为Bitmap,这样我们就能使用了。

BitMatrix转Bitmap

    public static Bitmap BitMatrix2Bitmap(BitMatrix bitMatrix){
        int width = bitMatrix.getWidth();
        int height = bitMatrix.getHeight();

        int[] pixels = new int[width * height];
        //将BitMatrix的像素保存下来
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                pixels[y * width + x] = bitMatrix.get(x,y) ? 0xFF000000 : 0xFFFFFFFF;
            }
        }
        //创建一样大小的Bitmap
        Bitmap bitmap = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
        //将BitMatrix的像素绘制到Bitmap
        bitmap.setPixels(pixels, 0, width, 0, 0, width, height);

        return bitmap;
    }

这样我们就实现了最简单的二维码生成了。

Zxing解析二维码参数说明

Zxing将解析图形编码的方式抽象成了一个类com.google.zxing.Reader, 和上面生成二维码一样这边不仅仅能解析二维码,还可以解析其他图形编码。

我们依旧开始先说里面的方法

    Result decode(BinaryBitmap image) 
        throws NotFoundException, ChecksumException, FormatException;

    Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
          throws NotFoundException, ChecksumException, FormatException;

  /**
   * Resets any internal state the implementation has after a decode, to prepare it
   * for reuse.
   */
    void reset();

首先来说下reset()这个方法,在上面我将源代码的说明也写上了。翻译为重置解码后实现的任何内部状态,准备重用。

另外解析代码的参数说明如下

参数 说明
BinaryBitmap image 解码的图像
Map hints 解析的参数

我们在看看解析编码参数设置有哪些

参数 说明
OTHER(Object.class) 未指定作用,应用自定义,Object类型
PURE_BARCODE(Void.class) 图像是一个纯粹的黑白图像的条形码,Boolean类型
POSSIBLE_FORMATS(List.class) 图像是哪几种编码格式,List或BarcodeFormat类型
TRY_HARDER(Void.class) 优化识别率,为了准确性花费更长的时间,识别速度会变慢,Boolean类型
CHARACTER_SET(String.class) 编码集设置,String类型
ALLOWED_LENGTHS(int[].class) 设置编码数据长度,不解析多余的数据,int[]类型
ASSUME_CODE_39_CHECK_DIGIT(Void.class) CODE_39的使用,Boolean类型
ASSUME_GS1(Void.class) GS1条码处理,Boolean类型
RETURN_CODABAR_START_END(Void.class) CODABAR编码使用,Boolean类型
NEED_RESULT_POINT_CALLBACK(ResultPointCallback.class) 当解析到可能的结束点时进行回调,Boolean类型
ALLOWED_EAN_EXTENSIONS(int[].class) 允许EAN或UPC编码有额外的长度,int[]类型

这边我们只设置二维码的解析,常用的方法只有PURE_BARCODEPOSSIBLE_FORMATSTRY_HARDERCHARACTER_SET,其中PURE_BARCODE就默认设置就好了,其他设置TRY_HARDER为Boolean.TRUE就是加大识别率,但是速度会变慢这边按需求设置就好了,CHARACTER_SET为”utf-8”,POSSIBLE_FORMATS为了速度可以只设置一个就是BarcodeFormat.QR_CODE就可以了。

Zxing解析二维码

接下来,我们试着使用Zxing代码解析二维码的使用
我们使用的是继承于Readercom.google.zxing.qrcode.QRCodeReader方法中的decode来解析二维码

    public static String decode(BinaryBitmap image)
            throws FormatException, ChecksumException, NotFoundException {
        Map<DecodeHintType, Object> hints = new HashMap<>();
        hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
        hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
        hints.put(DecodeHintType.POSSIBLE_FORMATS, BarcodeFormat.QR_CODE);

        Result result = new QRCodeReader().decode(image,hints);
        return result.getText();
    }

这边我们可以看到,我们正常需要传入一个Bitmap而不是com.google.zxing.BinaryBitmap,那么我们就需要将Bitmap转为BinaryBitmap

Bitmap转BinaryBitmap

    public static BinaryBitmap Bitmap2BinaryBitmap(Bitmap bitmap){
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();

        int[] pixels = new int[width * height];
        //获取像素
        bitmap.getPixels(pixels, 0, width, 0, 0, width, height);

        RGBLuminanceSource source = new RGBLuminanceSource(width,height,pixels);

        return new BinaryBitmap(new HybridBinarizer(source));
    }

结合上面的方法,我们就实现了二维码的解析了。


相机结合Zxing实现扫码

我们可以看到,如果单独使用上面的方法就是可以实现的简单的生成二维码和解析二维码。但是我们正常使用的使用是使用相机然后实现扫码识别的。这边我们就需要引入相机,下面我将写一个简单的相机,然后通过拍照获取的Bitmap然后解析二维码。

创建一个普通的相机

我这边就直接贴相机的代码了,想要具体了解的可以百度或者看这篇文章Android自定义Camera最佳入门实例,当然这是以前的Camera,5.0之后的Camera2这边没有涉及。

效果如下

gif

全部代码如下

activity_qr_code.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:id="@+id/fl_qr"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </FrameLayout>

    <Button
        android:id="@+id/btn_qr"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="20dp"
        android:text="拍照" />

    <ImageView
        android:id="@+id/iv_qr"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        android:layout_margin="20dp" />
</RelativeLayout>
CameraPreview.java
import android.content.Context;
import android.hardware.Camera;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.io.IOException;

/**
 * CameraPreview
 * Author: gjn.
 * Time: 2018/3/16.
 */

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private Camera camera;
    private SurfaceHolder surfaceHolder;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        this.camera = camera;
        surfaceHolder = getHolder();
        surfaceHolder.addCallback(this);
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            camera.setPreviewDisplay(holder);
            camera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (surfaceHolder.getSurface() == null) {
            return;
        }

        camera.stopPreview();

        try {
            camera.setPreviewDisplay(surfaceHolder);
            camera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
QrCodeActivity.java
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.Toast;

import com.google.zxing.ChecksumException;
import com.google.zxing.FormatException;
import com.google.zxing.NotFoundException;


public class QrCodeActivity extends AppCompatActivity {
    private Camera mCamera;
    private CameraPreview mPreview;
    private FrameLayout fl;
    private ImageView iv;
    private Button btn;
    private Camera.PictureCallback mCallback;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        //竖屏
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        //全屏
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_qr_code);

        fl = (FrameLayout) findViewById(R.id.fl_qr);
        iv = (ImageView) findViewById(R.id.iv_qr);
        btn = (Button) findViewById(R.id.btn_qr);

        iv.setVisibility(View.GONE);

        mCallback = new Camera.PictureCallback() {
            @Override
            public void onPictureTaken(final byte[] data, Camera camera) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        if (data != null) {
                            Bitmap bitmap = BitmapFactory.decodeByteArray(data,0,data.length);
                            bitmap = rotateBitmapByDegree(bitmap,90);

                            final Bitmap endBitmap = bitmap;

                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    iv.setImageBitmap(endBitmap);
                                    iv.setVisibility(View.VISIBLE);
                                    String s = "";
                                    try {
                                        s = qrUtils.readQRBitmap(endBitmap);
                                    } catch (FormatException | ChecksumException | NotFoundException e) {
                                        e.printStackTrace();
                                    }

                                    Toast.makeText(QrCodeActivity.this, "==>\n"+s,
                                            Toast.LENGTH_SHORT).show();
                                }
                            });
                        }
                    }
                }).start();
            }
        };

        initCamera();

        onClikc();

    }

    private void onClikc() {
        fl.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCamera.autoFocus(new Camera.AutoFocusCallback() {
                    @Override
                    public void onAutoFocus(boolean success, Camera camera) {
                        Toast.makeText(QrCodeActivity.this, "聚焦成功!", Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCamera.autoFocus(new Camera.AutoFocusCallback() {
                    @Override
                    public void onAutoFocus(boolean success, Camera camera) {
                        mCamera.takePicture(null,null,mCallback);
                    }
                });
            }
        });

        iv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                iv.setVisibility(View.GONE);
                mCamera.startPreview();
            }
        });
    }

    private void initCamera() {
        try {
            mCamera = Camera.open();
        }catch (Exception e){
            e.printStackTrace();
        }
        mPreview = new CameraPreview(this,mCamera);
        fl.addView(mPreview);
        mCamera.setDisplayOrientation(90);
    }

    private Bitmap rotateBitmapByDegree(Bitmap bm, int i) {

        Matrix matrix = new Matrix();
        matrix.postRotate(i);

        Bitmap bitmap = Bitmap.createBitmap(bm, 0, 0,
                bm.getWidth(), bm.getHeight(),
                matrix, true);

        if (bitmap == null) {
            bitmap = bm;
        }
        return bitmap;
    }

    private void closeCamera() {
        if (mCamera != null) {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

    @Override
    protected void onResume() {
        mCamera.stopPreview();
        super.onResume();
    }

    @Override
    protected void onPause() {
        mCamera.startPreview();
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        closeCamera();
        super.onDestroy();
    }
}

总结

记录二维码的学习,顺便之后如果需要用到别人的二维码库,其实是可以直接修改他们的Zxing版本,达到更新Zxing的croe的更新。


资料

ZXing应用详解
Android自定义Camera最佳入门实例
zxing扫描二维码和识别图片二维码及其优化策略

猜你喜欢

转载自blog.csdn.net/g777520/article/details/79640748