Android二维码的创建、解析及NotFoundException

来源:互联网 发布:主动刹车 知乎 编辑:程序博客网 时间:2024/06/16 10:28

本篇博客主要记录一下Android生成及解析二维码的基本方法,
同时记录一下遇到的NotFoundException及对应解决方法。


如今很多APK都集成了二维码,其本质不过是将信息按照0、1的方式写入到图片中,
0、1分别对应了不同的颜色。

从APK调用api的角度来看,生成和解析二维码是非常容易的,
真正的难度其实还是在信息的编码及图像的识别上,而这就比较依赖于专业知识了。

对于普通开发者而言,在项目中引用google的支持库后,就能够开发基本的功能了。

我自己写demo时,在Android Studio中引用的支持库为com.google.zxing:core:3.3.0。

整个demo的界面非常简单,如下图所示:

基本功能就是点击GENERATE按键后,在界面下方生成二维码;
点击SCAN按键后,从二维码图片中得到对应的信息。


其中,生成二维码的核心代码如下:

    private void generateQRCode() {        //得到信息对应的像素数组        int[] pixels = generatePixels();        if (pixels != null) {            Bitmap bitmap = Bitmap.createBitmap(                    QR_WIDTH, QR_HEIGHT, Bitmap.Config.ARGB_8888);            //根据像素数组生成bitmap            //这部分参数含义查看一下API描述,比较容易弄懂            bitmap.setPixels(pixels, 0, QR_WIDTH, 0, 0, QR_WIDTH, QR_HEIGHT);            //将bitmap显示到界面上            mImageView.setImageBitmap(bitmap);            mScanButton.setEnabled(true);        }    }

容易看出,上面代码的重点部分在于生成像素数组。

这部分内容如下所示:

    private int[] generatePixels() {        //得到需要写入的信息        String data = createData();        int[] pixels = null;        //定义编码的附加信息,这里指定以utf-8编码        Hashtable<EncodeHintType, Object> hints = new Hashtable<>();        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");        try {            //这里以QRcodeWriter,将信息编码成矩阵            //参数分别为信息、编码格式、矩阵宽、矩阵高及附加信息            BitMatrix bitMatrix = new QRCodeWriter()                    .encode(data, BarcodeFormat.QR_CODE, QR_WIDTH, QR_HEIGHT, hints);            //得到矩阵后,就可以根据矩阵得到像素数组            pixels = new int[QR_WIDTH * QR_HEIGHT];            for (int y = 0; y < QR_HEIGHT; ++y) {                for (int x = 0; x < QR_WIDTH; ++x) {                    //轮寻矩阵的每一个元素                    //我在这里比较传统的,用黑白来形成像素数组                    //按需可进行调整                    pixels[y * QR_WIDTH + x] =                            bitMatrix.get(x, y) ? Color.BLACK : Color.WHITE;                }            }        } catch (WriterException e) {            Log.d("ZJTest", e.toString());        }        return pixels;    }

最后这一部分是用于生成信息的:

    private String createData() {        String data = null;        try {            JSONObject jsonObject = new JSONObject();            jsonObject.put("author", "ZhangJianIsAStark");            jsonObject.put("url", "http://blog.csdn.net/gaugamela");            data = jsonObject.toString();        } catch (JSONException e) {            Log.d("ZJTest", e.toString());        }        return data;    }

可以按照需要写入任何信息,只要最后以String表示即可。
我在这里是以JSONObject的信息,生成String。

按照上述代码,点击GENERATE按键后,就可以得到二维码了,如下图所示:

图中,我的长宽的数值均为1000。

根据上面的代码,我们知道形成二维码的逻辑是:
1、得到String字符;
2、将String字符编码为像素数组;
3、根据像素数组,得到Bitmap。


接下来,我们看看解析二维码的基本方法。

一般像微信扫一扫这样的功能,都依赖于对Camera的操作。
自己对这一块不是很熟,因此是照着其他人的demo学习的,链接如下。
http://www.cnblogs.com/weixing/archive/2013/08/28/3287120.html
这个demo的主体应该是Zxing的示例代码,其中在Android 5.1之后的版本上使用时,
需要修改代码获取Camera的运行时权限。
此外,该demo中Camera的用法太老了,现在都应该使用Camera2的接口,简单看看就好。

在这里我仅记录一下,直接从图像中解析信息的步骤,略去Camera获取图片等操作。

    private void scanQRCode() {        //将ImageView的drawable转化为BitmapDrawable        //然后获取Bitmap        Bitmap bitmap = ((BitmapDrawable)mImageView.getDrawable()).getBitmap();        //按比例缩小Bitmap        Matrix matrix = new Matrix();        matrix.postScale(0.1f, 0.1f);        Bitmap resizeBitmap = Bitmap.createBitmap(bitmap, 0, 0,                bitmap.getWidth(), bitmap.getHeight(), matrix, true);        int width = resizeBitmap.getWidth();        int height = resizeBitmap.getHeight();        //从Bitmap中获取像素矩阵        int[] pixels = new int[width * height];        resizeBitmap.getPixels(pixels, 0, width, 0, 0, width, height);        //利用像素矩阵得到RGBLuminanceSource        //ZXing库中定义了LuminanceSource及其子类        RGBLuminanceSource rgbLuminanceSource =                new RGBLuminanceSource(width, height, pixels);        //利用LuminanceSource、HybridBinarizer得到BinaryBitmap        BinaryBitmap binaryBitmap = new BinaryBitmap(                new HybridBinarizer(rgbLuminanceSource));        //定义解码附加信息        Hashtable<DecodeHintType, Object> hints = new Hashtable<>();        hints.put(DecodeHintType.CHARACTER_SET, "utf-8");        try {            //利用MultiFormatReader得到解码结果            //本例也可以使用QRCodeReader            Result result = new MultiFormatReader().decode(binaryBitmap, hints);            try {                //利用Result的getText方法获取String对象                //以下是JSONObject的基本解析                JSONObject jsonObject = new JSONObject(result.getText());                String name = jsonObject.getString("author");                String url = jsonObject.getString("url");                Log.d("ZJTest", "name: " + name + ", url: " + url);            } catch (JSONException e) {                Log.d("ZJTest", e.toString());            }        } catch (NotFoundException e) {            Log.d("ZJTest", e.toString());            e.printStackTrace();        }    }

解析图像后的log信息如下:

03-20 21:41:42.675 5781-5781/stark.a.is.zhang.zxingtest D/ZJTest: name: ZhangJianIsAStark, url: http://blog.csdn.net/gaugamela

与之前写入的一致。

从上面的代码可以看出,二维码的解析,基本上是生成的逆过程:
1、根据Bitmap,得到像素数组;
2、根据像素数组解码出字符;
3、从字符中得到之前写入的信息。

其中解码的过程比较复杂,使用了大量Zxing库中的类。


最后记录一下,写代码遇到的问题。

最初写demo时,自己在解析二维码时,
从ImageView中获取Bitmap后,并没有对Bitmap进行缩小的操作。
于是,遇到了Zxing库抛出的NotFoundException。

自己仔细看了代码,没觉得有什么问题。
结果修改ImageView的宽、高为200以下时,就能正确解析出结果。

根据这个现象,首先推测是否从bitmap获取像素数组时,丢失了部分信息。
于是进行测试,发现在宽、高为1000时,比较生成和解析的像素数组,发现完全一致。
因此,图片与像素数组之间的转换没有问题,应该是Zxing库的原因。

目前的根本原因没有找到,个人推测是否Zxing库限制了像素数组大小的上限,
当像素信息超过上限后,会丢失掉部分信息?

在主动缩小Bitmap后,问题就解决了。

0 0