本篇博客主要记录一下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后,问题就解决了。