android 中的二维码生成与去除白边

    现在二维码应用越来越广泛,把一个url转换为二维码图片,让后去扫描,应用太多了。现在 zxing:core 包也用的相当广泛,android studio 使用也很方便,在 gradle 中加入配置,     compile 'com.google.zxing:core:3.2.1'   

    然后调用方法
   
    public static Bitmap createQRImage(String content, int widthPix, int heightPix) {
        if (!TextUtils.isEmpty(content)) {
            try {
                //配置参数
                Map<EncodeHintType, Object> hints = new HashMap<>();
                hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
                //容错级别
                hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
                //设置空白边距的宽度
               // hints.put(EncodeHintType.MARGIN, 0); //啊啊啊

                // 图像数据转换,使用了矩阵转换
                BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, widthPix, heightPix, hints);
                int[] pixels = new int[widthPix * heightPix];
                // 下面这里按照二维码的算法,逐个生成二维码的图片,
                // 两个for循环是图片横列扫描的结果
                for (int y = 0; y < heightPix; y++) {
                    for (int x = 0; x < widthPix; x++) {
                        if (bitMatrix.get(x, y)) {
                            pixels[y * widthPix + x] = 0xff000000;
                        } else {
                            pixels[y * widthPix + x] = 0xffffffff;
                        }
                    }
                }

                // 生成二维码图片的格式,使用ARGB_8888
                Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888);
                bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix);
                return bitmap;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    content 是 http的url, widthPix 和 heightPix 是要生成bitmap 的大小。但是记住,二维码是正方形的,也就说,如果宽和高不一致时,会以小的为边长。如果要在二维码中间加入其它图片,则

     /**
     * 在二维码中间添加Logo图案
     */
    public static Bitmap addLogo(Bitmap src, Bitmap logo) {
        if (src == null) {
            return null;
        }
        if (logo == null) {
            return src;
        }
        //获取图片的宽高
        int srcWidth = src.getWidth();
        int srcHeight = src.getHeight();
        int logoWidth = logo.getWidth();
        int logoHeight = logo.getHeight();
        if (srcWidth == 0 || srcHeight == 0) {
            return null;
        }
        if (logoWidth == 0 || logoHeight == 0) {
            return src;
        }
        //logo大小为二维码整体大小的1/5
        float scaleFactor = srcWidth * 1.0f / 5 / logoWidth;
        Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
        try {
            Canvas canvas = new Canvas(bitmap);
            canvas.drawBitmap(src, 0, 0, null);
            canvas.scale(scaleFactor, scaleFactor, srcWidth / 2, srcHeight / 2);
            canvas.drawBitmap(logo, (srcWidth - logoWidth) / 2, (srcHeight - logoHeight) / 2, null);
            canvas.save(Canvas.ALL_SAVE_FLAG);
            canvas.restore();
        } catch (Exception e) {
            bitmap = null;
            e.getStackTrace();
        }
        return bitmap;
    }

    把两个图片合成一张图片。

    以上代码是去年项目中的代码,用了一年了,结果来了新需求了,就是生成二维码时四周有白色的边框,要把边框给去掉。然后就找方法吧,很快,百度出了答案,注意createQRImage()方法中注释的地方  啊啊啊  的地方,就是在这个地方,设置为0时,就可以了。运行后,然并卵,没鸟用。继续找新的方法,看了BitMatrix类似矩阵的类,发现有几个方法 getTopLeftOnBit() 和 getBottomRightOnBit() , 对外暴露了 生成二维码 的左上角和右下角的方法,既然这样,就有办法了,生成bitmap后根据坐标去截取新的图片。

    /**
     * 判断是否有白边,如果有,就把二维码给截取出来
     */
    private static Bitmap createCenterBitmap(BitMatrix bitMatrix, Bitmap bitmap) {
        try {
            int[] topLeftOnBit = bitMatrix.getTopLeftOnBit();
            int[] bottomRightOnBit = bitMatrix.getBottomRightOnBit();
            int left = topLeftOnBit[0];
            int top =  topLeftOnBit[1];
            int right = bottomRightOnBit[0];
            int bottom = bottomRightOnBit[1];
            if( left > 0 && top > 0 && left < right && top < bottom){
                int width = right - left;
                int height = bottom - top;
                return Bitmap.createBitmap(bitmap, left, top, width, height);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }
    在createQRImage()方法中最后一步获取到bitmap后,把bitmap和 bitMatrix 传进去,就可以去裁剪bitmap了,把二维码中的黑白点裁剪出来。嗯,可以了,完成新需求;但,很快,发现两个小坑。一个是 getBottomRightOnBit() 这个方法返回右下角的坐标,但坑爹的是,如果右下角恰好是白点而不是黑点的话,右下角坐标就不准了,宽度会变小,会把白色的角那一点给减去,导致拿到的right 值小了,所以截取出来的二维码右边少了一丢丢,然后扫描时怎么也扫描不出来。这个右下角是黑点还是白点是根据url值决定了,不是必现,所以这个坑有掉隐蔽,或者说用错了方法。补救措施有两种,一个是拿到 int width = right - left; int height = bottom - top; 在这里对 width  和 height 做个判断,取大值,这样就行了
    
     /**
     * 判断是否有白边,如果有,就把二维码给截取出来
     */
    private static Bitmap createCenterBitmap(BitMatrix bitMatrix, Bitmap bitmap) {
        try {
            int[] topLeftOnBit = bitMatrix.getTopLeftOnBit();
            int[] bottomRightOnBit = bitMatrix.getBottomRightOnBit();
            int left = topLeftOnBit[0];
            int top =  topLeftOnBit[1];
            int right = bottomRightOnBit[0];
            int bottom = bottomRightOnBit[1];
            if( left > 0 && top > 0 && left < right && top < bottom){
                int width = right - left;
                int height = bottom - top;
                int wh = Math.max(width, height);
                return Bitmap.createBitmap(bitmap, left, top, wh, wh);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }           
    
    另外一种方法是,BitMatrix类中getEnclosingRectangle()这个方法,能拿到左上角和宽高,既然这样,就不用自己计算了,直接用这个方法。

    private static Bitmap createCenterBitmap2(BitMatrix bitMatrix, Bitmap bitmap) {
        try {
            int[] rec = bitMatrix.getEnclosingRectangle();
            int left = rec[0];
            int top =  rec[1];
            int width = rec[2];
            int height = rec[3];
            return Bitmap.createBitmap(bitmap, left, top, width, height);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }

    后来,在网上也找到一个靠谱的方法,通过缩减矩阵的大小
     /**
     * 删除白边
     * */
    private static BitMatrix deleteWhite(BitMatrix matrix) {
        int[] rec = matrix.getEnclosingRectangle();
        int resWidth = rec[2] ;
        int resHeight = rec[3] ;
        BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
        resMatrix.clear();
        for (int i = 0; i < resWidth; i++) {
            for (int j = 0; j < resHeight; j++) {
                if (matrix.get(i + rec[0], j + rec[1]))
                    resMatrix.set(i, j);
            }
        }
        return resMatrix;
    }
    这个方法需要在 createQRImage()方法中,生成 BitMatrix bitMatrix 时调用,
    public static Bitmap createQRImage2(String content, int widthPix, int heightPix) {
        if (!TextUtils.isEmpty(content)) {
            try {
                //配置参数
                Map<EncodeHintType, Object> hints = new HashMap<>();
                hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
                //容错级别
                hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);

                // 图像数据转换,使用了矩阵转换
                BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, widthPix, heightPix, hints);
                bitMatrix = deleteWhite(bitMatrix);
                widthPix = bitMatrix.getWidth();
                heightPix = bitMatrix.getHeight();
                int[] pixels = new int[widthPix * heightPix];
                // 下面这里按照二维码的算法,逐个生成二维码的图片,
                // 两个for循环是图片横列扫描的结果
                for (int y = 0; y < heightPix; y++) {
                    for (int x = 0; x < widthPix; x++) {
                        if (bitMatrix.get(x, y)) {
                            pixels[y * widthPix + x] = 0xff000000;
                        } else {
                            pixels[y * widthPix + x] = 0xffffffff;
                        }
                    }
                }

                // 生成二维码图片的格式,使用ARGB_8888
                Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888);
                bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix);

                return bitmap;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    基本结束,但还有个小问题,就是截取图片或缩减矩阵后,bitmap比着原先传进来的值变小了一部分,这个也好解决,算出新bitmap的图的宽和原图的宽,算个比例,然后扩大,或者截取时就把图给拉伸扩大到原图大小。
    private static Bitmap createCenterBitmap2(BitMatrix bitMatrix, Bitmap bitmap) {
        try {
            int[] rec = bitMatrix.getEnclosingRectangle();
            int left = rec[0];
            int top =  rec[1];
            int width = rec[2];
            int height = rec[3];
            Matrix matrix = new Matrix();
            float scaleValue = 1.0f * bitmap.getWidth() / width ;
            matrix.postScale(scaleValue, scaleValue);// 使用后乘
            return Bitmap.createBitmap(bitmap, left, top, width, height, matrix, false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }
    关键是try catch 里最后四行代码,算出比例,然后拉伸。

    bitmap的缩放,在createQRImage2()方法最后调用,用局部变量记录传进来的宽和高,得到二维码bitmap后,调用下面方法,把三个值穿进去即可。
     private static Bitmap scaleBitmap(Bitmap origin, int newWidth, int newHeight) {
        if (origin == null) {
            return null;
        }
        int height = origin.getHeight();
        int width = origin.getWidth();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        Matrix matrix = new Matrix();
        matrix.postScale(scaleWidth, scaleHeight);// 使用后乘
        Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
        if (!origin.isRecycled()) {
            origin.recycle();
        }
        return newBM;
    }

    二维码这一块,关键还是多看看,多想想。
阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页