使用Zxing玩转二维码白边的各个花样

来源:互联网 发布:无人机飞控调参软件 编辑:程序博客网 时间:2024/04/29 03:34

1.生成二维码

简单介绍一下Zxing二维码库的使用方式,Zxing库很强大,可以生成各种格式的二维码(分析源码部分时再看其他的类型),最常用的就是QR格式。

1.1代码

如果没有Zxing库,可以到我的云盘下载。
把jar包下载,copy到工程的libs目录下,buildpath即可

1.1.1 获取编码后的数据Bitmatrix

BitMatrix是Zxing库定义的一个二维码的数据类。
这个方法主要是生成二维码的BitMatrix,实际上就是一个矩阵,二维数组–!
获取到Bitmap后,就可以随意展示了

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static Bitmap generateQRCode(String content, int width, intheight) {
       try{
           HashMap hints =new HashMap();
           // 设置编码方式utf-8
           hints.put(EncodeHintType.CHARACTER_SET,"utf-8");
           //设置二维码的纠错级别为h
           hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
           BitMatrix matrix =new MultiFormatWriter().encode(content,
                   BarcodeFormat.QR_CODE, width, height, hints);
           returnbitMatrix2Bitmap(matrix);
       }catch (WriterException e) {
           e.printStackTrace();
       }
       returnnull;
   }

1.1.2将数据Bitmatrix转换成Bitmap

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static Bitmap bitMatrix2Bitmap(BitMatrix matrix) {
       matrix = updateBit(matrix,0);
       intw = matrix.getWidth();
       inth = matrix.getHeight();
       int[] rawData =new int[w * h];
       for(int i = 0; i < w; i++) {
           for(int j = 0; j < h; j++) {
               intcolor = Color.WHITE;
               if(matrix.get(i, j)) {
                       // 有内容的部分,颜色设置为黑色,当然这里可以自己修改成喜欢的颜色
                   color = Color.BLACK;
               }
               rawData[i + (j * w)] = color;
           }
       }
 
       Bitmap bitmap = Bitmap.createBitmap(w, h, Config.RGB_565);
       bitmap.setPixels(rawData,0, w, 0,0, w, h);
       returnbitmap;
   }

2.源码分析白边的生成过程

下面就分析Zxing的源码以及默认白边的形成
先看generateQRCode里的关键方法MultiFormatWriter类的encode方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public BitMatrix encode(String contents, BarcodeFormat format, intwidth, int height, Map hints) throws WriterException {
         Writer writer;
         switch(format) {
             caseEAN_8:
           writer =new EAN8Writer();
           break;
         caseEAN_13:
           writer =new EAN13Writer();
           break;
       caseUPC_A:
           writer =new UPCAWriter();
          break;
        caseQR_CODE:
           writer =new QRCodeWriter();
           break;
         caseCODE_39:
           writer =new Code39Writer();
          break;
         caseCODE_128:
           writer =new Code128Writer();
           break;
         caseITF:
           writer =new ITFWriter();
           break;
         casePDF_417:
           writer =new PDF417Writer();
           break;
         caseCODABAR:
           writer =new CodaBarWriter();
           break;
         default:
           thrownew IllegalArgumentException("No encoder available for format "+ format);
         }
        returnwriter.encode(contents, format, width, height, hints);
        }
       }

实际上这个方法就是依据format来选择一种编码方式,最常用的就是QR_CODE的方式了,还有其他的方式,基本不用~~有兴趣的可以百度下
然后我们再看QRCodeWriter的encode方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public BitMatrix encode(String contents, BarcodeFormat format, intwidth, int height, Map hints)
   throwsWriterException
   {
    if(contents.length() == 0) {
     thrownew IllegalArgumentException("Found empty contents");
   }
 
    if(format != BarcodeFormat.QR_CODE) {
      thrownew IllegalArgumentException("Can only encode QR_CODE, but got "+ format);
   }
 
    if((width < 0) || (height <0)) {
      thrownew IllegalArgumentException("Requested dimensions are too small: "+ width + 'x' + height);
   }
 
 
   ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
    if(hints != null) {
      ErrorCorrectionLevel requestedECLevel = (ErrorCorrectionLevel)hints.get(EncodeHintType.ERROR_CORRECTION);
     if(requestedECLevel != null) {
        errorCorrectionLevel = requestedECLevel;
     }
    }
   // 前面的都是做编码前的准备,安全检验,纠错级别设置等
   QRCode code =new QRCode();
   // 这里才是真正的将contents转换成code
    Encoder.encode(contents, errorCorrectionLevel, hints, code);
    // return的时候将code转换成BitMatrix,并加入白边
    returnrenderResult(code, width, height);
      }

