Android之zxing二维码生成与识别

时间:2022-11-17 00:26:33

二维码:

是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;

在代码编制上巧妙的利用构成计算机内部逻辑基础的0和1比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图像输入设备或光电扫描设备自动识读以实现信息自动处理;

二维码能够在横向和纵向两个方位同时表达信息,因此能在很小的面积内表达大量的信息;

二维码相对于条形码的优势就是省空间;


zxing简介:

zxing是一个开放源码的,用java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的接口。

zxing可以实现使用手机的内置的摄像头完成条形码和二维码的扫描与解码。

zxing可以实现条形码和二维码的编码与解码。

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

UPC-A,UPC-E

EAN-8,EAN-13

39码

93码

代码128

QR码


Android上zxing的使用:

这里使用的时候可以有两种形式:

1.将zxing的jar包放到工程的lib库中,然后还要拷贝相应的类源码到工程中去,整个文件夹拷贝过去也是很快的;

2.将已经弄好zxing的工程作为当前工程的依赖库,然后直接使用就可以了;

如图:

Android之zxing二维码生成与识别



下面来通过一个实例来完成以下三个功能:

1.生成二维码;

2.解析二维码图片;

3.扫描二维码并解析;


最终效果是这样的:

Android之zxing二维码生成与识别


在我们新建工程之前,我们必须将依赖库导入到Eclipse中,依赖库的原工程文件夹我已经打包,文章最后面有链接可以下载。


识别二维码(识别图片)这个功能需要用到一个名叫RGBLuminanceSource的类,这个类的内容如下:

import java.io.FileNotFoundException;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import com.google.zxing.LuminanceSource;

public class RGBLuminanceSource extends LuminanceSource {
private final byte[] luminances;

public RGBLuminanceSource(Bitmap bitmap) {
super(bitmap.getWidth(), bitmap.getHeight());
//得到图片的宽高
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//得到图片的像素
int[] pixels = new int[width * height];
//
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
//为了测量纯解码速度,我们将整个图像灰度阵列前面,这是一样的通道
// YUVLuminanceSource在现实应用。
//得到像素大小的字节数
luminances = new byte[width * height];
//得到图片每点像素颜色
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
int pixel = pixels[offset + x];
int r = (pixel >> 16) & 0xff;
int g = (pixel >> 8) & 0xff;
int b = pixel & 0xff;
//当某一点三种颜色值相同时,相应字节对应空间赋值为其值
if (r == g && g == b) {
luminances[offset + x] = (byte) r;
}
//其它情况字节空间对应赋值为:
else {
luminances[offset + x] = (byte) ((r + g + g + b) >> 2);
}
}
}
}

public RGBLuminanceSource(String path) throws FileNotFoundException {
this(loadBitmap(path));
}


@Override
public byte[] getMatrix() {
return luminances;
}

@Override
public byte[] getRow(int arg0, byte[] arg1) {
if (arg0 < 0 || arg0 >= getHeight()) {
throw new IllegalArgumentException(
"Requested row is outside the image: " + arg0);
}
int width = getWidth();
if (arg1 == null || arg1.length < width) {
arg1 = new byte[width];
}
System.arraycopy(luminances, arg0 * width, arg1, 0, width);
return arg1;
}

private static Bitmap loadBitmap(String path) throws FileNotFoundException {
Bitmap bitmap = BitmapFactory.decodeFile(path);
if (bitmap == null) {
throw new FileNotFoundException("Couldn't open " + path);
}
return bitmap;
}
}

接下来,还有一个特别要注意的地方就是manifest的配置部分,需要加入权限,和依赖库中的一个Activity的声明:

    <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.zxing.activity.CaptureActivity"></activity>
</application>

好,现在我们来看看自己编写的Activity类如何实现以上所说的三个功能:

import java.util.Hashtable;

import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.zxing.activity.CaptureActivity;
import com.zxing.encoding.EncodingHandler;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener {

private static final int CHOOSE_PIC = 0;
private static final int PHOTO_PIC = 1;

private EditText contentEditText = null;
private ImageView qrcodeImageView = null;
private String imgPath = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupViews();
}

private void setupViews() {
contentEditText = (EditText) findViewById(R.id.editText1);
findViewById(R.id.button1).setOnClickListener(this);
findViewById(R.id.button2).setOnClickListener(this);
findViewById(R.id.button3).setOnClickListener(this);
qrcodeImageView = (ImageView) findViewById(R.id.img1);
}

