Android 自定义二维码

Android生成二维码使用的是zxing。

1.加入依赖,或者自己选择zxing版本:Releases · zxing/zxing · GitHub

dependencies {
    ...
    implementation 'com.google.zxing:core:3.5.0'
}

2.生成普通二维码:

  • 生成二维码
  /**
     * 创建二维码位图 (支持自定义配置和自定义样式)
     *
     * @param content          字符串内容
     * @param width            位图宽度,要求>=0(单位:px)
     * @param height           位图高度,要求>=0(单位:px)
     * @param character_set    字符集/字符转码格式 (支持格式:{@link CharacterSetECI })。传null时,zxing源码默认使用 "ISO-8859-1"
     * @param error_correction 容错级别 (支持级别:{@link ErrorCorrectionLevel })。传null时,zxing源码默认使用 "L"
     * @param margin           空白边距 (可修改,要求:整型且>=0), 传null时,zxing源码默认使用"4"。
     * @param color_black      黑色色块的自定义颜色值
     * @param color_white      白色色块的自定义颜色值
     * @return
     */
    @Nullable
    public static Bitmap createQRCodeBitmap(String content, int width, int height,
                                            @Nullable String character_set, @Nullable String error_correction, @Nullable String margin,
                                            @ColorInt int color_black, @ColorInt int color_white) {

        /** 1.参数合法性判断 */
        if (TextUtils.isEmpty(content)) { // 字符串内容判空
            return null;
        }

        if (width < 0 || height < 0) { // 宽和高都需要>=0
            return null;
        }

        try {
            /** 2.设置二维码相关配置,生成BitMatrix(位矩阵)对象 */
            Hashtable<EncodeHintType, String> hints = new Hashtable<>();

            if (!TextUtils.isEmpty(character_set)) {
                hints.put(EncodeHintType.CHARACTER_SET, character_set); // 字符转码格式设置
            }

            if (!TextUtils.isEmpty(error_correction)) {
                hints.put(EncodeHintType.ERROR_CORRECTION, error_correction); // 容错级别设置
            }

            if (!TextUtils.isEmpty(margin)) {
                hints.put(EncodeHintType.MARGIN, margin); // 空白边距设置
            }
//            hints.put(EncodeHintType.QR_VERSION,"4");
            BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);

            /** 3.创建像素数组,并根据BitMatrix(位矩阵)对象为数组元素赋颜色值 */
            int[] pixels = new int[width * height];
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    if (bitMatrix.get(x, y)) {
                        pixels[y * width + x] = color_black; // 黑色色块像素设置
                    } else {
                        pixels[y * width + x] = color_white; // 白色色块像素设置
                    }
                }
            }

            /** 4.创建Bitmap对象,根据像素数组设置Bitmap每个像素点的颜色值,之后返回Bitmap对象 */
            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
            return bitmap;
        } catch (WriterException e) {
            e.printStackTrace();
        }

        return null;

    }
  • 添加二维码logo
   /**
     * 在二维码中间添加Logo图案
     */
    private 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;
        }
        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.restore();
        } catch (Exception e) {
            bitmap = null;
            e.getStackTrace();
        }
        return bitmap;
    }

但是在使用过程中,我们有时候会需要用到圆形二维码或者自定义码点大小的情况。

3.生成自定义二维码

二维码主要分为两个部分:定位点和内容区域;二维码的复杂度和二维码版本有关,在生成二维码的时候,如果不设置参数EncodeHintType.QR_VERSION,zxing是根据数据大小自动选择版本,数据越大,版本越高,像素点越多;定位点是一个7x7的方形;

这里生成自定义形状的二维码依旧使用的zxing的库获取点位数据

 Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        hints.put(EncodeHintType.MARGIN, 0);

        QRCode qrCode = Encoder.encode(content, ErrorCorrectionLevel.H, hints);

        ByteMatrix matrix = qrCode.getMatrix();//获取点位数据
        int originalWidth = matrix.getWidth();//获取宽度
        int originalHeight = matrix.getHeight();//获取高度

   

得到matrix对象之后,根据需要的图片大小和边距获取每个点的大小,再通过matrix对象为数组元素赋值和生成对应想要的图案形状,并绘制出来。

