Android 彻底解决zxing生成条码两边空白问题

解决源码

不耽误大家时间,直接上解决代码

依赖:

implementation 'cn.bingoogolapple:bga-qrcode-zxing:1.3.7'

解决方法

/**
     * 参考OneDimensionalCodeWriter源码中对于条码边距的计算
     * @param width    条码宽度
     * @param contents 条码内容
     */
    private int getNewWidth(int width, String contents) {
        Code128Writer code128Writer = new Code128Writer();
        boolean[] code = code128Writer.encode(contents);

        int inputWidth = code.length;
        int outputWidth = Math.max(width, inputWidth);
        int remain = outputWidth % inputWidth;
        return outputWidth - remain;
    }

生成条码

Bitmap bitmapBar = QRCodeEncoder.syncEncodeBarcode(contents, getNewWidth(width, contents), height, 0);
imageView.setImageBitmap(bitmapBar);

复制上面那个方法,生成条码的时候重新计算一遍条码宽度,生成出来的条码就不会有两边空白的问题,对于条码的识别没有任何影响,在项目中已得到验证。

如果没有使用bga-qrcode-zxing库生成条码的话,还要增加如下代码

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);

完整方法如下

/**
     * 同步创建条形码图片
     *
     * @param content  要生成条形码包含的内容
     * @param width    条形码的宽度,单位px
     * @param height   条形码的高度,单位px
     * @param textSize 字体大小,单位px,如果等于0则不在底部绘制文字
     * @return 返回生成条形的位图
     */
    public static Bitmap syncEncodeBarcode(String content, int width, int height, int textSize) {
        if (TextUtils.isEmpty(content)) {
            return null;
        }
        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);

        try {
            BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.CODE_128, width, height, hints);
            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] = 0xff000000;
                    } else {
                        pixels[y * width + x] = 0xffffffff;
                    }
                }
            }
            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
            if (textSize > 0) {
                bitmap = showContent(bitmap, content, textSize);
            }
            return bitmap;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

好了现在无论你是直接使用zxing,还是使用大神封装的库,都可以解决zxing生成条码边距的问题了。如果你有时间的话可以看下原理,非常简单。

原理

我们从MultiFormatWriter().encode方法入手

 @Override
  public BitMatrix encode(String contents,
                          BarcodeFormat format,
                          int width, int height,
                          Map<EncodeHintType,?> hints) throws WriterException {

    Writer writer;
    switch (format) {
      ...
      case CODE_128:
        writer = new Code128Writer();
        break;
     ...
      default:
        throw new IllegalArgumentException("No encoder available for format " + format);
    }
    return writer.encode(contents, format, width, height, hints);
  }

省略了无关代码,最终会调到Code128Writerencode方法

@Override
  public BitMatrix encode(String contents,
                          BarcodeFormat format,
                          int width,
                          int height,
                          Map<EncodeHintType,?> hints) throws WriterException {
    if (format != BarcodeFormat.CODE_128) {
      throw new IllegalArgumentException("Can only encode CODE_128, but got " + format);
    }
    return super.encode(contents, format, width, height, hints);
  }

我们点击super进入它的父类OneDimensionalCodeWriter

@Override
  public BitMatrix encode(String contents,
                          BarcodeFormat format,
                          int width,
                          int height,
                          Map<EncodeHintType,?> hints) throws WriterException {
    if (contents.isEmpty()) {
      throw new IllegalArgumentException("Found empty contents");
    }

    if (width < 0 || height < 0) {
      throw new IllegalArgumentException("Negative size is not allowed. Input: "
                                             + width + 'x' + height);
    }

    int sidesMargin = getDefaultMargin();
    if (hints != null && hints.containsKey(EncodeHintType.MARGIN)) {
      sidesMargin = Integer.parseInt(hints.get(EncodeHintType.MARGIN).toString());
    }

    boolean[] code = encode(contents);
    return renderResult(code, width, height, sidesMargin);
  }

第一点,这里取sidesMargin 的时候会判断hints中是否有EncodeHintType.MARGIN,如果有的话,就回去这个里面的值,这也是我们为什么要增加如下方法的原因,当然另外两个参数现在看来也没啥用,可以删掉

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);

第二点,我们接着往下看

/**
   * @return a byte array of horizontal pixels (0 = white, 1 = black)
   */
  private static BitMatrix renderResult(boolean[] code, int width, int height, int sidesMargin) {
    int inputWidth = code.length;
    // Add quiet zone on both sides.
    int fullWidth = inputWidth + sidesMargin;
    int outputWidth = Math.max(width, fullWidth);
    int outputHeight = Math.max(1, height);

    int multiple = outputWidth / fullWidth;
    int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;

    BitMatrix output = new BitMatrix(outputWidth, outputHeight);
    for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
      if (code[inputX]) {
        output.setRegion(outputX, 0, multiple, outputHeight);
      }
    }
    return output;
  }

这个leftPadding 就是条码边距产生的罪魁祸首,具体的计算可以自己debug看下,产生边距的原因就是

int multiple = outputWidth / fullWidth;

这里的计算丢失了精度,如果我们给outputWidth 减去一个差值,让它刚好可以整除,那么算出的leftPadding就一定是0,也就解决了边距问题,OK。

总结

遇到问题不要慌,拿出手机拍个照,发个。。。骚瑞,串词了
总结下来就是,我们遇到问题不要慌,很多时候看一下源码就可以解决,很多时候源码复杂是因为,涉及的类比较多,来回跳转,但是理一理,也很简单,好了,到这吧,bye~

你可能感兴趣的:(Android知识)