0. 前言
今天这篇文章主要描述二维码的生成与扫描,使用目前流行的Zxing,为什么要讲二维码,因为二维码太普遍了,随便一个Android APP都会有二维码扫描。本篇旨在帮助有需求的同学快速完成二维码生成和扫描的功能。
本篇转载自:http://blog.csdn.net/hai_qing_xu_kong/article/details/51260428
1. Zxing的使用
从github上下载项目后,可以看到整体代码结构如下:
我们只需将Zxing包下的所有代码copy一份到我们的项目中去,除了这些还需要zxing的jar包,最后相应的资源文件,包括values文件下的ids文件、raw文件中的资源文件(可以替换)、layout文件下的activity_capture.xml(可以进行相应的订制) 和图片资源。
2. 生成二维码的实现
等上面工作全部准备完毕后,就可以创建我们的二维码了。如何生成二维码?
需要EncodingUtils这个二维码生成工具类。通过调用工具类中的createQRCode()方法来生成二维码。该方法参数介绍如下:
-
/*
-
* content:二维码内容
-
* widthPix:二维码宽度
-
* heightPix:二维码高度
-
* logoBm:二维码中间的logo对应的Bitmap
-
*/
-
public
static Bitmap
createQRCode
(String content, int widthPix, int heightPix, Bitmap logoBm)
下面完成的是生成的一个百度地址的二维码,中间LOGO是Android小机器人。并保存图片到本地,方便后续测试二维码的本地读取功能。
-
/**
-
* 创建、展示二维码并将bitmap保存在本地
-
*/
-
private
void
create
() {
-
int
width
= DensityUtil.dip2px(
this,
200);
-
Bitmap
bitmap
= EncodingUtils.createQRCode(
"http://www.baidu.com",
-
width, width, BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher));
-
iv_zxing.setImageBitmap(bitmap);
-
saveBitmap(bitmap);
-
}
-
-
/**
-
* 将Bitmap保存在本地
-
*
-
* @param bitmap
-
*/
-
public
void
saveBitmap
(Bitmap bitmap) {
-
// 首先保存图片
-
File
appDir
=
new
File(Environment.getExternalStorageDirectory(),
"zxing_image");
-
if (!appDir.exists()) {
-
appDir.mkdir();
-
}
-
String
fileName
=
"zxing_image" +
".jpg";
-
File
file
=
new
File(appDir, fileName);
-
try {
-
FileOutputStream
fos
=
new
FileOutputStream(file);
-
bitmap.compress(CompressFormat.JPEG,
100, fos);
-
fos.flush();
-
fos.close();
-
}
catch (Exception e) {
-
e.printStackTrace();
-
}
-
-
// 把文件插入到系统图库
-
try {
-
MediaStore.Images.Media.insertImage(
this.getContentResolver(),file.getAbsolutePath(), fileName,
null);
-
}
catch (FileNotFoundException e) {
-
e.printStackTrace();
-
}
-
-
// 通知图库更新
-
sendBroadcast(
new
Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
-
Uri.parse(
"file://" +
"/sdcard/namecard/")));
-
}
看到如下效果:
3. 读取二维码的实现
3.1 摄像头扫描的方式
二维码扫描需要借助于CaptureActivity这个类,打开CaptureActivity界面并进行扫描,扫描完毕后回调onActivityResult()方法,从onActivityResult()中得到扫描后的结果。效果就不演示的,因为使用的是模拟器。详细代码如下:
-
/**
-
* 打开二维码扫描
-
*/
-
private
void
open
() {
-
config();
-
startActivityForResult(
new
Intent(MainActivity.
this,CaptureActivity.class),
0);
-
}
-
/**
-
* 提高屏幕亮度
-
*/
-
private
void
config
() {
-
WindowManager.
LayoutParams
lp
= getWindow().getAttributes();
-
lp.screenBrightness =
1.0f;
-
getWindow().setAttributes(lp);
-
}
-
-
@Override
-
protected
void
onActivityResult
(int requestCode, int resultCode, Intent data) {
-
super.onActivityResult(requestCode, resultCode, data);
-
if (resultCode == RESULT_OK) {
-
Bundle
bundle
= data.getExtras();
-
String
result
= bundle.getString(
"result");
-
tv_result.setText(result);
-
}
-
}
3.2 本地图片扫描的方式
扫描本地图片需要我们在CaptureActivity中进行相应的修改,为此我在扫描界面底部增加了一个按钮,用来选择本地图片。layout代码这里就不展示,我们直接看点击后的事件处理。
-
/**
-
* 打开本地图片
-
*/
-
private
void
openLocalImage
() {
-
// 打开手机中的相册
-
Intent
innerIntent
=
new
Intent(Intent.ACTION_GET_CONTENT);
-
innerIntent.setType(
"image/*");
-
Intent
wrapperIntent
= Intent.createChooser(innerIntent,
"选择二维码图片");
-
this.startActivityForResult(wrapperIntent,
0x01);
-
}
打开系统图片库后选择图片,这时需要重写onActivityResult()方法用于返回图片信息。
-
@Override
-
protected
void
onActivityResult
(int requestCode, int resultCode, Intent data) {
-
super.onActivityResult(requestCode, resultCode, data);
-
if (resultCode == RESULT_OK) {
-
switch (requestCode) {
-
case
0x01:
-
// 获取选中图片的路径
-
Cursor
cursor
= getContentResolver().query(data.getData(),
null,
null,
null,
null);
-
if (cursor.moveToFirst()) {
-
photo_path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
-
}
-
cursor.close();
-
-
new
Thread(
new
Runnable() {
-
@Override
-
public
void
run
() {
-
Result
result
= scanningImage(photo_path);
-
if (result !=
null) {
-
handleDecode(result,
new
Bundle());
-
}
-
}
-
}).start();
-
break;
-
}
-
}
-
}
获取图片路径photo_path后,调用scanningImage()方法进行扫描,Zxing源码中,扫描到的结果都是存放在Result结果集中。获取到Result后,就进行结果的回传,阅读CaptureActivity源码可以得知最后Result结果集会传递给handleDecode()方法。
-
/**
-
* A valid barcode has been found, so give an indication of success and show
-
* the results.
-
*
-
* @param rawResult
-
* The contents of the barcode.
-
* @param bundle
-
* The extras
-
*/
-
public
void
handleDecode
(Result rawResult, Bundle bundle) {
-
inactivityTimer.onActivity();
-
beepManager.playBeepSoundAndVibrate();
-
-
Intent
resultIntent
=
new
Intent();
-
bundle.putInt(
"width", mCropRect.width());
-
bundle.putInt(
"height", mCropRect.height());
-
bundle.putString(
"result", rawResult.getText());
-
resultIntent.putExtras(bundle);
-
this.setResult(RESULT_OK, resultIntent);
-
CaptureActivity.
this.finish();
-
}
获取到图片路径后需要将其二维码信息包装成Result对象,因此需要解析图片:
-
/**
-
* 扫描二维码图片的方法
-
*
-
* @param path
-
* @return
-
*/
-
public Result
scanningImage
(String path) {
-
if (TextUtils.isEmpty(path)) {
-
return
null;
-
}
-
Hashtable<DecodeHintType, String> hints =
new
Hashtable<DecodeHintType, String>();
-
hints.put(DecodeHintType.CHARACTER_SET,
"UTF8");
// 设置二维码内容的编码
-
-
BitmapFactory.
Options
options
=
new
BitmapFactory.Options();
-
options.inJustDecodeBounds =
true;
// 先获取原大小
-
scanBitmap = BitmapFactory.decodeFile(path, options);
-
options.inJustDecodeBounds =
false;
// 获取新的大小
-
int
sampleSize
= (
int) (options.outHeight / (
float)
200);
-
if (sampleSize <=
0)
-
sampleSize =
1;
-
options.inSampleSize = sampleSize;
-
scanBitmap = BitmapFactory.decodeFile(path, options);
-
int
width
= scanBitmap.getWidth();
-
int
height
= scanBitmap.getHeight();
-
int[] pixels =
new
int[width * height];
-
scanBitmap.getPixels(pixels,
0, width,
0,
0, width, height);
-
/**
-
* 第三个参数是图片的像素
-
*/
-
RGBLuminanceSource
source
=
new
RGBLuminanceSource(width, height,
-
pixels);
-
BinaryBitmap
bitmap1
=
new
BinaryBitmap(
new
HybridBinarizer(source));
-
QRCodeReader
reader
=
new
QRCodeReader();
-
try {
-
return reader.decode(bitmap1, hints);
-
-
}
catch (NotFoundException e) {
-
e.printStackTrace();
-
}
catch (ChecksumException e) {
-
e.printStackTrace();
-
}
catch (FormatException e) {
-
e.printStackTrace();
-
}
-
return
null;
-
}
根据路径获取Bitmap,最后通过QRCodeReader 中的decode方法解析成Result对象并返回,最终传递给handleDecode方法。运行程序效果如下,扫描出来的是之前定义的百度地址。
最后不要忘了申明权限和CaptureActivity。
-
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
-
<uses-permission android:name="android.permission.CAMERA"/>
-
<uses-permission android:name="android.permission.VIBRATE"/>
-
<activity android:name="com.example.zxingtest.zxing.activity.CaptureActivity"/>