天天看點

【android應用】詳解基于ZXing生成的二維碼,文末附帶源碼---去除白邊,以及透明底

最近在做一個二維碼的相關的應用,順帶整理下相關的知識。今天我們來看看二維碼的白邊如何去除,以及如何生透明底的二維碼。
【android應用】詳解基于ZXing生成的二維碼,文末附帶源碼---去除白邊,以及透明底

前篇回顧:【android應用】基于ZXing生成的二維碼,文末附帶源碼---添加logo、彩色、背景、文字、水印在這裡都能找到

在上篇中我們已經知道了彩色二維碼如何生成,以及logo、背景、文字、水印的添加,但裡面那個白邊一直占據着我們的視線,今天就來講講如何去除掉這個白邊。

目錄

何去除白邊

1、MultiFormatWriter類的encode方法

2、QRCodeWriter類的encode方法

3、方法renderResult

4、解決方案

如何生成透明底的二維碼

1、再來看下生成二維碼圖的函數

2、色彩模式ARGB

3、解決方案

4、效果圖

如何添加半透明的水印和logo

1、了解下drawBitmap方法

2、看下添加水印的代碼

3、解決方案

4、效果圖

源碼

結束語

何去除白邊

下面我們來跟一下二維碼生成的源碼流程。首先是調用位置:

BitMatrix matrix = new MultiFormatWriter().encode(content,BarcodeFormat.QR_CODE, width, height, hints);

1、MultiFormatWriter類的encode方法

public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType, ?> hints) throws WriterException {
         Writer writer;
         switch (format) {
             case EAN_8: 
           writer = new EAN8Writer();
           break;
         case EAN_13: 
           writer = new EAN13Writer();
           break;
       case UPC_A: 
           writer = new UPCAWriter();
          break;
        case QR_CODE: 
           //用到的是這個模式
           writer = new QRCodeWriter();
           break;
         case CODE_39: 
           writer = new Code39Writer();
          break;
         case CODE_128: 
           writer = new Code128Writer();
           break;
         case ITF: 
           writer = new ITFWriter();
           break;
         case PDF_417: 
           writer = new PDF417Writer();
           break;
         case CODABAR: 
           writer = new CodaBarWriter();
           break;
         default: 
           throw new IllegalArgumentException("No encoder available for format " + format);
         }
        return writer.encode(contents, format, width, height, hints);
        }
       }
           
實際上這個方法就是依據format來選擇一種編碼方式,我們這裡用的是QR_CODE的方式了,然後我們再看QRCodeWriter的encode方法。

2、QRCodeWriter類的encode方法

public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType, ?> hints) throws WriterException {
        if(contents.length() == 0) {
            throw new IllegalArgumentException("Found empty contents");
        } else if(format != BarcodeFormat.QR_CODE) {
            throw new IllegalArgumentException("Can only encode QR_CODE, but got " + format);
        } else if(width >= 0 && height >= 0) {
            ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
            if(hints != null) {
                ErrorCorrectionLevel code = (ErrorCorrectionLevel)hints.get(EncodeHintType.ERROR_CORRECTION);
                if(code != null) {
                    errorCorrectionLevel = code;
                }
            }

            // 前面的都是做編碼前的準備,安全檢驗,糾錯級别設定等
            QRCode code1 = new QRCode();
            // 這裡才是真正的将contents轉換成code
            Encoder.encode(contents, errorCorrectionLevel, hints, code1);
            // return的時候将code轉換成BitMatrix,并加入白邊
            return renderResult(code1, width, height);
        } else {
            throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' + height);
        }
    }
           
下面再看将code轉換成BitMatrix,并加入白邊的方法renderResult

3、方法renderResult

