Android 二维码扫描(仿微信界面),根据Google zxing
2017-12-18 11:03
323 查看
Android 二维码扫描(仿微信界面),根据Google zxing
Android项目开发中经常会用到二维码扫描,例如登陆、支付等谷歌方面已经有了一个开源库(地址: https://github.com/zxing/zxing),里面的内容还是比较多的,如果想深入学习这方面的内容,还是不错的。当然了还是要根据实际需求去选择,所以我们就选择类库中的核心Jar包就行了(地址:https://github.com/ASCN-BJ/ZhaoYun/blob/master/qrcodelibrary/libs/core-3.3.1.jar)。GoogleZxing中的原装的类库有一个问题就是竖屏的时候会导致无法识别,原因是竖屏的时候没有对相机的预览效果进行处理,所以在项目中,进行了处理,使得竖屏的时候的识别能够成功。所以正好看了一下,就当学习了。
效果图
- 二维码的扫描
zxing中的二维码识别界面主要是在CaptureActivity中,基本原理通过获取相机的预览界面然后对获取到的数据进行处理,预览效果是在PreviewCallback中获取数据,然后通过DecodeHandler发送数据,具体识别是在DecodeHandler类中处理。
@Override public void onPreviewFrame(byte[] data, Camera camera) { Point cameraResolution = configManager.getCameraResolution(); Handler thePreviewHandler = previewHandler; // if (cameraResolution != null && thePreviewHandler != null) { // Message message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.x, // cameraResolution.y, data); // message.sendToTarget(); // previewHandler = null; // } else { // Log.d(TAG, "Got preview callback, but no handler or resolution available"); // } // Log.d(TAG, "onPreviewFrame: " + "onRunning"); if (cameraResolution != null && thePreviewHandler != null) { //add by tancolo Point screenResolution = configManager.getScree 4000 nResolution(); Message message; if (screenResolution.x < screenResolution.y) { // portrait message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.y, cameraResolution.x, data); } else { // landscape message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.x, cameraResolution.y, data); } message.sendToTarget(); previewHandler = null; } }
可以看到在onPreviewFrame中将具体byte[] data数据发送出去,同时将预览效果的宽高发送出去,用于后续的处理。
接下来看具体二维码识别的代码
@Override public void handleMessage(Message message) { if (message == null || !running) { return; } if (message.what == R.id.decode) { decode((byte[]) message.obj, message.arg1, message.arg2); } else if (message.what == R.id.quit) { running = false; Looper.myLooper().quit(); } }
private void decode(byte[] data, int width, int height) { long start = System.currentTimeMillis(); //add by tancolo if (width < height) { // portrait byte[] rotatedData = new byte[data.length]; for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) rotatedData[y * width + width - x - 1] = data[y + x * height]; } data = rotatedData; } //end add Result rawResult = null; PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height); if (source != null) { BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); try { rawResult = multiFormatReader.decodeWithState(bitmap);//具体识别方法,获取到数据 } catch (ReaderException re) { // continue } finally { multiFormatReader.reset(); } } Handler handler = activity.getHandler(); if (rawResult != null) { // Don't log the barcode contents for security. long end = System.currentTimeMillis(); Log.d(TAG, "Found barcode in " + (end - start) + " ms"); if (handler != null) { Message message = Message.obtain(handler, R.id.decode_succeeded, rawResult); Bundle bundle = new Bundle(); bundleThumbnail(source, bundle); message.setData(bundle); message.sendToTarget(); } } else { if (handler != null) { Message message = Message.obtain(handler, R.id.decode_failed); message.sendToTarget(); } } }
二维码的生成
这里写代码片
try { mQRCodeEncoder2 = new QRCodeEncoder2(smallerDimension); //生成二维码 Bitmap bitmap = mQRCodeEncoder2.encodeAsBitmap(text, BarcodeFormat.QR_CODE); if (bitmap == null) { qrCodeEncoder = null; return true; } iv_image_view.setImageBitmap(bitmap); } catch (WriterException e) { qrCodeEncoder = null; }
具体识别
public Bitmap encodeAsBitmap(String contentsToEncode, BarcodeFormat format) throws WriterException { if (contentsToEncode == null) { return null; } Map<EncodeHintType, Object> hints = null; String encoding = guessAppropriateEncoding(contentsToEncode); if (encoding != null) { hints = new EnumMap<>(EncodeHintType.class); hints.put(EncodeHintType.CHARACTER_SET, encoding); } BitMatrix result; try { format = BarcodeFormat.valueOf(format.toString()); result = new MultiFormatWriter().encode(contentsToEncode, format, dimension, dimension, hints); } catch (IllegalArgumentException iae) { // Unsupported format return null; } int width = result.getWidth(); int height = result.getHeight(); int[] pixels = new int[width * height]; for (int y = 0; y < height; y++) { int offset = y * width; for (int x = 0; x < width; x++) { pixels[offset + x] = result.get(x, y) ? BLACK : WHITE;//生成二维码数据 } } Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); bitmap.setPixels(pixels, 0, width, 0, 0, width, height); return bitmap; }
本地图片的识别
public static String getResult(Bitmap bitmap) { //Zxing自带的解析类 byte data[]; int[] datas = new int[bitmap.getWidth() * bitmap.getHeight()]; data = new byte[bitmap.getWidth() * bitmap.getHeight()]; bitmap.getPixels(datas, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight()); for (int i = 0; i < datas.length; i++) { data[i] = (byte) datas[i]; } Set<BarcodeFormat> decodeFormats = new HashSet<>(); decodeFormats.addAll(DecodeFormatManager.PRODUCT_FORMATS); decodeFormats.addAll(DecodeFormatManager.INDUSTRIAL_FORMATS); decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS); decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS); decodeFormats.addAll(DecodeFormatManager.AZTEC_FORMATS); decodeFormats.addAll(DecodeFormatManager.PDF417_FORMATS); Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class); hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats); MultiFormatReader multiFormatReader = new MultiFormatReader(); multiFormatReader.setHints(hints); Result rawResult = null; PlanarYUVLuminanceSource source = buildLuminanceSource(data, bitmap.getWidth(), bitmap.getHeight()); if (source != null) { BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source)); try { rawResult = multiFormatReader.decodeWithState(bitmap1); } catch (ReaderException re) { // continue } finally { multiFormatReader.reset(); } } return rawResult != null ? rawResult.getText() : ""; }
手势添加
微信中我们可以发现有手势识别,主要是双击效果,多点触控效果,手电筒(根据传感器显示手电筒图标)
public final class ViewfinderView extends View implements ScaleGestureDetector.OnScaleGestureListener, View.OnTouchListener{ private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64}; private static final long ANIMATION_DELAY = 80L; private static final int CURRENT_POINT_OPACITY = 0xA0; private static final int MAX_RESULT_POINTS = 20; private static final int POINT_SIZE = 6; private CameraManager cameraManager; private final Paint paint; private Bitmap resultBitmap; private final int maskColor; private final int resultColor; private final int laserColor; private final int resultPointColor; private int scannerAlpha; private List<ResultPoint> possibleResultPoints; private List<ResultPoint> lastPossibleResultPoints; private Rect tmpRect; private Bitmap tmpBitmap; private float cornerWidth;//方角宽度 private float cornerLength;//方角长度 private int mStrokeWidth;//内圈寬度 private int corner_rect_length;//内圈颜色 private int cornerColor;//角度颜色 private String description;//描述文字 private int textColor; private boolean textVisible; private float textMargin;//描述文本距离上面的距离 private float text_size;//描述文本字体大小 private ScaleGestureDetector scaleGestureDetector; private GestureDetector gestureDetector; private boolean zoomMaxFlag = true; // This constructor is used when the class is built from an XML resource. public ViewfinderView(Context context, AttributeSet attrs) { super(context, attrs); // Initialize these once for performance rather than calling them every time in onDraw(). paint = new Paint(Paint.ANTI_ALIAS_FLAG); Resources resources = getResources(); resultColor = resources.getColor(R.color.result_view); laserColor = resources.getColor(R.color.viewfinder_laser); resultPointColor = resources.getColor(R.color.possible_result_points); scannerAlpha = 0; possibleResultPoints = new ArrayList<>(5); lastPossibleResultPoints = null; tmpRect = new Rect(); tmpBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.scan); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewfinderView); cornerWidth = a.getDimensionPixelSize(R.styleable.ViewfinderView_corner_width, dip2px(context, 4)); cornerLength = a.getDimensionPixelSize(R.styleable.ViewfinderView_corner_length, dip2px(context, 20)); mStrokeWidth = a.getDimensionPixelSize(R.styleable.ViewfinderView_corner_rect_length, dip2px(context, 1)); cornerColor = a.getColor(R.styleable.ViewfinderView_corner_color, Color.parseColor("#62e203")); corner_rect_length = a.getColor(R.styleable.ViewfinderView_corner_rect_color, Color.parseColor("#66ffffff")); maskColor = a.getColor(R.styleable.ViewfinderView_mask_color, resources.getColor(R.color.viewfinder_mask)); description = a.getString(R.styleable.ViewfinderView_text_description); if (TextUtils.isEmpty(description)) { description = "将二维码/条码放入框内,即可自动扫描"; } textColor = a.getColor(R.styleable.ViewfinderView_text_color, Color.parseColor("#66ffffff")); textVisible = a.getBoolean(R.styleable.ViewfinderView_text_visible, true); textMargin = a.getDimensionPixelSize(R.styleable.ViewfinderView_text_margin, dip2px(context, 20)); text_size = a.getDimensionPixelSize(R.styleable.ViewfinderView_text_size, sp2px(context, 14)); a.recycle(); setOnTouchListener(this); scaleGestureDetector = new ScaleGestureDetector(context, this); gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDoubleTap(MotionEvent e) { if (zoomMaxFlag) { if (mCameraZoomListener != null) { mCameraZoomListener.onZooming(true, false, true, 1); } zoomMaxFlag = false; } else { if (mCameraZoomListener != null) { mCameraZoomListener.onZooming(true, false, false, 1); } zoomMaxFlag = true; } return true; } @Override public boolean onSingleTapUp(MotionEvent e) { // if (mCameraZoomListener != null) { // //判断点击事件是否在 // mCameraZoomListener.onZooming(false, true, false, 1); // } return false; } @Override public void onLongPress(MotionEvent e) { super.onLongPress(e); // if (mCameraZoomListener != null) { // //判断点击事件是否在 // mCameraZoomListener.onZooming(false, true, false, 1); // } } }); } public void setCameraManager(CameraManager cameraManager) { this.cameraManager = cameraManager; } @SuppressLint("DrawAllocation") @Override public void onDraw(Canvas canvas) { if (cameraManager == null) { return; // not ready yet, early draw before done configuring } Rect frame = cameraManager.getFramingRect(); Rect previewFrame = cameraManager.getFramingRectInPreview(); if (frame == null || previewFrame == null) { return; } int width = canvas.getWidth(); int height = canvas.getHeight(); // Draw the exterior (i.e. outside the framing rect) darkened paint.setColor(resultBitmap != null ? resultColor : maskColor); canvas.drawRect(0, 0, width, frame.top, paint); canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint); canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint); canvas.drawRect(0, frame.bottom + 1, width, height, paint); if (resultBitmap != null) { // Draw the opaque result bitmap over the scanning rectangle paint.setAlpha(CURRENT_POINT_OPACITY); canvas.drawBitmap(resultBitmap, null, frame, paint); } else { // Draw a red "laser scanner" line through the middle to show decoding is active paint.setColor(laserColor); // paint.setAlpha(SCANNER_ALPHA[scannerAlpha]); scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length; // int middle = frame.height() / 2 + frame.top; // canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);//移除红线 float scaleX = frame.width() / (float) previewFrame.width(); float scaleY = frame.height() / (float) previewFrame.height(); List<ResultPoint> currentPossible = possibleResultPoints; List<ResultPoint> currentLast = lastPossibleResultPoints; int frameLeft = frame.left; int frameTop = frame.top; if (currentPossible.isEmpty()) { lastPossibleResultPoints = null; } else { possibleResultPoints = new ArrayList<>(5); lastPossibleResultPoints = currentPossible; paint.setAlpha(CURRENT_POINT_OPACITY); paint.setColor(resultPointColor); synchronized (currentPossible) { for (ResultPoint point : currentPossible) { canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX), frameTop + (int) (point.getY() * scaleY), POINT_SIZE, paint); } } } if (currentLast != null) { paint.setAlpha(CURRENT_POINT_OPACITY / 2); paint.setColor(resultPointColor); synchronized (currentLast) { float radius = POINT_SIZE / 2.0f; for (ResultPoint point : currentLast) { canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX), frameTop + (int) (point.getY() * scaleY), radius, paint); } } } drawRectCorner(frame, canvas); drawBitmap(frame, canvas); drawBottomText(frame, canvas); // Request another update at the animation interval, but only repaint the laser line, // not the entire viewfinder mask. // postInvalidateDelayed(ANIMATION_DELAY, // frame.left - POINT_SIZE, // frame.top - POINT_SIZE, // frame.right + POINT_SIZE, // frame.bottom + POINT_SIZE); } } public void drawViewfinder() { Bitmap resultBitmap = this.resultBitmap; this.resultBitmap = null; if (resultBitmap != null) { resultBitmap.recycle(); } invalidate(); } /** * Draw a bitmap with the result points highlighted instead of the live scanning display. * * @param barcode An image of the decoded barcode. */ public void drawResultBitmap(Bitmap barcode) { resultBitmap = barcode; invalidate(); } public void addPossibleResultPoint(ResultPoint point) { List<ResultPoint> points = possibleResultPoints; synchronized (points) { points.add(point); int size = points.size(); if (size > MAX_RESULT_POINTS) { // trim it points.subList(0, size - MAX_RESULT_POINTS / 2).clear(); } } } private ValueAnimator animator; private float x; private boolean flag = true; /*画四个角,如果通过绘制path的方式会卡*/ private void drawRectCorner(Rect frame, Canvas canvas) { //画内部rect paint.setStrokeWidth(mStrokeWidth); paint.setColor(corner_rect_length); paint.setStyle(Paint.Style.STROKE); canvas.drawRect(frame, paint); paint.reset(); //画四角 paint.setStyle(Paint.Style.FILL); paint.setColor(cornerColor); //左上角 canvas.drawRect(frame.left, frame.top, frame.left + cornerLength, frame.top + cornerWidth, paint); canvas.drawRect(frame.left, frame.top, frame.left + cornerWidth, frame.top + cornerLength, paint); //右上角 canvas.drawRect(frame.right - cornerLength, frame.top, frame.right, frame.top + cornerWidth, paint); canvas.drawRect(frame.right - cornerWidth, frame.top, frame.right, frame.top + cornerLength, paint); //左下角 canvas.drawRect(frame.left, frame.bottom - cornerLength, frame.left + cornerWidth, frame.bottom, paint); canvas.drawRect(frame.left, frame.bottom - cornerWidth, frame.left + cornerLength, frame.bottom, paint); //右下角 canvas.drawRect(frame.right - cornerLength, frame.bottom - cornerWidth, frame.right, frame.bottom, paint); canvas.drawRect(frame.right - cornerWidth, frame.bottom - cornerLength, frame.right, frame.bottom, paint); paint.reset(); } /*画扫描线*/ private void drawBitmap(final Rect frame, Canvas canvas) { if (animator == null) { animator = ValueAnimator.ofFloat(frame.top, frame.bottom); animator.setDuration(3000); animator.setRepeatCount(ValueAnimator.INFINITE); animator.setRepeatMode(ValueAnimator.RESTART); animator.setInterpolator(new LinearInterpolator()); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { x = (Float) animation.getAnimatedValue(); postInvalidate( frame.left - POINT_SIZE, frame.top - POINT_SIZE, frame.right + POINT_SIZE, frame.bottom + POINT_SIZE); } }); } if (flag) { animator.start(); flag = false; } tmpRect.set(frame.left, (int) x, frame.right, (int) x + 40); canvas.drawBitmap(tmpBitmap, null, tmpRect, paint); } private Rect tmpRect1 = new Rect(); private void drawBottomText(Rect frame, Canvas canvas) { if (textVisible) { paint.setTextSize(text_size); paint.setColor(textColor); paint.setTextAlign(Paint.Align.CENTER); paint.getTextBounds(description, 0, description.length(), tmpRect1); canvas.drawText(description, 0, description.length(), frame.centerX(), frame.centerY() + frame.height() / 2 + textMargin + tmpRect1.height(), paint); paint.reset(); } } /** * 将dip或dp值转换为px值,保证尺寸大小不变 */ public static int dip2px(Context context, float dipValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dipValue * scale + 0.5f); } /** * 将sp值转换为px值,保证文字大小不变 */ public static int sp2px(Context context, float spValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } @Override public boolean onScale(ScaleGestureDetector detector) { if (mCameraZoomListener != null) { mCameraZoomListener.onZooming(false, false, false, detector.getScaleFactor()); } return true; } @Override public boolean onScaleBegin(ScaleGestureDetector detector) { return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { } @Override public boolean onTouch(View v, MotionEvent event) { scaleGestureDetector.onTouchEvent(event); gestureDetector.onTouchEvent(event); return true; } public interface CameraZoomListener { /** * @param isDouble 是否双击 * @param isMax //是否移动到最大 * @param scaleValue //缩小放大的值 * @param isSingle //是否单击 */ void onZooming(boolean isDouble, boolean isSingle, boolean isMax, float scaleValue); } private CameraZoomListener mCameraZoomListener; public void setCameraZoomListener(CameraZoomListener mCameraZoomListener) { this.mCameraZoomListener = mCameraZoomListener; } }
如果生产环境的话,可以根据具体的产品修改界面
项目地址:https://github.com/ASCN-BJ/ZXingLibrary
相关文章推荐
- 高仿仿微信界面二维码扫描效果 之 Android 基于google Zxing实现二维码、条形码扫描
- Android 二维码扫描基于Google Zxing(仿微信)
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果