//解析二维码图片,返回结果封装在Result对象中
private com.google.zxing.Result parseQRcodeBitmap(String bitmapPath){
//解析转换类型UTF-8
Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>();
hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
//获取到待解析的图片
BitmapFactory.Options options = new BitmapFactory.Options();
//如果我们把inJustDecodeBounds设为true,那么BitmapFactory.decodeFile(String path, Options opt)
//并不会真的返回一个Bitmap给你,它仅仅会把它的宽,高取回来给你
options.inJustDecodeBounds = true;
//此时的bitmap是null,这段代码之后,options.outWidth 和 options.outHeight就是我们想要的宽和高了
Bitmap bitmap = BitmapFactory.decodeFile(bitmapPath,options);
//我们现在想取出来的图片的边长(二维码图片是正方形的)设置为400像素
/**
options.outHeight = 400;
options.outWidth = 400;
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeFile(bitmapPath, options);
*/
//以上这种做法,虽然把bitmap限定到了我们要的大小,但是并没有节约内存,如果要节约内存,我们还需要使用inSimpleSize这个属性
options.inSampleSize = options.outHeight / 400;
if(options.inSampleSize <= 0){
options.inSampleSize = 1; //防止其值小于或等于0
}
/**
* 辅助节约内存设置
*
* options.inPreferredConfig = Bitmap.Config.ARGB_4444; // 默认是Bitmap.Config.ARGB_8888
* options.inPurgeable = true;
* options.inInputShareable = true;
*/
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeFile(bitmapPath, options);
//新建一个RGBLuminanceSource对象,将bitmap图片传给此对象
RGBLuminanceSource rgbLuminanceSource = new RGBLuminanceSource(bitmap);
//将图片转换成二进制图片
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(rgbLuminanceSource));
//初始化解析对象
QRCodeReader reader = new QRCodeReader();
//开始解析
Result result = null;
try {
result = reader.decode(binaryBitmap, hints);
} catch (Exception e) {
// TODO: handle exception
}

return result;
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
imgPath = null;
if(resultCode == RESULT_OK){
switch (requestCode) {
case CHOOSE_PIC:
String[] proj = new String[]{MediaStore.Images.Media.DATA};
Cursor cursor = MainActivity.this.getContentResolver().query(data.getData(), proj, null, null, null);

if(cursor.moveToFirst()){
int columnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
System.out.println(columnIndex);
//获取到用户选择的二维码图片的绝对路径
imgPath = cursor.getString(columnIndex);
}
cursor.close();

//获取解析结果
Result ret = parseQRcodeBitmap(imgPath);
Toast.makeText(MainActivity.this,"解析结果:" + ret.toString(), Toast.LENGTH_LONG).show();
break;
case PHOTO_PIC:
String result = data.getExtras().getString("result");
Toast.makeText(MainActivity.this,"解析结果:" + result, Toast.LENGTH_LONG).show();
break;

default:
break;
}
}

}

@SuppressLint("InlinedApi")
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
//获取界面输入的内容
String content = contentEditText.getText().toString();
//判断内容是否为空
if (null == content || "".equals(content)) {
Toast.makeText(MainActivity.this, "请输入要写入二维码的内容...",
Toast.LENGTH_SHORT).show();
return;
}

try {
//生成二维码图片,第一个参数是二维码的内容,第二个参数是正方形图片的边长,单位是像素
Bitmap qrcodeBitmap = EncodingHandler.createQRCode(content, 400);
qrcodeImageView.setImageBitmap(qrcodeBitmap);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case R.id.button2:
//跳转到图片选择界面去选择一张二维码图片
Intent intent1 = new Intent();
// if(android.os.Build.VERSION.SDK_INT < 19){
// intent1.setAction(Intent.ACTION_GET_CONTENT);
// }else{
// intent1.setAction(Intent.ACTION_OPEN_DOCUMENT);
// }
intent1.setAction(Intent.ACTION_PICK);

intent1.setType("image/*");

Intent intent2 = Intent.createChooser(intent1, "选择二维码图片");
startActivityForResult(intent2, CHOOSE_PIC);
break;
case R.id.button3:
//跳转到拍照界面扫描二维码
Intent intent3 = new Intent(MainActivity.this, CaptureActivity.class);
startActivityForResult(intent3, PHOTO_PIC);
break;

default:
break;
}

}
}

最后附上所有代码打包的链接,有需要的同学可以下载,导入到自己的Eclipse中去:

Android Zxing Demo