下面再看将code转换成BitMatrix,并加入白边的方法renderResult

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private static BitMatrix renderResult(QRCode code, int width, intheight) {
        ByteMatrix input = code.getMatrix();
        if(input == null) {
            thrownew IllegalStateException();
        }
        intinputWidth = input.getWidth();
        intinputHeight = input.getHeight();
        // 这里qrWidth就是原始的二维码的宽度了,包含8单位宽度的白边
        intqrWidth = inputWidth + 8;
        intqrHeight = inputHeight + 8;
        // 依据用户的输入宽高,计算最后的输出宽高
        intoutputWidth = Math.max(width, qrWidth);
        intoutputHeight = Math.max(height, qrHeight);
 
//计算缩放比例
    intmultiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
 
   // 计算白边的宽度
        intleftPadding = (outputWidth - inputWidth * multiple) / 2;
        inttopPadding = (outputHeight - inputHeight * multiple) / 2;
 
  BitMatrix output =new BitMatrix(outputWidth, outputHeight);
 
   intinputY = 0;
        // 嵌套循环,将ByteMatrix的内容计算padding后转换成BitMatrix
        for(int outputY = topPadding; inputY < inputHeight; outputY += multiple) {
            intinputX = 0;
            for(int outputX = leftPadding; inputX < inputWidth; outputX += multiple) {
                if(input.get(inputX, inputY) == 1) {
                    output.setRegion(outputX, outputY, multiple, multiple);
                }
                inputX++;
            }
            inputY++;
        }
 
   returnoutput;
    }

这个方法里的代码不难读懂,所以要去掉白边实际上就很简单了,自定义一个QRCodeWriter类,完全把Zxing包的QRCodeWriter类复制过来,然后将renderResult方法里的padding去掉就可以了(为什么不继承QRCodeWriter,因为它是final类~~)。
下面是去掉padding后的代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private static BitMatrix renderResult(QRCode code, int width, intheight) {
        ByteMatrix input = code.getMatrix();
        if(input == null) {
            thrownew IllegalStateException();
        }
        intinputWidth = input.getWidth();
        intinputHeight = input.getHeight();
        // 依据用户的输入宽高,计算最后的输出宽高
        intoutputWidth = Math.max(width, inputWidth);
        intoutputHeight = Math.max(height, inputHeight);
 
  //计算缩放比例
        intmultiple = Math.min(outputWidth / inputWidth, outputHeight / inputHeight);
 
   BitMatrix output =new BitMatrix(outputWidth, outputHeight);
        intinputY = 0;
        // 嵌套循环,将ByteMatrix的内容计算padding后转换成BitMatrix
        for(int outputY = 0; inputY < inputHeight; outputY += multiple) {
            intinputX = 0;
            for(int outputX = 0; inputX < inputWidth; outputX += multiple) {
                if(input.get(inputX, inputY) == 1) {
                    output.setRegion(outputX, outputY, multiple, multiple);
                }
                inputX++;
            }
            inputY++;
        }
 
   returnoutput;
}

