现在二维码应用越来越广泛,把一个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 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 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;
    }

    二维码这一块,关键还是多看看,多想想。

更多相关文章

  1. Android(安卓)APK安装过程及原理详解
  2. [Android] AsyncTask使用实例---加载网络图片
  3. Android中Intent传递对象的两种方法(Serializable,Parcelable)
  4. android中获得屏幕、视图、任务栏、状态栏的高宽以及屏幕的设置
  5. Android的两种数据存储方式分析(一)
  6. android jni介绍
  7. Android的AIDL以及挂断电话
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. Mysql Where使用列别名
  2. mysql存储过程的使用
  3. mysql组并对UNION进行排序
  4. 通过 MySQL 存储原理来分析排序和锁(转)
  5. mysqli_query中的最大查询长度。
  6. Class.forName("com.mysql.jdbc.driver")
  7. MySQL Internal - InnoDB存储引擎(行结构
  8. MySql查询脚本,每月统计活动用户。
  9. php怎么读取MYSQL数据到radio选项
  10. 计算他们自动放入全列的两列