private static BitMatrix renderResult(QRCode code, int width, int height) {
        ByteMatrix input = code.getMatrix();
        if (input == null) {
            throw new IllegalStateException();
        }
        int inputWidth = input.getWidth();
        int inputHeight = input.getHeight();
        // 這裡qrWidth就是原始的二維碼的寬度了,包含8機關寬度的白邊
        int qrWidth = inputWidth + 8;
        int qrHeight = inputHeight + 8;
        // 依據使用者的輸入寬高,計算最後的輸出寬高
        int outputWidth = Math.max(width, qrWidth);
        int outputHeight = Math.max(height, qrHeight);

        //計算縮放比例
        int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);

        // 計算白邊的寬度
        int leftPadding = (outputWidth - inputWidth * multiple) / 2;
        int topPadding = (outputHeight - inputHeight * multiple) / 2;

        BitMatrix output = new BitMatrix(outputWidth, outputHeight);

        int inputY = 0;
        // 嵌套循環,将ByteMatrix的内容計算padding後轉換成BitMatrix
        for (int outputY = topPadding; inputY < inputHeight; outputY += multiple) {
            int inputX = 0;
            for (int outputX = leftPadding; inputX < inputWidth; outputX += multiple) {
                if (input.get(inputX, inputY) == 1) {
                    output.setRegion(outputX, outputY, multiple, multiple);
                }
                inputX++;
            }
            inputY++;
        }

   return output;
}
           

4、解決方案

這個方法裡的代碼不難讀懂,是以要去掉白邊實際上就很簡單了,自定義一個QRCodeWriter類,完全把Zxing包的QRCodeWriter類複制過來,然後将renderResult方法裡的padding去掉就可以了(為什麼不繼承QRCodeWriter,因為它是final類~~)。 
【android應用】詳解基于ZXing生成的二維碼,文末附帶源碼---去除白邊,以及透明底

修改後如下:

private static BitMatrix renderResult(QRCode code, int width, int height) {
        ByteMatrix input = code.getMatrix();
        if (input == null) {
            throw new IllegalStateException();
        }
        int inputWidth = input.getWidth();
        int inputHeight = input.getHeight();
        // 依據使用者的輸入寬高,計算最後的輸出寬高
        int outputWidth = Math.max(width, inputWidth);
        int outputHeight = Math.max(height, inputHeight);

        //計算縮放比例
        int multiple = Math.min(outputWidth / inputWidth, outputHeight / inputHeight);
        BitMatrix output = new BitMatrix(outputWidth, outputHeight);
        int inputY = 0;
        // 嵌套循環,将ByteMatrix的内容計算padding後轉換成BitMatrix
        for (int outputY = 0; inputY < inputHeight; outputY += multiple) {
            int inputX = 0;
            for (int outputX = 0; inputX < inputWidth; outputX += multiple) {
                if (input.get(inputX, inputY) == 1) {
                    output.setRegion(outputX, outputY, multiple, multiple);
                }
                inputX++;
            }
            inputY++;
        }

        return output;
    }
           
但是你還是會發現,還是有一點點小小的白邊。而且整體往上偏了效果圖如下:
【android應用】詳解基于ZXing生成的二維碼,文末附帶源碼---去除白邊,以及透明底
在renderResult方法中加上調試log在看下:
【android應用】詳解基于ZXing生成的二維碼,文末附帶源碼---去除白邊,以及透明底
【android應用】詳解基于ZXing生成的二維碼,文末附帶源碼---去除白邊,以及透明底

可以看出29*8 != 240;

解決方案1:

嘗試修改inputWidth和inputHeight為29的倍數是可以解決的,但是每次都需要計算傳值這樣很是麻煩。

解決方案2:

在renderResult方法得到multiple後重新指派一次給inputWidth和inputHeight,這樣比較友善不需要每次計算傳值。

最終renderResult方法修改如下:

private static BitMatrix renderResult(QRCode code, int width, int height) {
        ByteMatrix input = code.getMatrix();
        if (input == null) {
            throw new IllegalStateException();
        }
        int inputWidth = input.getWidth();
        int inputHeight = input.getHeight();
        // 依據使用者的輸入寬高,計算最後的輸出寬高
        int outputWidth = Math.max(width, inputWidth);
        int outputHeight = Math.max(height, inputHeight);

        //計算縮放比例
        int multiple = Math.min(outputWidth / inputWidth, outputHeight / inputHeight);
        //重新寫一次outputWidth和inputHeight
        outputWidth = multiple * inputWidth;
        outputHeight = multiple * inputHeight;
        Log.d(TAG, "renderResult: outputWidth " +outputWidth +" outputHeight "+outputHeight);
        Log.d(TAG, "renderResult: inputHeight " +inputWidth +" inputHeight "+inputHeight);
        Log.d(TAG, "renderResult: multiple " + multiple);
        BitMatrix output = new BitMatrix(outputWidth, outputHeight);
        int inputY = 0;
        // 嵌套循環,将ByteMatrix的内容計算padding後轉換成BitMatrix
        for (int outputY = 0; inputY < inputHeight; outputY += multiple) {
            int inputX = 0;
            for (int outputX = 0; inputX < inputWidth; outputX += multiple) {
                if (input.get(inputX, inputY) == 1) {
                    output.setRegion(outputX, outputY, multiple, multiple);
                }
                inputX++;
            }
            inputY++;
        }

        return output;
    }
           

這樣就能完美的去除白邊了效果圖如下:

【android應用】詳解基于ZXing生成的二維碼,文末附帶源碼---去除白邊,以及透明底
後面我會把源碼貼上,如果對你有用,希望能給我一個贊。謝謝!!!

如何生成透明底的二維碼

1、再來看下生成二維碼圖的函數

private static Bitmap bitMatrix2Bitmap(BitMatrix matrix) {
    int WIDTH = matrix.getWidth();
    int HEIGHT = matrix.getHeight();
    int[] pixels = new int[WIDTH * HEIGHT];
    for (int y = 0; y < WIDTH; y++) {
        for (int x = 0; x < HEIGHT; x++) {
            int color = Color.WHITE;
            if (matrix.get(x, y)) {
                // 有内容的部分,顔色設定為漸變,當然這裡可以自己修改成喜歡的顔色
                color = 0xFF0094FF + y/2;// 藍色
//              if (x < WIDTH / 2 && y < HEIGHT / 2) {
//                   color = 0xFF0094FF;// 藍色
//                   //Integer.toHexString(new Random().nextInt());
//              } else if (x < WIDTH / 2 && y > HEIGHT / 2) {
//                   color = 0xFFFED545;// 黃色
//              } else if (x > WIDTH / 2 && y > HEIGHT / 2) {
//                   color = 0xFF5ACF00;// 綠色
//              } else {
//                   color = 0xFF000000;// 黑色
//              }
            }
            pixels[x + (y * WIDTH)] = color;
        }
    }

    Bitmap bitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.RGB_565);
    bitmap.setPixels(pixels, 0, WIDTH, 0, 0, WIDTH, HEIGHT);
    return bitmap;
}
           

我們隻需要将color的初始值設定成我們我們想要的顔色,就能得到我們想要的底色。比如藍色(0xFF0094FF):

【android應用】詳解基于ZXing生成的二維碼,文末附帶源碼---去除白邊,以及透明底
是不是感覺自己馬上就能搞出透明底的隻需要将FF改成8F就木有問題了,可結果不是這樣,不要慌。我們再來回顧下色彩模式。你就知道哪不對了。

2、色彩模式ARGB

可以檢視我轉載的一篇部落格,飛機票:常用色彩模式ARGB詳解

3、解決方案

半透明:
  • 将0xFF0094FF改成0x800094FF
  • 将Bitmap.Config.RGB_565模式修改成Bitmap.Config.ARGB_8888模式
private static Bitmap bitMatrix2Bitmap(BitMatrix matrix) {
        int WIDTH = matrix.getWidth();
        int HEIGHT = matrix.getHeight();
        int[] pixels = new int[WIDTH * HEIGHT];
        for (int y = 0; y < WIDTH; y++) {
            for (int x = 0; x < HEIGHT; x++) {
                //int color = 0xFFFFFFFF;
                int color = 0x800094FF;
                if (matrix.get(x, y)) {
                    // 有内容的部分,顔色設定為黑色,當然這裡可以自己修改成喜歡的顔色
                    color = 0xFF000000;
                }
                pixels[x + (y * WIDTH)] = color;
            }
        }

        Bitmap bitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888);
        bitmap.setPixels(pixels, 0, WIDTH, 0, 0, WIDTH, HEIGHT);
        return bitmap;
    }
           

4、效果圖