完整代码如下:

  • 生成自定义二维码
  /**
     * 自定义二维码
     *
     * @param content:二维码内容
     * @param psRandom:生成圆形码点时参与随机半径大小生成
     * @param sizes:二维码尺寸大小
     * @param colors:二维码颜色,第一位为点位颜色,第二位为背景颜色
     * @param padding:边距
     * @param anchortype:定位点类型:0:方形矩形;1:圆点矩形;2:圆形;3.4.5.6:多边形矩形
     * @param contenttype:码点类型:0:方形码点;1:圆形码点;其他为多边形码点
     * @param ratio:定位点的大小比例
     * @param contentratiod:码点的缩放比例
     * @return
     */
    public static Bitmap CreateQRCode(String content, float psRandom, int[] sizes, int[] colors, int padding, int anchortype, int contenttype, float ratio, float contentratiod) throws Exception {
        int outputWidth = sizes[0], outputHeight = sizes[1];
        int qrColor = colors[0], bgColor = colors[1];

        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        hints.put(EncodeHintType.MARGIN, 0);
        QRCode qrCode = Encoder.encode(content, ErrorCorrectionLevel.H, hints);

        ByteMatrix matrix = qrCode.getMatrix();
        int originalWidth = matrix.getWidth();//矩阵的列数
        int originalHeight = matrix.getHeight();//矩阵的行数

        outputWidth = Math.max(originalWidth, outputWidth);
        outputHeight = Math.max(originalHeight, outputHeight);

        int originalPadding = Math.min(outputWidth / (originalWidth + 2), outputHeight / (originalHeight + 2));
        padding = Math.max(padding, originalPadding);

        int cellWidth = Math.min((outputWidth - padding * 2) / originalWidth, (outputHeight - padding * 2) / originalHeight);//每一个点位的宽高

        int outputLeft = (outputWidth - cellWidth * originalWidth) / 2;
        int outputTop = (outputHeight - cellWidth * originalHeight) / 2;

        float cellMid = (float) (cellWidth * 1.0 / 2);


        double randomRange = 0.25 * psRandom;

        Paint paint = new Paint();
        paint.setColor(qrColor);
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);

        Bitmap bitmap = Bitmap.createBitmap(outputWidth, outputHeight, Bitmap.Config.ARGB_8888);
        bitmap.eraseColor(bgColor);
        Canvas canvas = new Canvas(bitmap);
        BitMatrix ignoreMatrix = new BitMatrix(originalWidth, originalHeight);
        if (anchortype == 2) {
            drawCircle2( outputWidth, outputHeight, originalWidth, originalHeight, (float) (cellWidth * 1.0 / 2), paint, canvas, qrColor, ignoreMatrix, ratio);
        }



        int cellFour = cellWidth / 4;


        //圆点可调整
        for (int y = 0; y < originalHeight; y++) {
            for (int x = 0; x < originalWidth; x++) {
                float outputY;
                float outputX;
                if (matrix.get(x, y) == 1) {
                    float r;
                    if ((x < 7 && y < 7) || (x >= originalWidth - 7 && y < 7) || (x < 7 && y >= originalHeight - 7)) {//定位点数据
                        drawAnchor2(anchortype, outputTop, outputLeft, cellWidth, paint, canvas, x, y, contentratiod);
                    } else {
                        if (contenttype == 1) {//圆形点位
                            outputY = outputTop + y * cellWidth + cellMid;//画圆点因为是要确定圆心的位置,而不是圆的左边界,所以要多加半径cellMid,来确认圆心
                            outputX = outputLeft + x * cellWidth + cellMid;
                            if (randomRange == 0) {
                                r = cellMid * contentratiod;
                            } else {
                                r = (int) ((1 + (Math.random() - 0.5) * randomRange * 2) * cellMid) * contentratiod;//生成的圆点会有随机的大小之分
                            }
                            canvas.drawCircle(outputX, outputY, r, paint);
                        } else if (contenttype == 0) {//方形点位
                            outputY = outputTop + y * cellWidth + (1.0f - contentratiod) * cellWidth / 2;
                            outputX = outputLeft + x * cellWidth + (1.0f - contentratiod) * cellWidth / 2;
                            canvas.drawRect(outputX, outputY, outputX + cellWidth * contentratiod, outputY + cellWidth * contentratiod, paint);
                        } else {//多边形
                            outputY = outputTop + y * cellWidth + (1.0f - contentratiod) * cellWidth / 2;
                            outputX = outputLeft + x * cellWidth + (1.0f - contentratiod) * cellWidth / 2;
                            double pr = 2 * Math.PI / contenttype;
                            double pr90 = Math.PI / 2;
                            Path path = new Path();
                            double r1;
                            double r2 = pr / 2 - pr90;
                            path.moveTo(outputX + cellMid, outputY);
                            path.lineTo((int) (outputX + cellMid + Math.cos(r2) * cellFour), (int) (outputY + cellMid + Math.sin(r2) * cellFour));
                            for (int indexL = 1; indexL < contenttype; indexL++) {
                                r1 = indexL * pr - pr90;
                                path.lineTo((int) (outputX + cellMid + Math.cos(r1) * cellMid* contentratiod), (int) (outputY + cellMid + Math.sin(r1) * cellMid* contentratiod));
                                r2 = indexL * pr + pr / 2 - pr90;
                                path.lineTo((int) (outputX + cellMid + Math.cos(r2) * cellFour), (int) (outputY + cellMid + Math.sin(r2) * cellFour));
                            }
                            path.close();
                            canvas.drawPath(path, paint);
                        }

                    }
                }
            }
        }

        return bitmap;
    }
  • 二维码的圆形定位点绘制
 /*绘制圆形定位点
     * cellMid:一个格子的一半大小
     * */
    private static void drawCircle2( int outputWidth, int outputHeight, int originalWidth, int originalHeight, float cellMid, Paint paint, Canvas canvas, int qrColor, BitMatrix ignoreMatrix, float ratio) {
        float rightWidth = cellMid * 2 * originalWidth;
        float rightHeight = cellMid * 2 * originalHeight;
        float outputLeft = (outputWidth - rightWidth) / 2;
        float outputTop = (outputHeight - rightHeight) / 2;
        float outputRight = outputLeft + rightWidth;
        float outputBottom = outputTop + rightHeight;
        float circleP = 7 * cellMid;//半径,cellMid用的是一个点位的一半大小,定位点占据了7x7的点位,
        float circleSmR = (float) (3 * cellMid * ratio);
        Paint bigCirclePaint = new Paint();
        bigCirclePaint.setColor(qrColor);
        bigCirclePaint.setStyle(Paint.Style.STROKE);
        bigCirclePaint.setAntiAlias(true);


        bigCirclePaint.setStrokeWidth((float) (cellMid * 2 * ratio));
        canvas.drawCircle(outputLeft + circleP, outputTop + circleP, circleSmR, paint);
        canvas.drawCircle(outputRight - circleP, outputTop + circleP, circleSmR, paint);
        canvas.drawCircle(outputLeft + circleP, outputBottom - circleP, circleSmR, paint);
        float circleR = 6 * cellMid;
        canvas.drawCircle(outputLeft + circleP, outputTop + circleP, circleR, bigCirclePaint);
        canvas.drawCircle(outputRight - circleP, outputTop + circleP, circleR, bigCirclePaint);
        canvas.drawCircle(outputLeft + circleP, outputBottom - circleP, circleR, bigCirclePaint);

        ignoreMatrix.setRegion(0, 0, 7, 7);
        ignoreMatrix.setRegion(originalWidth - 7, 0, 7, 7);
        ignoreMatrix.setRegion(0, originalHeight - 7, 7, 7);

    }
  • 二维码的方形定位点绘制
   /*绘制非圆形定位点*/
    private static void drawAnchor2(int anchortype, float outputTop, float outputLeft, int cellWidth, Paint paint, Canvas canvas, int x, int y, float remark3) {
        float outputY = 0;
        float outputX = 0;
        switch (anchortype) {
            case 0://普通矩形
                outputY = outputTop + y * cellWidth + (1.0f - remark3) * cellWidth / 2;
                outputX = outputLeft + x * cellWidth + (1.0f - remark3) * cellWidth / 2;
                canvas.drawRect(outputX, outputY, outputX + cellWidth * remark3, outputY + cellWidth * remark3, paint);
                break;
            case 1://圆形矩形
                outputY = outputTop + y * cellWidth + (float) (cellWidth * 1.0 / 2);
                outputX = outputLeft + x * cellWidth + (float) (cellWidth * 1.0 / 2);
                canvas.drawCircle(outputX, outputY, (float) (cellWidth * remark3 * 1.0 / 2), paint);
                break;
            case 3:
            case 4:
            case 5:
            case 6:
                outputY = outputTop + y * cellWidth;
                outputX = outputLeft + x * cellWidth;
                double pr = 2 * Math.PI / anchortype;
                double pr90 = Math.PI / 2;
                Path path = new Path();
                double r1;
                double r2 = pr / 2 - pr90;
                int cellMid = cellWidth / 2;
                int cellFour = cellWidth / 4;
                path.moveTo(outputX + cellMid, outputY);
                path.lineTo((int) (outputX + cellMid + Math.cos(r2) * cellFour), (int) (outputY + cellMid + Math.sin(r2) * cellFour));
                for (int indexL = 1; indexL < anchortype; indexL++) {
                    r1 = indexL * pr - pr90;
                    path.lineTo((int) (outputX + cellMid + Math.cos(r1) * cellMid), (int) (outputY + cellMid + Math.sin(r1) * cellMid));
                    r2 = indexL * pr + pr / 2 - pr90;
                    path.lineTo((int) (outputX + cellMid + Math.cos(r2) * cellFour), (int) (outputY + cellMid + Math.sin(r2) * cellFour));
                }
                path.close();
                canvas.drawPath(path, paint);
                break;
        }
    }
  •  方法调用
int[] sizes = new int[]{600, 600};
int[] colors = {Color.BLACK, Color.WHITE};

Bitmap bitmap1 = CreateQRCode("你好123456", 1.0f, sizes, colors,
                    0, 0, 0, 1.0f, 1.0f);
  •  正常尺寸图形

 

  •  定位点和码点缩小之后

        

扫描二维码关注公众号,回复: 15747386 查看本文章

注:这里主要使用了方形,圆点和多边形,还可以替换成表情等码点,这里点的大小变化都是基于了中心点不变,如果需要将码点显示为正常大小的一半,比如只显示左侧或者上半部分,同样可以在数组的循环方法中绘制想要的样式,不过这种时候需要麻烦公司的大佬重编解析库。

猜你喜欢

转载自blog.csdn.net/QhappyfishQ/article/details/126365364