[TOC]
前言
接上篇,我们修改了扫码界面,不过整个库的方法调用还是比较繁琐,接下来对常用的扫码相关方法进行封装。
编写工具类
首先对生成二维码和识别图片二维码两个功能进行封装,代码不多直接贴出来,这其中生成二维码提供了加logo和不加logo两个方法供调用,详细看代码。
public class QRUtil {
private static final int QR_BM_SIZE = 400;//生成二维码图片大小
private static final int QR_BM_LOGO_SIZE = QR_BM_SIZE / 3;//logo大小
public static Bitmap createQRBitmap(String context, int bm_size) {
return createQRBitmap(context, bm_size, null, 2);
}
/**
* 生成二维码
*
* @param context
* @param bm_size
* @param logo
* @param edgeMargin
* @return
*/
public static Bitmap createQRBitmap(String context, int bm_size, Bitmap logo, int edgeMargin) {
Bitmap bitmap = null;
BitMatrix matrix = null;
MultiFormatWriter writer = new MultiFormatWriter();
try {
//设置格式
Map<EncodeHintType, Object> encodeHintTypeMap = new HashMap<>();
encodeHintTypeMap.put(EncodeHintType.MARGIN, edgeMargin <= 0 ? 2 : edgeMargin);
matrix = writer.encode(context, BarcodeFormat.QR_CODE, bm_size, bm_size, encodeHintTypeMap);
BarcodeEncoder encoder = new BarcodeEncoder();
bitmap = encoder.createBitmap(matrix);
if (logo != null && !logo.isRecycled()) {
bitmap = synthesisLogo(bitmap, logo);
}
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
/**
* 在二维码bitmap中加入logo
* @param qrBm
* @param logo
* @return
*/
private static Bitmap synthesisLogo(Bitmap qrBm, Bitmap logo) {
Canvas canvas = new Canvas(qrBm);
int w = logo.getWidth();
int h = logo.getHeight();
float wScale = QR_BM_LOGO_SIZE * 1.0f / w;
float hScale = QR_BM_LOGO_SIZE * 1.0f / h;
Matrix matrix = new Matrix();
matrix.postScale(wScale, hScale);
logo = Bitmap.createBitmap(logo, 0, 0, w, h, matrix, false);
int middle = qrBm.getWidth() / 2;
RectF rectF = new RectF(middle - QR_BM_LOGO_SIZE / 2, middle - QR_BM_LOGO_SIZE / 2, middle + QR_BM_LOGO_SIZE / 2, middle + QR_BM_LOGO_SIZE / 2);
canvas.drawBitmap(logo, null, rectF, null);
return qrBm;
}
/**
* 识别bitmap中的二维码信息,该方法不宜在主线程调用
*
* @param bitmap
* @return
*/
public static Result spotQRCode(Bitmap bitmap) throws FormatException, ChecksumException, NotFoundException {
Result result = null;
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int[] data = new int[width * height];
bitmap.getPixels(data, 0, width, 0, 0, width, height);
RGBLuminanceSource source = new RGBLuminanceSource(width, height, data);
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
QRCodeReader reader = new QRCodeReader();
//result中包含了扫描到的信息,调用 result.getText()可以获取到文本信息
result = reader.decode(binaryBitmap);
return result;
}
}
封装扫码相关方法
封装之前需要考虑一下想要什么样的调用方式,一般我们希望通过一个方法启动,在一个监听类里面接收返回的数据就好,按照这样的思路我们新建一个类QRScannerHelper
public class QRScannerHelper {
private Activity mContext;
private OnScannerCallBack mCallBack;
public QRScannerHelper(Activity context) {
this.mContext = context;
}
/**
* 开启扫码界面
*/
public void startScanner() {
new IntentIntegrator(mContext)
.setOrientationLocked(false)
.setDesiredBarcodeFormats(IntentIntegrator.ALL_CODE_TYPES)
.setPrompt("将二维码/条码放入框内,即可自动扫描")
.initiateScan(); // 初始化扫描
}
/**
* 设置扫码完成该的监听
*
* @param mCallBack
*/
public void setCallBack(OnScannerCallBack mCallBack) {
this.mCallBack = mCallBack;
}
/**
* 该方法需要再activity的onActivityResult中调用获取返回的信息
*
* @param requestCode
* @param resultCode
* @param data
*/
public void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult intentResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if (mCallBack != null) {
mCallBack.onScannerBack(intentResult.getContents());
}
}
public interface OnScannerCallBack {
void onScannerBack(String result);
}
}
现在我们启动扫码的操作如下
/**
* 在onCreate中调用
*/
private void initQRScanner() {
mScannerHelper = new QRScannerHelper(this);
mScannerHelper.setCallBack(new QRScannerHelper.OnScannerCallBack() {
@Override
public void onScannerBack(IntentResult result) {
Toast.makeText(MainActivity.this, result.getContents(), Toast.LENGTH_SHORT).show();
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (mScannerHelper != null) {
mScannerHelper.onActivityResult(requestCode, resultCode, data);
}
}
/**
* 开启扫描界面
*
* @param view
*/
public void start(View view) {
mScannerHelper.startScanner();
}
增加辅助功能 闪光灯开关和相册选取照片识别二维码
闪光灯开关
在zxing_capture.xml中增加2个按钮
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!--
This Activity is typically full-screen. Therefore we can safely use centerCrop scaling with
a SurfaceView, without fear of weird artifacts. -->
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:id="@+id/zxing_barcode_scanner"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:zxing_framing_rect_height="200dp"
app:zxing_framing_rect_width="200dp"
app:zxing_preview_scaling_strategy="centerCrop"
app:zxing_use_texture_view="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginBottom="20dp"
android:background="@android:color/transparent"
android:gravity="center"
android:orientation="horizontal">
<!--切换闪光灯-->
<ImageView
android:id="@+id/btn_switch_light"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/lights" />
<!--打开相册-->
<ImageView
android:id="@+id/btn_open_album"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="20dp"
android:src="@drawable/pic" />
</LinearLayout>
</com.journeyapps.barcodescanner.DecoratedBarcodeView>
</merge>
DecoratedBarcodeView类其实已经封装好了打开和关闭闪光灯的方法,因此只需要在CaptureActivity调用
mBarcodeScannerView.setTorchListener(this);
mSwitchLightView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (isLightOn) {
mBarcodeScannerView.setTorchOff();
} else {
mBarcodeScannerView.setTorchOn();
}
}
});
相册选取照片分为相册选取照片加载到内存bitmap和对bitmap进行时和别操作,为了不在CaptureActivity引入过多的代码,我新建一个类将这部分代码进行封装,创建一个类叫QRSpotHelper,QRSpotHelper的详细代码较多不贴出来,可下载demo查看。在CaptureActivity中使用
//相册选取按钮的点击事件
mOpenAlbumView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mQrSpotHelper == null) {
mQrSpotHelper = new QRSpotHelper(CaptureActivity.this, mOnSpotCallBack);
}
mQrSpotHelper.spotFromAlbum();
}
});
//由于照片选取需要在onActivityResult中回调,因此需要添加下面代码
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (mQrSpotHelper != null) {
mQrSpotHelper.onActivityResult(requestCode, resultCode, data);
}
}
//照片识别的监听
private QRSpotHelper.OnSpotCallBack mOnSpotCallBack = new QRSpotHelper.OnSpotCallBack() {
@Override
public void onSpotStart() {
mProgressBar.setVisibility(View.VISIBLE);
}
@Override
public void onSpotSuccess(Result result) {
//识别成功后将返回的结果传递给上层activity
mProgressBar.setVisibility(View.GONE);
String data = result.getText();
Intent intent = new Intent();
intent.putExtra("data", data);
setResult(SPOT_SUCCESS, intent);
finish();
}
@Override
public void onSpotError() {
mProgressBar.setVisibility(View.GONE);
Toast.makeText(CaptureActivity.this, "未发现二维码", Toast.LENGTH_SHORT).show();
}
};
在相册选取照片识别成功的代码中,我们将结果提交给上层activity,而上层activity的onActivityResult已经被我们在QRScannerHelper中拦截,因此需要重新修改QRScannerHelper的onActivityResult方法处理识别结果返回给监听类
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mCallBack == null) return;
String result;
if (requestCode == IntentIntegrator.REQUEST_CODE && resultCode == CaptureActivity.SPOT_SUCCESS) {
result = data.getStringExtra("data");
} else {
IntentResult intentResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
result = intentResult.getContents();
}
mCallBack.onScannerBack(result);
}
通过上面对源代码的梳理和修改,对二维码的操作变得简单了许多。demo中所涉及到的一些样式仅仅作为参考,可自行修改。如有不足之处还请指出,感谢。