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~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章