精华内容
参与话题
问答
  • 完整的二维码扫描解决方案

    千次阅读 2018-04-24 12:06:40
    现在的应用都有自己的二维码扫描工具,最近接手的项目也不例外,集成了ZXing二维码扫描,但是用起来却实在差强人意,后来看到QrCodeReaderView效果不错,识别速度上有明显提高,而且使用也非常简单,于是便迅速集成...

    现在的应用都有自己的二维码扫描工具,最近接手的项目也不例外,集成了ZXing二维码扫描,但是用起来却实在差强人意,后来看到QrCodeReaderView效果不错,识别速度上有明显提高,而且使用也非常简单,于是便迅速集成,唯独对扫码界面定制不够,于是再对ZXing中的自定义View修改,便得到较为完整的二维码扫描解决方案:

    • 二维码扫描框
    • 自定义属性
    • 颜色资源
    • 图片资源
    • 生成二维码
    • 二维码生成工具类

    二维码扫描框

    /*
     * Copyright (C) 2008 ZXing authors
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.apple.widget;
    
    import android.content.Context;
    import android.content.res.Resources;
    import android.content.res.TypedArray;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Rect;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.view.View;
    
    import com.apple.lottle.R;
    
    import java.util.Collection;
    import java.util.HashSet;
    
    /**
     * 自定义组件实现,扫描功能
     */
    public final class ViewfinderView extends View {
    
        private static final long ANIMATION_DELAY = 100L;
        private static final int OPAQUE = 0xFF;
    
        public static int FRAME_WIDTH = -1;
        public static int FRAME_HEIGHT = -1;
        public static int FRAME_MARGINTOP = -1;
        private final Paint paint;
        private final int maskColor;
        private final int resultColor;
        private final int resultPointColor;
        private int screenWidthPx;
        private int screenHeightPx;
        private Bitmap resultBitmap;
        private Collection<ResultPoint> possibleResultPoints;
        private Collection<ResultPoint> lastPossibleResultPoints;
        private Rect framingRect;
        //扫描区域提示文本
        private String labelText;
        //扫描区域提示文本颜色
        private int labelTextColor;
        private float labelTextSize;
        // 扫描线移动的y
        private int scanLineTop;
        // 扫描线移动速度
        private int SCAN_VELOCITY;
        // 扫描线
        private Bitmap scanLight;
        // 是否展示小圆点
        private boolean isCircle;
        // 扫描框边角颜色
        private int innercornercolor;
        // 扫描框边角长度
        private int innercornerlength;
        // 扫描框边角宽度
        private int innercornerwidth;
    
        public ViewfinderView(Context context) {
            this(context, null);
        }
    
        public ViewfinderView(Context context, AttributeSet attrs) {
            this(context, attrs, -1);
        }
    
        public ViewfinderView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            paint = new Paint();
            Resources resources = getResources();
            maskColor = resources.getColor(R.color.viewfinder_mask);
            resultColor = resources.getColor(R.color.result_view);
            resultPointColor = resources.getColor(R.color.possible_result_points);
            possibleResultPoints = new HashSet<>(5);
            screenWidthPx = context.getResources().getDisplayMetrics().widthPixels;
            screenHeightPx = context.getResources().getDisplayMetrics().heightPixels;
            scanLight = BitmapFactory.decodeResource(resources, R.drawable.scan_light);
    
            initInnerRect(context, attrs);
        }
    
        /**
         * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
         */
        public static int dip2px(Context context, float dpValue) {
            final float scale = context.getResources().getDisplayMetrics().density;
            return (int) (dpValue * scale + 0.5f);
        }
    
        /**
         * 初始化内部框的大小
         *
         * @param context
         * @param attrs
         */
        private void initInnerRect(Context context, AttributeSet attrs) {
            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ViewfinderView);
            // 扫描框距离顶部
            float innerMarginTop = ta.getDimension(R.styleable.ViewfinderView_inner_margintop, -1);
            if (innerMarginTop != -1) {
                FRAME_MARGINTOP = (int) innerMarginTop;
            }
            // 扫描框的宽度
            FRAME_WIDTH = (int) ta.getDimension(R.styleable.ViewfinderView_inner_width, screenWidthPx * 2 / 3);
            // 扫描框的高度
            FRAME_HEIGHT = (int) ta.getDimension(R.styleable.ViewfinderView_inner_height, screenWidthPx * 2 / 3);
            // 扫描框边角颜色
            innercornercolor = ta.getColor(R.styleable.ViewfinderView_inner_corner_color, Color.parseColor("#45DDDD"));
            // 扫描框边角长度
            innercornerlength = (int) ta.getDimension(R.styleable.ViewfinderView_inner_corner_length, 65);
            // 扫描框边角宽度
            innercornerwidth = (int) ta.getDimension(R.styleable.ViewfinderView_inner_corner_width, 15);
            labelTextColor = ta.getColor(R.styleable.ViewfinderView_label_text_color, 0x90FFFFFF);
            labelText = ta.getString(R.styleable.ViewfinderView_label_text);
            labelTextSize = ta.getFloat(R.styleable.ViewfinderView_label_text_size, 36);
            // 扫描bitmap
            Drawable drawable = ta.getDrawable(R.styleable.ViewfinderView_inner_scan_bitmap);
            if (drawable != null) {
            }
            // 扫描控件
            scanLight = BitmapFactory.decodeResource(getResources(), ta.getResourceId(R.styleable.ViewfinderView_inner_scan_bitmap, R.drawable.scan_light));
            // 扫描速度
            SCAN_VELOCITY = ta.getInt(R.styleable.ViewfinderView_inner_scan_speed, 5);
            isCircle = ta.getBoolean(R.styleable.ViewfinderView_inner_scan_iscircle, true);
            ta.recycle();
        }
    
        /**
         * Calculates the framing rect which the UI should draw to show the user where to place the
         * barcode. This target helps with alignment as well as forces the user to hold the device
         * far enough away to ensure the image will be in focus.
         *
         * @return The rectangle to draw on screen in window coordinates.
         */
        public Rect getFramingRect() {
            try {
                int leftOffset = (screenWidthPx - FRAME_WIDTH) / 2;
    
                int topOffset;
                if (FRAME_MARGINTOP != -1) {
                    topOffset = FRAME_MARGINTOP;
                } else {
                    topOffset = (screenHeightPx - FRAME_HEIGHT) / 3;
                }
                framingRect = new Rect(leftOffset, topOffset, leftOffset + FRAME_WIDTH, topOffset + FRAME_HEIGHT);
                // }
                return framingRect;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        @Override
        public void onDraw(Canvas canvas) {
            Rect frame = getFramingRect();
            if (frame == 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(OPAQUE);
                canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
            } else {
                drawFrameBounds(canvas, frame);
                drawScanLight(canvas, frame);
                //绘制提示信息
                drawTextInfo(canvas, frame);
                Collection<ResultPoint> currentPossible = possibleResultPoints;
                Collection<ResultPoint> currentLast = lastPossibleResultPoints;
                if (currentPossible.isEmpty()) {
                    lastPossibleResultPoints = null;
                } else {
                    possibleResultPoints = new HashSet<ResultPoint>(5);
                    lastPossibleResultPoints = currentPossible;
                    paint.setAlpha(OPAQUE);
                    paint.setColor(resultPointColor);
    
                    if (isCircle) {
                        for (ResultPoint point : currentPossible) {
                            canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 6.0f, paint);
                        }
                    }
                }
                if (currentLast != null) {
                    paint.setAlpha(OPAQUE / 2);
                    paint.setColor(resultPointColor);
    
                    if (isCircle) {
                        for (ResultPoint point : currentLast) {
                            canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 3.0f, paint);
                        }
                    }
                }
                postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top, frame.right, frame.bottom);
            }
        }
    
        /**
         * 绘制移动扫描线
         *
         * @param canvas
         * @param frame
         */
        private void drawScanLight(Canvas canvas, Rect frame) {
            if (scanLineTop == 0) {
                scanLineTop = frame.top;
            }
            if (scanLineTop >= frame.bottom - 30) {
                scanLineTop = frame.top;
            } else {
                scanLineTop += SCAN_VELOCITY;
            }
            Rect scanRect = new Rect(frame.left, scanLineTop, frame.right,
                    scanLineTop + 30);
            canvas.drawBitmap(scanLight, null, scanRect, paint);
        }
    
        /**
         * 绘制取景框边框
         *
         * @param canvas
         * @param frame
         */
        private void drawFrameBounds(Canvas canvas, Rect frame) {
            paint.setColor(innercornercolor);
            paint.setStyle(Paint.Style.FILL);
            int corWidth = innercornerwidth;
            int corLength = innercornerlength;
            // 左上角
            canvas.drawRect(frame.left, frame.top, frame.left + corWidth, frame.top + corLength, paint);
            canvas.drawRect(frame.left, frame.top, frame.left + corLength, frame.top + corWidth, paint);
            // 右上角
            canvas.drawRect(frame.right - corWidth, frame.top, frame.right, frame.top + corLength, paint);
            canvas.drawRect(frame.right - corLength, frame.top, frame.right, frame.top + corWidth, paint);
            // 左下角
            canvas.drawRect(frame.left, frame.bottom - corLength, frame.left + corWidth, frame.bottom, paint);
            canvas.drawRect(frame.left, frame.bottom - corWidth, frame.left + corLength, frame.bottom, paint);
            // 右下角
            canvas.drawRect(frame.right - corWidth, frame.bottom - corLength, frame.right, frame.bottom, paint);
            canvas.drawRect(frame.right - corLength, frame.bottom - corWidth, frame.right, frame.bottom, paint);
        }
    
        //绘制文本
        private void drawTextInfo(Canvas canvas, Rect frame) {
            paint.setColor(labelTextColor);
            paint.setTextSize(labelTextSize);
            paint.setTextAlign(Paint.Align.CENTER);
            canvas.drawText(labelText, frame.left + frame.width() / 2, frame.bottom + innercornerlength * 1.5f, paint);
        }
    
        public void drawViewfinder() {
            resultBitmap = null;
            invalidate();
        }
    
        public void addPossibleResultPoint(ResultPoint point) {
            possibleResultPoints.add(point);
        }
    }

    自定义属性

    <declare-styleable name="ViewfinderView">
        <attr name="inner_width" format="dimension"/>
        <attr name="inner_height" format="dimension"/>
        <attr name="inner_margintop" format="dimension" />
        <attr name="inner_corner_color" format="color" />
        <attr name="inner_corner_length" format="dimension" />
        <attr name="inner_corner_width" format="dimension" />
        <attr name="inner_scan_bitmap" format="reference" />
        <attr name="inner_scan_speed" format="integer" />
        <attr name="inner_scan_iscircle" format="boolean" />
        <attr name="label_text_color" format="color"/>
        <attr name="label_text" format="string"/>
        <attr name="label_text_size" format="integer"/>
    </declare-styleable>

    颜色资源

    <!-- 二维码扫描 -->
    <color name="viewfinder_mask">#60000000</color>
    <color name="result_view">#b0000000</color>
    <color name="possible_result_points">#c0ffff00</color>

    图片资源

    扫描线

    生成二维码

    Bitmap bitmap = QrCodeUtils.createImage(jsonObject.toString(), 400, 400, null);
    imageView.setImageBitmap(bitmap);

    二维码生成工具类

    package com.apple.utils;
    
    import android.app.Activity;
    import android.content.Context;
    import android.content.res.AssetFileDescriptor;
    import android.graphics.Bitmap;
    import android.graphics.Matrix;
    import android.media.AudioManager;
    import android.media.MediaPlayer;
    import android.os.Vibrator;
    import android.text.TextUtils;
    
    import com.google.zxing.BarcodeFormat;
    import com.google.zxing.EncodeHintType;
    import com.google.zxing.WriterException;
    import com.google.zxing.common.BitMatrix;
    import com.google.zxing.qrcode.QRCodeWriter;
    import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
    import com.zhcx.doctorgh.R;
    
    import java.io.IOException;
    import java.util.Hashtable;
    
    /**
     * 二维码扫描工具类
     */
    public class QrCodeUtils {
    
        /**
         * 生成二维码图片
         *
         * @param text
         * @param w
         * @param h
         * @param logo
         * @return
         */
        public static Bitmap createImage(String text, int w, int h, Bitmap logo) {
            if (TextUtils.isEmpty(text)) {
                return null;
            }
            try {
                Bitmap scaleLogo = getScaleLogo(logo, w, h);
    
                int offsetX = w / 2;
                int offsetY = h / 2;
    
                int scaleWidth = 0;
                int scaleHeight = 0;
                if (scaleLogo != null) {
                    scaleWidth = scaleLogo.getWidth();
                    scaleHeight = scaleLogo.getHeight();
                    offsetX = (w - scaleWidth) / 2;
                    offsetY = (h - scaleHeight) / 2;
                }
                Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
                hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
                //容错级别
                hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
                //设置空白边距的宽度
                hints.put(EncodeHintType.MARGIN, 0);
                BitMatrix bitMatrix = new QRCodeWriter().encode(text, BarcodeFormat.QR_CODE, w, h, hints);
                int[] pixels = new int[w * h];
                for (int y = 0; y < h; y++) {
                    for (int x = 0; x < w; x++) {
                        if (x >= offsetX && x < offsetX + scaleWidth && y >= offsetY && y < offsetY + scaleHeight) {
                            int pixel = scaleLogo.getPixel(x - offsetX, y - offsetY);
                            if (pixel == 0) {
                                if (bitMatrix.get(x, y)) {
                                    pixel = 0xff000000;
                                } else {
                                    pixel = 0xffffffff;
                                }
                            }
                            pixels[y * w + x] = pixel;
                        } else {
                            if (bitMatrix.get(x, y)) {
                                pixels[y * w + x] = 0xff000000;
                            } else {
                                pixels[y * w + x] = 0xffffffff;
                            }
                        }
                    }
                }
                Bitmap bitmap = Bitmap.createBitmap(w, h,
                        Bitmap.Config.ARGB_8888);
                bitmap.setPixels(pixels, 0, w, 0, 0, w, h);
                return bitmap;
            } catch (WriterException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        private static Bitmap getScaleLogo(Bitmap logo, int w, int h) {
            if (logo == null) return null;
            Matrix matrix = new Matrix();
            float scaleFactor = Math.min(w * 1.0f / 5 / logo.getWidth(), h * 1.0f / 5 / logo.getHeight());
            matrix.postScale(scaleFactor, scaleFactor);
            Bitmap result = Bitmap.createBitmap(logo, 0, 0, logo.getWidth(), logo.getHeight(), matrix, true);
            return result;
        }
    }

    展开全文
  • 二维码扫描实现

    千次阅读 2017-05-03 16:06:24
    二维码扫描实现二维码原理 三个回形大方块,是为了给相机定位的;中间的黑白块,黑块代表1,白块代表0,八个一组,组成二进制信息。二维码原理 这个小视频,简单的介绍了下二维码iOS简单的实现二维码扫描知道二维码...

    二维码扫描实现

    二维码原理

    二维码扫描过程示意图
    三个回形大方块,是为了给相机定位的;中间的黑白块,黑块代表1,白块代表0,八个一组,组成二进制信息。二维码原理 这个小视频,简单的介绍了下二维码

    iOS简单的实现二维码扫描

    知道二维码的原理后,就可以简单的实现一下二维码扫描。在iOS7之前,二维码扫描大多数采用的是zxing这个第三方库ZXing和zbar,但是使用的时候会遇到很多坑,之后,苹果自己实现了二维码的扫描方法,扫描迅速识别率高,现在看看如何简单的实现。

    实现步骤

    //1.获取摄像设备
    let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)!
    //2.创建输入流
    var input: AVCaptureDeviceInput?
    do {
        input = try AVCaptureDeviceInput.init(device: device)
    } catch  {
    
    }
    //3.判断输入流是否可用
    guard input != nil else {
         return
     }
     //4.创建输入流
     let output = AVCaptureMetadataOutput.init()
     //5.设置代理,在主线程里刷新,注意此时self要签AVCaptureMetadataOutputObjectsDelegate协议
     output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
     //6.创建连接对象,添加输入输出流
     session = AVCaptureSession.init()
     //高质量采集率
     session?.canSetSessionPreset(AVCaptureSessionPresetHigh)
     if (session?.canAddInput(input))! {
         session?.addInput(input)
     }
     if (session?.canAddOutput(output))! {
         session?.addOutput(output)
     }
     //7.设置扫码支持的编码格式
     output.metadataObjectTypes = [AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code]
     //8.设置扫描范围(每一个取值0~1,以屏幕的右上角为原点,扫描的图像其实是旋转了90度)
     output.rectOfInterest = CGRect.init(x: (scanY)/APP_HEIGHT, y: (scanX)/APP_WIDTH, width: scanH/APP_HEIGHT, height: scanW/APP_WIDTH)
     //9.扫描区域大小的设置(扫描时候可以看见的背景视图,这部分也可以自定义,显示自己想要的布局)
     let layer = AVCaptureVideoPreviewLayer.init(session: session)
     layer.frame = self.view.frame
     layer.session = session
     layer.videoGravity = AVLayerVideoGravityResizeAspectFill
     self.view.layer.insertSublayer(layer, at: 0)
     //10.开始捕获对象
     session?.startRunning()
    
     //11.实现代理方法          
     extension ViewController: AVCaptureMetadataOutputObjectsDelegate {
    //扫描结果
    func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
        if metadataObjects.count > 0 {
            //获取到信息后停止扫描
            session?.stopRunning()
            let metaDataObject = metadataObjects.first as! AVMetadataMachineReadableCodeObject
            //输出扫描字符串
            print("stringValue:\(metaDataObject.stringValue)")
            //移除扫描视图
            let layer = self.view.layer.sublayers?.first as! AVCaptureVideoPreviewLayer
            layer.removeFromSuperlayer()
        }
    }
    

    }

    自定义二维码扫描

    上面的方式只能初步学习使用,而实际上的应用开发需要像微信或者支付宝那样的样式,所以需要进一步的开发出我们想要的样式。

    扫描区域背景

    观察微信的扫描界面发现,除了中间的扫描框之外,周围有一层半透明的视图,所以首先想到的办法是:去除中间的扫描区域,将图像分为四个部分,设置统一的颜色和透明度,添加进view。这种方法当然能实现想要的效果,但是明显不够优雅,所以考虑另一种方法,将当前view的某一区域设置为透明,这样就可以达到想要的效果了。
    UIGraphicsGetCurrentContext()对象有一个clear(_ rect: CGRect)方法,将指定的区域设置为透明,UIGraphicsGetCurrentContext方法获取上下文,只有在draw(_ rect: CGRect)方法中才能获取到。

    let context = UIGraphicsGetCurrentContext()
        if let context = context {
            context.setFillColor(red: 40/255.0, green: 40/255.0, blue: 40/255.0, alpha: 0.5)
            context.fill(CGRect.init(x: 0, y: 0, width: self.frame.width, height: self.frame.size.height))
            context.clear(CGRect.init(x: 100, y: 0, width: 50, height: 50))
        }
    

    扫描线

    扫描线使用UIView的动画就可以实现了:

    //扫描线动画效果
    func scanLineAnimation() {
        UIView.animate(withDuration: 3, delay: 0, options: .repeat, animations: {
            self.lineView.frame.origin.y += (self.scanH - 1)
        }) { (finished) in
        }
    }
    

    四个角的边框

    在扫描框上添加白色的边框,用来突出扫描框:

    //添加白色边框
    func addWhiteRect(context: CGContext, rect: CGRect) {
        context.stroke(rect)
        context.setStrokeColor(red: 1, green: 1, blue: 1, alpha: 1)
        context.setLineWidth(0.8)
        context.addRect(rect)
        context.strokePath()
    }
    

    添加四个角的边框,以顶点为起点,分别向两边延伸一段距离,然后连线,填充颜色:

    //扫描框的实现
    func addCornerLine(context: CGContext, rect: CGRect) {
        //画四个边角
        context.setLineWidth(2)
        context.setStrokeColor(red: 83/255.0, green: 239/255.0, blue: 111/255.0, alpha: 1)
    
        //左上角
        let pointsTopLeftA = [
            CGPoint.init(x: rect.origin.x, y: rect.origin.y),
            CGPoint.init(x: rect.origin.x + borderWidth, y: rect.origin.y)
        ]
        let pointsTopLeftB: Array = [
            CGPoint.init(x: rect.origin.x, y: rect.origin.y),
            CGPoint.init(x: rect.origin.x, y: rect.origin.y + borderWidth)
        ]
        addLine(pointA: pointsTopLeftA, pointB: pointsTopLeftB, context: context)
    
        //左下角
        let pointsBottomLeftA = [
            CGPoint.init(x: rect.origin.x, y: rect.maxY),
            CGPoint.init(x: rect.origin.x + borderWidth, y: rect.maxY)
        ]
        let pointsBottomLeftB = [
            CGPoint.init(x: rect.origin.x, y: rect.maxY),
            CGPoint.init(x: rect.origin.x, y: rect.maxY - borderWidth)
        ]
        addLine(pointA: pointsBottomLeftA, pointB: pointsBottomLeftB, context: context)
    
        //右上角
        let pointsTopRightA = [
            CGPoint.init(x: rect.maxX, y: rect.origin.y),
            CGPoint.init(x: rect.maxX - borderWidth, y: rect.origin.y)
        ]
        let pointsTopRightB = [
            CGPoint.init(x: rect.maxX, y: rect.origin.y),
            CGPoint.init(x: rect.maxX, y: rect.origin.y + borderWidth)
        ]
        addLine(pointA: pointsTopRightA, pointB: pointsTopRightB, context: context)
    
        //右下角
        let pointsBottomRightA = [
            CGPoint.init(x: rect.maxX, y: rect.maxY),
            CGPoint.init(x: rect.maxX - borderWidth, y: rect.maxY)
        ]
        let pointsBottomRightB = [
            CGPoint.init(x: rect.maxX, y: rect.maxY),
            CGPoint.init(x: rect.maxX, y: rect.maxY - borderWidth)
        ]
        addLine(pointA: pointsBottomRightA, pointB: pointsBottomRightB, context: context)
    
        context.strokePath()
    }
    
    private func addLine(pointA: [CGPoint], pointB: [CGPoint], context: CGContext) {
        context.addLines(between: pointA)
        context.addLines(between: pointB)
    }
    

    横竖屏

    如果当前页面要适应横竖屏的情况的话,目前还没想到什么比较好的方式,因为如果在背景图上采用autosizing的画,屏幕旋转后扫描框和扫描线会发生形变,跟预期不太一致,所以只能设置屏幕不能旋转

    识别相册中的二维码

    主要用到CIDetecor,将图片转成CIImage,通过features(in image: CIImage) -> [CIFeature]方法获取CIFeature的数组对象

    func readQRCode(qrImage: UIImage?, _ handle: @escaping completionHandle) {
        guard let qrImage = qrImage else {
            return
        }
        let context = CIContext.init(options: nil)
        let detector = CIDetector.init(ofType: CIDetectorTypeQRCode, context: context, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh])
        let image = CIImage.init(cgImage: qrImage.cgImage!)
        let features = detector?.features(in: image)
        let feature: CIQRCodeFeature = features?.first as! CIQRCodeFeature
        let result = feature.messageString
        handle(result!)
    }
    

    生成二维码

    首先将需要的信息转成data,然后使用CIFilter输出图像,再将CIImage转换成UIImage,并放大显示

    func createCodeImage(string: String) -> UIImage? {
        guard !string.isEmpty else {
            return nil
        }
        //二维码滤镜
        let filter = CIFilter.init(name: "CIQRCodeGenerator")
        //恢复滤镜的默认属性
        filter?.setDefaults()
        //将字符串转换成NSData
        let data = string.data(using: String.Encoding.utf8)
        //通过KVC设置滤镜inputMessage数据
        filter?.setValue(data, forKey: "inputMessage")
        //获得滤镜输出的图像
        let outImage = filter?.outputImage
        //将CIImage转换成UIImage,并放大显示
        let image = createNonInterpolatedUIImageFormCIImage(image: outImage!, size: 200)
        return image
    }
    
    ///改变二维码大小
    private func createNonInterpolatedUIImageFormCIImage(image: CIImage, size: CGFloat) -> UIImage? {
        let extent = image.extent.integral
        let scale = min(size/extent.width, size/extent.height)
        //创建bitmap
        let width: size_t = size_t(extent.width * scale)
        let height: size_t = size_t(extent.height * scale)
        let cs = CGColorSpaceCreateDeviceGray()
        let bitmapRef = CGContext.init(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 0, space: cs, bitmapInfo: CGImageAlphaInfo.none.rawValue)
        let context = CIContext.init(options: nil)
        let bitmapImage = context.createCGImage(image, from: extent)
        bitmapRef?.interpolationQuality = .high
        bitmapRef?.scaleBy(x: scale, y: scale)
        bitmapRef?.draw(bitmapImage!, in: extent)
        //保存bitmap到图片
        let scaledImage = bitmapRef!.makeImage()
        return UIImage.init(cgImage:scaledImage!)
    }
    

    小结

    将二维码的需求分割后,就可以根据需求自定义想要的样式了,这里是我封装的一个二维码的库XELQRCode

    参考链接

    再见ZXing 使用系统原生代码处理QRCode

    展开全文
  • 一分钟实现扫描二维码功能

    千次阅读 2018-07-16 11:08:17
    二维码扫描的功能在很多APP上都会出现,较为常用的第三方库是zxing,zxing很强大,但是有时候我们并不需要那么复杂的功能,只需要简单的扫描、生成以及处理扫描结果,一般都是通过重写几个类来实现项目需求。...

    前言

    二维码扫描的功能在很多APP上都会出现,较为常用的第三方库是zxing,zxing很强大,但是有时候我们并不需要那么复杂的功能,只需要简单的扫描、生成以及处理扫描结果,一般都是通过重写几个类来实现项目需求。我开发了几个项目都用到了扫描二维码的功能,第一个项目,写了个完整的功能,之后的项目都是从第一个项目里面复制代码的,几次之后,觉得有点繁琐,所以就单独封装成一个项目,传到jcenter上,以后再遇到扫描二维码的功能,只需要在gradle导入,即可实现快速开发了。

    下载APP体验
    img

    导入

    compile 'com.hebin:hxbrzxing:1.0.1'

    使用

    导入库之后,就可以使用扫描二维码的功能了,只需要新建一个activity,然后继承CaptureActivity即可。

    class MainActivity : CaptureActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
        }
    }
    

    自定义使用

    如果只是简单的继承,那么得到的效果图如下:
    这里写图片描述

    这里提供了几个可以自定义的属性;

    1. 标题栏自定义

    class MainActivity : CaptureActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            val view = LayoutInflater.from(this@MainActivity).inflate(R.layout.simple_title,null)
            setTitleView(view)
        }
    }
    

    2. 背景图片自定义

    class MainActivity : CaptureActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setBackground(R.mipmap.ic_launcher)
        }
    }
    

    3. 提示文字自定义

    class MainActivity : CaptureActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setTipText("请扫描二维码")
        }
    }

    4. 附加功能

    class MainActivity : CaptureActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            val view= LayoutInflater.from(this@MainActivity).inflate(R.layout.simple_title,null)
            // 打开相册,识别图片二维码
            view.tvTitle.setOnClickListener { openPhoto() }
            // 打开闪光灯
            view.tvTitle.setOnClickListener { openLight() }
            setTitleView(view)
        }
    
    }

    扫描结果处理

    继承CaptureActivity.ResultListener,并且在oncreat里面,写上setListener(this)即可实现监听,然后在onResult里面做逻辑处理。

    class MainActivity : CaptureActivity(), CaptureActivity.ResultListener {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setListener(this)
        }
    
        override fun onResult(result: String) {
            if (result.contains("http")) {
                Toast.makeText(this@MainActivity, "跳转到网页", Toast.LENGTH_SHORT).show()
            } else {
                Toast.makeText(this@MainActivity, "这个二维码不是网页", Toast.LENGTH_SHORT).show()
            }
        }
    }

    后话

    至此,只要通过简单的几行代码就实现扫描二维码的功能,而且这个扫描二维码的功能,是支持连续扫描的,不需要退出重新进入即可再次扫描。项目源码已经传到github上了。

     https://github.com/Hebin320/Zxing
    展开全文
  • H5+ 二维码扫描功能

    万次阅读 2017-06-19 12:56:33
    二维码在生活中的使用越来越广泛,APP开发中,也越来越多的需求需要用到二维码的扫描功能,以下就针对h5+的二维码扫描功能做一些简单的介绍; 1. var bc = new plus.barcode.Barcode( id, filters, styles ); ...


    二维码在生活中的使用越来越广泛,APP开发中,也越来越多的需求需要用到二维码的扫描功能,以下就针对h5+的二维码扫描功能做一些简单的介绍;

    1. var bc = new plus.barcode.Barcode( id, filters, styles ); 创建二维码扫描对象

    id: 条码识别控件在Webview窗口的DOM节点的id值

    filters: 要识别的条码类型过滤器,为条码类型常量数组,条码识别引擎可支持多种二维码及一维码类型,默认情况支持QR、EAN13、EAN8三种类型。

    styles :

    - frameColor: )扫描框颜色

    - scanbarColor:扫描条颜色

    - background: (String 类型 )条码识别控件背景颜色


    2. bc.start( options ); 开始条码识别

    options:条码识别控件扫描条码的参数

    - conserve: 是否保存成功扫描到的条码数据时的截图

    - filename: 保存成功扫描到的条码数据时的图片路径

    - vibrate:成功扫描到条码数据时是否需要震动提醒

    - sound:成功扫描到条码数据时播放的提示音类型 可取值: “none” - 不播放提示音; “default” 播放默认提示音(5+引擎内置)


    3. bc.cancel(); 结束条码识别


    - bc.close(); 关闭条码识别控件,释放控件占用系统资源,调用close方法后控件对象将不可使用。

    - bc.setFlash( true); 是否开启闪光灯

    - 条码识别成功回调
    bc.onmarked = function ( type, code, file ) {

    }

    - type:识别到的条码类型 Number类型的值

    - code:识别到的条码数据 从条码图片中扫描出的数据内容,字符串采用UTF8编码格式。

    - file:识别到的条码图片文件路径


    7. 条码识别失败 回调函数
    bt.onerror = function ( error ) {

    }

    常见问题:
    1.二维码扫描成功之后,当再次返回到扫描界面的时候,不能重新进行扫描;
    原因:扫描成功之后,扫描界面已经变成了扫描成功的状态,当你再次但回到这个界面的时候,依然记录的是之前扫描成功的状态,所以不能再次进行扫描;
    解决办法:在扫描成功之后强制刷新界面;

    var bc = new plus.barcode.Barcode('barcode');
    bc.setFlash(false);
    bc.onmarked=function(type, result){//扫描成功
        plus.webview.show('barcodeSucceed','pop-in',100);
        mui.fire(plus.webview.getWebviewById('barcodeSucceed'),'showResult'{result:result});
        window.setTimeout(function(){
                plus.webview.currentWebview().reload();
        },1000);
    }
    bc.start();


    二维码扫描Demo 包含常见问题的解决办法下载

    展开全文
  • 二维码扫描的一些代码

    千次阅读 2018-04-16 18:46:11
    // 扫描、生成二维码 // 扫描按钮布局文件 &amp;lt;Relativelayout android:id=&quot;@+id/ll_title_address&quot; android:layout_width=&quot;match_parent&quot; android:layout_...
  • 实现网站二维码扫描登录

    万次阅读 多人点赞 2014-03-31 10:33:50
    总体来看,扫描二维码实现网站的登录并不是太困难的事情。首先来看一下二维码登录的整体架构: 在整个架构中,主要包含了几个模块:手机App、浏览器、Web服务器以及存储服务(session服务)。整个方
  • 二维码如何判断已被扫描

    万次阅读 2018-06-15 14:20:34
    前端界面如何检测二维码扫描或未被扫描而提醒“该二维码已过期请重试”?1、前端点击生成二维码按钮或点击相关链接2、该网页生成唯一个id的二维码,关闭该页面或每次刷新后都会改变。3、浏览器把这个id通知给...
  • Android 扫描二维码(Scan Kit) 华为统一扫码服务(Scan Kit)能够提供专业的二维码与条形码扫描与解析能力,通过集成Scan Kit,帮助应用快速构建扫码功能。 统一扫码服务的功能 全面的码识别能力:Scan Kit支持...
  • 微信扫描二维码获取用户的信息(网页授权) 文章目录微信扫描二维码获取用户的信息(网页授权)一、需求解说二、步骤准备1.内网穿透1.1内网穿透工具natapp1.2启动natapp2.微信公众号测试号2.1申请测试号2.2接口配置...
  • 了解二维码这个东西还是从微信中,当时微信推出二维码扫描功能,自己感觉挺新颖的,从一张图片中扫一下竟然能直接加好友,不可思议啊,那时候还不了解二维码,呵呵,然后做项目的时候,老板说要加上二维码扫描功能,...
  • 了解二维码这个东西还是从微信中,当时微信推出二维码扫描功能,自己感觉挺新颖的,从一张图片中扫一下竟然能直接加好友,不可思议啊,那时候还不了解二维码,呵呵,然后做项目的时候,老板说要加上二维码扫描功能,...
  • 条形码、二维码扫描、生成Demo 完整源码

    万次下载 热门讨论 2012-08-05 12:58:49
    使用Google ZXing开源项目制作的条形码、二维码的生成、扫描Demo
  • Android二维码扫描Demo 程序源码

    万次下载 热门讨论 2013-08-08 08:24:25
    此为示例代码,详细讲解请参考: http://blog.csdn.net/guolin_blog/article/details/9526247
  • 基于MUI框架的使用HTML5+实现的二维码扫描功能

    万次阅读 多人点赞 2017-02-09 09:24:17
    Barcode的一个实现案例 ... Barcode模块管理条码扫描,提供常见的条码(二维码及一维码)的扫描识别功能,可调用设备的摄像头对条码图片扫描进行数据输入。通过plus.barcode可获取条码码管理对象。博客有更新,修...
  • windows桌面二维码扫描器,手机不在的时候也能扫二维码了!CTFer做题必备!屏幕上的二维码直接扫,还用截什么图,还用保存什么图片,工具什么的一个就够了!
  • 二维码扫描功能,查了许多资料,感觉都不是太理想,然后就自己从新写一个喽
  • Qt实现二维码扫描

    千次下载 热门讨论 2013-11-22 15:56:50
    Qt实现的一个二维码扫描器,利用QZXing库进行解码。 更多信息请参考:http://blog.sina.com.cn/s/blog_a6fb6cc90101gng5.html
  • android开发二维码扫描

    万次阅读 2017-08-02 19:40:57
    最近项目开发中,遇到了二维码的问题!于是就去Google,搜索结果提及最多的就是ZXing了!当然这也是Google推荐的! ZXingGithub地址:https://code.google.com/p/zxing/ 但是ZXing存在的问题也很明显:包太大,...
  • zxing是一个开放源码的,用Java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的接口;可以实现使用手机的内置的摄像头完成条形码和二维码扫描与解码;也可以实现条形码和二维码的编码与解码。
  • React Native之二维码扫描

    万次阅读 2016-10-22 07:56:30
    我们知道在android原生开发中,我们经常要用到二维码扫描的功能,在微信、QQ、浏览器、名片全能王、淘宝、支付宝等等软件里面,都会用到,android里面我们最常用的就是zxing,而在RN里面也有类似的组件,感谢作者...

空空如也

1 2 3 4 5 ... 20
收藏数 108,354
精华内容 43,341
关键字:

二维码扫描