效果图(为了区分白边,将整个背景色设置成的#0f0了)
去掉白边前:这里写图片描述

去掉白边后:这里写图片描述

去掉白边的其他方法

方法1:

这个方法就是将Zxing生成的BitMatrix更新一下去掉了周边,并重新设置白边的宽度,见margin

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<code class="hljs cs">   private staticBitMatrix updateBit(BitMatrix matrix, intmargin) {
        inttempM = margin * 2;
        int[] rec = matrix.getEnclosingRectangle();// 获取二维码图案的属性
        intresWidth = rec[2] + tempM;
        intresHeight = rec[3] + tempM;
        BitMatrix resMatrix =new BitMatrix(resWidth, resHeight);// 按照自定义边框生成新的BitMatrix
        resMatrix.clear();
        for(int i = margin; i < resWidth - margin; i++) { // 循环,将二维码图案绘制到新的bitMatrix中
            for(int j = margin; j < resHeight - margin; j++) {
                if(matrix.get(i - margin + rec[0], j - margin + rec[1])) {
                    resMatrix.set(i, j);
                }
            }
        }
        returnresMatrix;
    }</code>

方法2:

在比较新的Zxing包中EncodeHintType有另外一个属性就是Margin,可以设置这个属性来更新,有兴趣的可以去玩玩哈,这里就不贴代码了

3.自定义白边颜色

当然通过上面的阅读,可以自定义白边的宽度了,下面就介绍下自定义白边颜色的方法;
当然,最简单的自定义白边就是使用一个imageview展示无边的二维码,外层view设置一个白边背景,就可以了~
这里要介绍的方法是修改bitMatrix2Bitmap方法,通过上文知道,通过zxing包生成的只是BitMaxtrix,这是不能直接用在imageview上的。读者如果仔细阅读了上文的bitMatrix2Bitmap方法就应该可以猜到这里要介绍的方法了,不多说,代码如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private static Bitmap bitMatrix2Bitmap(BitMatrix matrix, Bitmap logBitmap) {
       intw = matrix.getWidth();
       inth = matrix.getHeight();
       int[] rec = matrix.getEnclosingRectangle();
       int[] rawData =new int[w * h];
       for(int i = 0; i < w; i++) {
           for(int j = 0; j < h; j++) {
               intcolor = Color.WHITE;
               if(matrix.get(i, j)) {
                   color = Color.BLACK;
               }
               // 设置白边的颜色
               if(i < rec[0] || j < rec[1] || i > (rec[0] + rec[2]) || j > (rec[1] + rec[3])){
                   color = Color.RED;
               }
               rawData[i + (j * w)] = color;
           }
       }
 
       Bitmap bitmap = Bitmap.createBitmap(w, h, Config.RGB_565);
       bitmap.setPixels(rawData,0, w, 0,0, w, h);
       returnaddLogo(bitmap, logBitmap);
   }

这个方法就是可以自定义白边颜色的方法,
效果图这里写图片描述

这里需要解释下BitMatrix的getEnclosingRectangle()获取到的东西到底是什么,这个方法的源码我就不贴了,这个方法返回一个一维数组,长度为4<喎�"/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">return new int[] { left, top, width, height };

left就是二维码内容左上角的x坐标,top就是左上角的y坐标,with就是二维码内容的宽度,height就是二维码内容的高度。
所以当i和j超出二维码内容范围的时候就可以设置自己的边框颜色了~~

4.二维码添加LOGO(单个文字等)

逻辑比较简单,获取到二维码的Bitmap后,Logo就是在这个Bitmap中间再绘制一个Bitmap不就可以了么。
代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private static Bitmap addLogo(Bitmap src, Bitmap logo) {
       if(logo == null) {
           returnsrc;
       }
       // 获取图片的宽高
       intsrcWidth = src.getWidth();
       intsrcHeight = src.getHeight();
       intlogoWidth = logo.getWidth();
       intlogoHeight = logo.getHeight();
       if(logoWidth == 0 || logoHeight == 0) {
           returnsrc;
       }
       // logo大小为二维码整体大小的1/5
       floatscaleFactor = 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();
       }
 
       returnbitmap;
   }

5.二维码下方添加文字段落

逻辑和添加logo是一样的,只不过这里添加的文字是放置在二维码下方的一段文字,需要测定文字的大小,行高等,代码逻辑我就不解释了,主要是是使用级少,项目中有一个分享需要将这个做成图片分享到微信,后来又嫌弃样式太不美观丢弃了~~~确实很烦。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public static Bitmap addTextToBitmap(Bitmap bmpSrc, String text) {
       intsrcWidth = bmpSrc.getWidth();
       intsrcHeight = bmpSrc.getHeight();
 
       // 先计算text所需要的height
       inttextSize = 20;
       intpadding = 3;
       inttextLinePadding = 1;
       // 每行的文字
       intperLineWords = (srcWidth - 2* padding) / textSize;
       intlineNum = text.length() / perLineWords;
       lineNum = text.length() % perLineWords ==0 ? lineNum : lineNum +1;
       inttextTotalHeight = lineNum * (textSize + textLinePadding) +2 * padding;
 
       Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight + textTotalHeight,
               Bitmap.Config.ARGB_8888);
       try{
           Canvas canvas =new Canvas(bitmap);
           canvas.drawColor(Color.WHITE);
           canvas.drawBitmap(bmpSrc,0, 0,null);
           Paint paint =new Paint(Paint.ANTI_ALIAS_FLAG);
           paint.setColor(Color.BLACK);
           paint.setTextSize(textSize);
           String lineText;
           for(int i = 0, startY = srcHeight + textSize, start, end; i < lineNum; i++) {
               start = i * perLineWords;
               end = start + perLineWords;
               lineText = text.substring(start, end > text.length() ? text.length() : end);
               canvas.drawText(lineText, padding, startY, paint);
               startY += textSize + textLinePadding;
           }
           canvas.save(Canvas.ALL_SAVE_FLAG);
           canvas.restore();
       }catch (Exception e) {
           bitmap =null;
           e.getStackTrace();
       }
       returnbitmap;
   }

实际上,最后的这2个部分都应该是Bitmap和Canvas的内容,这期就先到这里了。

原创粉丝点击