大概一年前的扫码风潮彻底把二维码推向大众,现在什么下载,加好友,关注,都要扫一扫,这里道长絮叨一下google旗下的ZXing,用ZXing实现二维码的生成和解析。
一、二维码的生成
这里道长用的是3.1.0版本的ZXing,如果要使用可以自己下载,也可以使用道长Demo中的。和以前一样,Demo道长放在文章最后。如果想看一下ZXing的源码可以点击传送门。
1.带边界的二维码
- 传递要生成二维码的数据
private void initData() {
String filePath = Environment.getDataDirectory().getPath() + File.separator + "data" + File.separator;
String fileName = "zxing.png";
json = new JSONObject();
try {
json.put("url", "https://github.com/zxing/zxing");
json.put("author", "yushan");
json.put("filepath", filePath);
json.put("filename", fileName);
} catch (JSONException e) {
e.printStackTrace();
}
}
- 创建工具类,并传递参数
EncodingHelper encodingHelper = new EncodingHelper(HomeActivity.this, json, 350);
Bitmap bitmap = encodingHelper.getBitmap(true);
tv_show.setVisibility(View.GONE);
iv_client_ewm.setVisibility(View.VISIBLE);
iv_client_ewm.setBackgroundDrawable(new BitmapDrawable(bitmap));
- 生成二维码的代码
/**
* 获取带边界的二维码
*
* @param hasBK
* @return
*/
public Bitmap getBitmap(Boolean hasBK) {
BitMatrix bitMatrix = null;
try {
bitMatrix = dealData();
} catch (WriterException e) {
e.printStackTrace();
}
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
int[] pixels = new int[width * height];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (bitMatrix.get(x, y)) {
pixels[y * width + x] = BLACK;
} else {
pixels[y * width + x] = WHITE;
}
}
}
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
// 画边框
if (hasBK != null && hasBK == true) {
Paint paint = new Paint();
Canvas canvas = new Canvas(bitmap);
drawBK(paint, canvas);
}
return bitmap;
}
/**
* 生成二维码矩阵
*
* @return
* @throws WriterException
*/
private BitMatrix dealData() throws WriterException {
String content = json.toString();
Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); // 字符编码
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); // 纠错等级
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix bitMatrix = new MultiFormatWriter().encode(content,
BarcodeFormat.QR_CODE, width, height, hints); // 生成矩阵
return bitMatrix;
}
- 效果图
2.带图标的二维码
- 调用方法
EncodingHelper encodingHelper = new EncodingHelper(HomeActivity.this, json, 350);
Bitmap bitmap = encodingHelper.getBitmapWithSingOrBK(R.drawable.ewm_hulu, true);
tv_show.setVisibility(View.GONE);
iv_client_ewm.setVisibility(View.VISIBLE);
iv_client_ewm.setBackgroundDrawable(new BitmapDrawable(bitmap));
- 生成二维码的代码
/**
* 获取带图标或边界的二维码
*
* @param drawableId
* @param hasBK
* @return
*/
public Bitmap getBitmapWithSingOrBK(int drawableId, Boolean hasBK) {
mBitmap = toRoundCorner(((BitmapDrawable) mContext.getResources().getDrawable(drawableId)).getBitmap(), 10);
if (mBitmap != null) {
Bitmap bitmapWithSign = bitmapWithSign(getBitmap(hasBK));
return bitmapWithSign;
} else {
return getBitmap(hasBK);
}
}
/**
* 生成带图标的二维码
*
* @param bitmap
* @return
*/
private Bitmap bitmapWithSign(Bitmap bitmap) {
Paint paint = new Paint();
Canvas canvas = new Canvas(bitmap);
canvas.drawARGB(0, 0, 0, 0);// 透明色
Rect outDst = new Rect();
outDst.left = width * 2 / 5 - 6;
outDst.top = height * 2 / 5 - 6;
outDst.right = width * 3 / 5 + 6;
outDst.bottom = height * 3 / 5 + 6;
canvas.drawBitmap(((BitmapDrawable) mContext.getResources().getDrawable(
R.drawable.ewm_bg)).getBitmap(), null, outDst, paint);
Rect inDst = new Rect();
inDst.left = width * 2 / 5;
inDst.top = height * 2 / 5;
inDst.right = width * 3 / 5;
inDst.bottom = height * 3 / 5;
canvas.drawBitmap(mBitmap, null, inDst, paint);
inDst = null;
outDst = null;
paint = null;
canvas = null;
return bitmap;
}
- 效果图
到这里道长已经把二维码的生成已经说完了,至于传参啊什么的这里道长就不说了,然后咱们说一下二维码的解析。
二、二维码的解析
这里咱么不止要初始化相机,还要自定义扫码界面。
效果图如下:
这里的自定义View是用画笔和画布实现的,这里道长就不贴代码了。
- 添加权限
记得在AndroidManifest.xml添加权限:
<uses-permission android:name="android.permission.CAMERA" />
- 初始化相机
viewfinderView.setShowInfoType(ViewfinderView.TEXTCONTENTTYPE_SCAN);
CameraManager.frameSize = 2;
CameraManager.init(getApplication());
- 开启扫描
@Override
protected void onResume() {
super.onResume();
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
if (hasSurface) {
initCamera(surfaceHolder);
} else {
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
decodeFormats = null;
characterSet = null;
}
private void initCamera(SurfaceHolder surfaceHolder) {
try {
CameraManager.get().openDriver(surfaceHolder);
} catch (IOException ioe) {
Toast.makeText(this, "请授予相应权限,再使用扫一扫!", Toast.LENGTH_SHORT).show();
finish();// 获取系统相机事件被拒绝后,直接finish()掉本activity
} catch (RuntimeException e) {
Toast.makeText(this, "请授予相应权限,再使用扫一扫!", Toast.LENGTH_SHORT).show();
finish();// 获取系统相机事件被拒绝后,直接finish()掉本activity
}
if (handler == null) {
handler = new CaptureActivityHandler(this, decodeFormats, characterSet);
}
}
- 开启一个线程decodeThread,用来扫描解析图片
public CaptureActivityHandler(ScanActivity activity, Vector<BarcodeFormat> decodeFormats,
String characterSet) {
this.activity = activity;
decodeThread = new DecodeThread(activity, decodeFormats, characterSet,
new ViewfinderResultPointCallback(activity.getViewfinderView()));
decodeThread.start();
state = State.SUCCESS;
manager = CameraManager.get();
// Start ourselves capturing previews and decoding.
manager.startPreview();
restartPreviewAndDecode();
}
- decodeThread线程在Handler中处理数据
/**
* Decode the data within the viewfinder rectangle, and time how long it took. For efficiency,
* reuse the same reader objects from one decode to the next.
*
* @param data The YUV preview frame.
* @param width The width of the preview frame.
* @param height The height of the preview frame.
*/
private void decode(byte[] data, int width, int height) {
if ((data == null) || (data.length <= 0)) {
return;
} // 判断以防null指针异常
long start = System.currentTimeMillis();
Result rawResult = null;
// modify here
byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
rotatedData[x * height + height - y - 1] = data[x + y * width];
}
int tmp = width; // Here we are swapping, that's the difference to #11
width = height;
height = tmp;
PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
try {
rawResult = multiFormatReader.decodeWithState(bitmap);
} catch (ReaderException re) {
// continue
} finally {
multiFormatReader.reset();
}
if (rawResult != null) {
long end = System.currentTimeMillis();
Log.e("yushan", "Found barcode (" + (end - start) + " ms):\n" + rawResult.toString());
Message message = Message.obtain(activity.getHandler(), R.id.decode_succeeded, rawResult);
Bundle bundle = new Bundle();
bundle.putParcelable(DecodeThread.BARCODE_BITMAP, source.renderCroppedGreyscaleBitmap());
message.setData(bundle);
// Log.d(TAG, "Sending decode succeeded message...");
message.sendToTarget();
} else {
Message message = Message.obtain(activity.getHandler(), R.id.decode_failed);
message.sendToTarget();
}
}
- 处理完成的数据通过message发送到另一个Handler
@Override
public void handleMessage(Message message) {
switch (message.what) {
case R.id.auto_focus:
//Log.d(TAG, "Got auto-focus message");
// When one auto focus pass finishes, start another. This is the closest thing to
// continuous AF. It does seem to hunt a bit, but I'm not sure what else to do.
if (state == State.PREVIEW) {
manager.requestAutoFocus(this, R.id.auto_focus);
}
break;
case R.id.restart_preview:
Log.d(TAG, "Got restart preview message");
restartPreviewAndDecode();
break;
case R.id.decode_succeeded:
Log.d(TAG, "Got decode succeeded message");
state = State.SUCCESS;
Bundle bundle = message.getData();
/***********************************************************************/
// Bitmap barcode = bundle == null ? null : (Bitmap) bundle.getParcelable(DecodeThread.BARCODE_BITMAP);
// activity.handleDecode((Result) message.obj, barcode);
String resultString = ((Result) message.obj).getText() + "";
activity.handleDecode(resultString);// 返回数据
/***********************************************************************/
break;
case R.id.decode_failed:
// We're decoding as fast as possible, so when one decode fails, start another.
state = State.PREVIEW;
manager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
break;
case R.id.return_scan_result:
Log.d(TAG, "Got return scan result message");
activity.setResult(Activity.RESULT_OK, (Intent) message.obj);
activity.finish();
break;
case R.id.launch_product_query:
Log.d(TAG, "Got product query message");
String url = (String) message.obj;
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
activity.startActivity(intent);
break;
}
}
- 把解析数据返回到主界面
/**
* 扫描返回数据
*
* @param resultString
*/
public void handleDecode(String resultString) {
inactivityTimer.onActivity();
Log.e("yushan", resultString);
Intent intent = this.getIntent();
intent.putExtra("resultString", resultString);
this.setResult(RESULT_OK, intent);
this.finish();
}
- 展示解析结果
好了,到了这里ZXing实现二维码的生成与解析都已经实现了,希望这篇博客能够为你提供一些帮助。