【android應用】詳解基于ZXing生成的二維碼,文末附帶源碼---去除白邊,以及透明底
透明的隻需要在透明上修改顔色即可(将0xFF0094FF改成0x000094FF),這裡就不貼效果圖了。

如何添加半透明的水印和logo

1、了解下drawBitmap方法

drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint);

Rect src: 是對圖檔進行裁截,若是空null則顯示整個圖檔

RectF dst:是圖檔在Canvas畫布中顯示的區域,

           大于src則把src的裁截區放大,

           小于src則把src的裁截區縮小。

Paint paint :該類儲存了繪制幾何圖形、文本和位圖的樣式和顔色資訊。

2、看下添加水印的代碼

public static Bitmap composeWatermark(Bitmap srcBMP, Bitmap markBMP) {
        if (srcBMP == null) {
            return null;
        }
        // 建立一個新的和SRC長度寬度一樣的位圖
        Bitmap newb = Bitmap.createBitmap(srcBMP.getWidth(),
                srcBMP.getHeight(), Bitmap.Config.ARGB_8888);

        Canvas cv = new Canvas(newb);
        // 在 0,0坐标開始畫入原圖
        cv.drawBitmap(srcBMP, 0, 0, null);
        // 在原圖的右下角畫入水印
        cv.drawBitmap(markBMP, srcBMP.getWidth() - markBMP.getWidth() * 3 / 5,
                srcBMP.getHeight() * 3 / 7, null);
        // 儲存
        cv.save(Canvas.ALL_SAVE_FLAG);
        // 存儲
        cv.restore();
        return newb;
    }
           
Paint 表示畫筆 Canvas 表示畫布,畫闆。可以看出Paint 傳入的null,說明使用預設的。我們可以通過畫筆Paint來設定,比如:畫筆大小,顔色值,透明度,填充樣式等等。這樣就簡單了。

3、解決方案

public static Bitmap composeWatermark(Bitmap srcBMP, Bitmap markBMP) {
        if (srcBMP == null) {
            return null;
        }
        // 建立一個新的和SRC長度寬度一樣的位圖
        Bitmap newb = Bitmap.createBitmap(srcBMP.getWidth(),
                srcBMP.getHeight(), Bitmap.Config.ARGB_8888);

        //擷取透明度
        Paint vPaint = selectAlpha(0);

        Canvas cv = new Canvas(newb);
        // 在 0,0坐标開始畫入原圖
        cv.drawBitmap(srcBMP, 0, 0, null);
        // 在原圖的右下角畫入水印
        cv.drawBitmap(markBMP, srcBMP.getWidth() - markBMP.getWidth() * 3 / 5,
                srcBMP.getHeight() * 3 / 7, vPaint);
        // 儲存
        cv.save(Canvas.ALL_SAVE_FLAG);
        // 存儲
        cv.restore();
        return newb;
    }

    /**
     * 設定透明度
     *
     * @param alpha    透明度
     * @return Paint    畫筆
     */
    private static Paint setAlpha(int alpha) {
        // 建立Paint 物件
        Paint vPaint = new Paint();
        vPaint .setStyle( Paint.Style.STROKE );   //空心
        vPaint .setAlpha(alpha);   //0—255
        return vPaint;
    }

    /**
     * 選擇透明度
     *
     * @param alpha    透明度  1 不透明 0 半透明
     * @return Paint    畫筆
     */
    private static Paint selectAlpha(int alpha) {
        if(alpha == 0){
            return setAlpha(75);
        }else{
            return setAlpha(255);
        }
    }
           

4、效果圖

【android應用】詳解基于ZXing生成的二維碼,文末附帶源碼---去除白邊,以及透明底

源碼

飛機票:【源碼】二維碼透明底和去除白邊功能

結束語

以上就是跟二維碼去除白邊和透明底的相關接口總結,希望對你有用。歡迎大家關注我們微信公衆号,來交流程式員的技術。如果能留言或者點個贊,我也是很開心的,非常感謝!
【android應用】詳解基于ZXing生成的二維碼,文末附帶源碼---去除白邊,以及透明底

部分圖檔來自網際網路

參考:https://blog.csdn.net/pxr1989104/article/details/51283585