【轉載】Android進階 - 二維碼生成

zxing.png

摘要

最近,公司業務上有個生成二維碼圖片的需求(Android端),之後筆者在網上查閱了一些資料,實現了這個功能。最後,給自己做個筆記,給各位做下分享。

什麼是二維碼?

百度鏈接:二維碼

二維碼生成方案(Android端)

在查找二維碼生成方案時,發現很多方案的源頭都指向了GitHub的開源庫https://github.com/zxing/zxing

1. ZXing簡介:

ZXing全稱zebra crossing,翻譯過來就是『斑馬線』的意思。ZXing是一個採用Java實現的、開源的、支持多格式(一維/二維)的條形碼圖像處理庫。

其中,QRCode格式就是我們常說的二維碼格式。

注:QRCode(Quick Response Code:快速響應碼)是二維條形碼中最常用的一種格式,所以很多人直接將QRCode翻譯爲二維碼,而且連百度百科都這樣稱呼,筆者也暫時就這麼稱呼了。

2. ZXing庫引入

對於開發者來講,我們需要下載ZXing庫的一個jar包(core-x.x.x.jar)或者通過添加依賴的方式引入庫文件,具體方法如下:

  • 方法一:ZXing提供了Maven庫,讓我們可以根據自己的需要選擇想要的jar包版本進行下載。Maven庫:https://repo1.maven.org/maven2/com/google/zxing/core/

  • 方法二(推薦):對於使用AndroidStudio開發的程序員而言,可能更習慣於在.gradle文件中添加依賴。具體代碼如下(3.3.0是筆者使用時的最新版本,想知道最新版本是多少可以去Maven庫查):

    dependencies {
    
    ......
    
    compile <span class="hljs-string">'com.google.zxing:core:3.3.0'</span>
    

}

3. ZXing庫使用

在公司的項目中,需要實現這樣一個功能:根據傳入的“url字符串”生成一張二維碼圖片。

也就是說,需要筆者寫一個工具方法:接收一個傳入的字符串,生成一個Bitmap對象並返回。

注:這裏筆者進行了擴展,可以傳入任意字符串(包含url字符串)。

接下來,先把筆者的二維碼生成工具類附上(使用該工具類的前提:你已完成第2步,導入了ZXing的核心庫)。

/**
 * @ClassName: QRCodeUtil
 * @Description: 二維碼工具類
 * @Author Wangnan
 * @Date 2017/2/8
 */

public class QRCodeUtil {

<span class="hljs-comment">/**
 * 創建二維碼位圖
 *
 * <span class="hljs-doctag">@param</span> content 字符串內容(支持中文)
 * <span class="hljs-doctag">@param</span> width 位圖寬度(單位:px)
 * <span class="hljs-doctag">@param</span> height 位圖高度(單位:px)
 * <span class="hljs-doctag">@return</span>
 */</span>
<span class="hljs-meta">@Nullable</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Bitmap <span class="hljs-title">createQRCodeBitmap</span><span class="hljs-params">(String content, <span class="hljs-keyword">int</span> width, <span class="hljs-keyword">int</span> height)</span></span>{
    <span class="hljs-keyword">return</span> createQRCodeBitmap(content, width, height, <span class="hljs-string">"UTF-8"</span>, <span class="hljs-string">"H"</span>, <span class="hljs-string">"2"</span>, Color.BLACK, Color.WHITE);
}

<span class="hljs-comment">/**
 * 創建二維碼位圖 (支持自定義配置和自定義樣式)
 *
 * <span class="hljs-doctag">@param</span> content 字符串內容
 * <span class="hljs-doctag">@param</span> width 位圖寬度,要求&gt;=0(單位:px)
 * <span class="hljs-doctag">@param</span> height 位圖高度,要求&gt;=0(單位:px)
 * <span class="hljs-doctag">@param</span> character_set 字符集/字符轉碼格式 (支持格式:{<span class="hljs-doctag">@link</span> CharacterSetECI })。傳null時,zxing源碼默認使用 "ISO-8859-1"
 * <span class="hljs-doctag">@param</span> error_correction 容錯級別 (支持級別:{<span class="hljs-doctag">@link</span> ErrorCorrectionLevel })。傳null時,zxing源碼默認使用 "L"
 * <span class="hljs-doctag">@param</span> margin 空白邊距 (可修改,要求:整型且&gt;=0), 傳null時,zxing源碼默認使用"4"。
 * <span class="hljs-doctag">@param</span> color_black 黑色色塊的自定義顏色值
 * <span class="hljs-doctag">@param</span> color_white 白色色塊的自定義顏色值
 * <span class="hljs-doctag">@return</span>
 */</span>
<span class="hljs-meta">@Nullable</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Bitmap <span class="hljs-title">createQRCodeBitmap</span><span class="hljs-params">(String content, <span class="hljs-keyword">int</span> width, <span class="hljs-keyword">int</span> height,
                                        @Nullable String character_set, @Nullable String error_correction, @Nullable String margin,
                                        @ColorInt <span class="hljs-keyword">int</span> color_black, @ColorInt <span class="hljs-keyword">int</span> color_white)</span></span>{

    <span class="hljs-comment">/** 1.參數合法性判斷 */</span>
    <span class="hljs-keyword">if</span>(TextUtils.isEmpty(content)){ <span class="hljs-comment">// 字符串內容判空</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }

    <span class="hljs-keyword">if</span>(width &lt; <span class="hljs-number">0</span> || height &lt; <span class="hljs-number">0</span>){ <span class="hljs-comment">// 寬和高都需要&gt;=0</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }

    <span class="hljs-keyword">try</span> {
        <span class="hljs-comment">/** 2.設置二維碼相關配置,生成BitMatrix(位矩陣)對象 */</span>
        Hashtable&lt;EncodeHintType, String&gt; hints = <span class="hljs-keyword">new</span> Hashtable&lt;&gt;();

        <span class="hljs-keyword">if</span>(!TextUtils.isEmpty(character_set)) {
            hints.put(EncodeHintType.CHARACTER_SET, character_set); <span class="hljs-comment">// 字符轉碼格式設置</span>
        }

        <span class="hljs-keyword">if</span>(!TextUtils.isEmpty(error_correction)){
            hints.put(EncodeHintType.ERROR_CORRECTION, error_correction); <span class="hljs-comment">// 容錯級別設置</span>
        }

        <span class="hljs-keyword">if</span>(!TextUtils.isEmpty(margin)){
            hints.put(EncodeHintType.MARGIN, margin); <span class="hljs-comment">// 空白邊距設置</span>
        }
        BitMatrix bitMatrix = <span class="hljs-keyword">new</span> QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);

        <span class="hljs-comment">/** 3.創建像素數組,並根據BitMatrix(位矩陣)對象爲數組元素賦顏色值 */</span>
        <span class="hljs-keyword">int</span>[] pixels = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[width * height];
        <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> y = <span class="hljs-number">0</span>; y &lt; height; y++){
            <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>; x &lt; width; x++){
                <span class="hljs-keyword">if</span>(bitMatrix.get(x, y)){
                    pixels[y * width + x] = color_black; <span class="hljs-comment">// 黑色色塊像素設置</span>
                } <span class="hljs-keyword">else</span> {
                    pixels[y * width + x] = color_white; <span class="hljs-comment">// 白色色塊像素設置</span>
                }
            }
        }

        <span class="hljs-comment">/** 4.創建Bitmap對象,根據像素數組設置Bitmap每個像素點的顏色值,之後返回Bitmap對象 */</span>
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        bitmap.setPixels(pixels, <span class="hljs-number">0</span>, width, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, width, height);
        <span class="hljs-keyword">return</span> bitmap;
    } <span class="hljs-keyword">catch</span> (WriterException e) {
        e.printStackTrace();
    }

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
}

}

工具類的方法看不懂沒關係,先縷清整體流程。之後,筆者會詳解二維碼生成方法createQRCodeBitmap。

這裏,筆者寫了兩個重載的createQRCodeBitmap方法。其中第一個方法,筆者進行了默認的參數設置,可以滿足生成二維碼的大部分需求。

  • createQRCodeBitmap(String content, int width, int height):傳入任意字符串和你想要的二維碼圖片的寬、高,生成一個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):完整的二維碼生成方法,支持自定義配置和自定義樣式。

接下來,給各位來一個實例,實現二維碼生成。

4. 二維碼生成(實例)

1.在AndroidStudio中新建一個Android工程。

2.導入ZXing的核心庫。

dependencies {
......

compile <span class="hljs-string">'com.google.zxing:core:3.3.0'</span>

}

3.將上面的“QRCodeUtil”工具類添加進工程。

4.在activity_main.xml中放置一個寬、高自適應的ImageView(用於顯示二維碼圖片)。

5.在MainActivity中調用QRCodeUtil工具類中的createQRCodeBitmap方法生成二維碼位圖,之後將Bitmap對象設置進ImageView中。代碼如下:

public class MainActivity extends AppCompatActivity {
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span><span class="hljs-params">(Bundle savedInstanceState)</span> </span>{
    <span class="hljs-keyword">super</span>.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ImageView mImageView = (ImageView) findViewById(R.id.iv);
    Bitmap mBitmap = QRCodeUtil.createQRCodeBitmap(<span class="hljs-string">"https://www.baidu.com"</span>, <span class="hljs-number">480</span>, <span class="hljs-number">480</span>);
    mImageView.setImageBitmap(mBitmap);
}

}

這裏,我們調用3個參數的createQRCodeBitmap方法:傳入了一個url字符串(百度鏈接),傳入了圖片的寬、高。

接下來,跑一下程序,效果如下圖所示:

QRCode_Demo.png.png

我們看到二維碼已經生成,如果你有另一部手機就可以用掃碼工具掃描驗證了。

如果你只有一部手機,那該如何驗證二維碼信息的正確性呢?

給各位介紹一個簡單的方法:微信

把當前截屏圖片發送給自己的“微信小號”或“微信朋友”,利用微信的圖片顯示工具查看(長按圖片,有二維碼時會自動識別),如下所示:

arcode_wechat.gif

事實證明,筆者生成的二維碼是正確的,微信跳轉到了“百度一下”這個鏈接。

5. createQRCodeBitmap方法細節詳解

雖然,筆者在createQRCodeBitmap方法中加了很多註釋幫助各位理解二維碼創建的流程,但可能各位對createQRCodeBitmap這個方法有些疑惑,這些配置參數有什麼作用?ZXing這個庫到底幫我們做了什麼?接下來,我們詳解這幾個細節。

1.character_set(字符集/字符轉碼格式)這個參數有什麼用?

筆者在3個參數的重載方法中設置的字符集是"UTF-8"。(代碼如下:)

utf-8.png

ZXing源碼默認使用的是"ISO-8859-1",而"ISO-8859-1"本身是不支持中文的。如果你的url中包含中文,字符集最好選用"UTF-8"。

接下來,給各位做個關於字符集的小實驗。

筆者將字符集改爲"ISO-8859-1"。

iso-8859-1.png

我們將“百度鏈接”換成“中文字符串”,如下所示:

modify_main.png

之後,筆者生成二維碼在微信中進行識別,識別結果如下:

decode_error.png

我們看到中文字符都解碼失敗了。

之後,我們將"ISO-8859-1"改回"UTF-8"。再生成二維碼在微信中識別,識別結果如下:

decode_success.png

我們可以看到識別成功了。(我們從這裏也可以看出“微信”的使用的解碼規則是"UTF-8")

2.error_correction(容錯級別)這個參數有什麼作用?

講這個問題前,先給各位解釋下什麼是"容錯"?

不知道各位有沒有注意過這些掃碼細節:二維碼圖片有時出現部分缺失(或破損)也能掃描成功;我們近距離掃二維碼時,圖形還沒掃全就提示掃描成功了;有些二維碼中間貼了一個小圖標,掃碼也能成功......這其實都歸功於二維碼有很強大的容錯性

回到代碼,筆者3個參數的createQRCodeBitmap方法使用的是"H",代碼如下所示:

error_correction_set.png

這個"H"是什麼鬼?看下源碼,各位就懂了(“紅色字體”是筆者加的“截圖註釋”)。

error_correction_source.png

有興趣的朋友可以試下,同一個字符串使用不同容錯率生成的二維碼圖形是不一樣的,但掃出的信息是相同的。

3.margin(空白邊距)這個參數有什麼作用?

筆者3個參數的createQRCodeBitmap方法中傳入的是"2"(如下圖所示)。

margin_set.png

接下來,我們分別傳入"0","1","2"看看二維碼的圖形有什麼變化。(如下圖所示)

margin_result.png

4.color_black,color_white 這兩個參數有什麼作用?

其實就是字面意思,黑色和白色。(這裏只是建議顏色,筆者代碼如下)

black_white.png

這裏,我們換幾種顏色試試。(效果圖如下)

colors_test.png

當然,筆者還是建議使用主流的黑色和白色。因爲在實際項目中玩花樣,如果導致部分手機掃描不出來,就有點作死了...o(╯□╰)o。

5.ZXing這個庫到底幫我們做了什麼?

看下圖,紅線圈出的這兩行代碼是整個方法的核心,是筆者藉助ZXing實現的。

zxing_core.png

BitMatrix是ZXing庫中的一個類,我們將配置參數傳入到QRCodeWriter的編碼方法中,在encode的執行過程中ZXing庫進行了相應的計算(計算哪個像素點應該是黑色,哪個像素點是白色),之後會生成一個BitMatrix對象存放最後的計算結果。

之後,如果你想知道矩陣中的哪個像素點是什麼顏色可以調用bitMatrix.get(x,y)方法。如果該方法返回true,那麼該像素點應該填充黑色,反之,應該填充白色。

細節解釋就到這了,如果還有不懂的地方,筆者建議各位去看下ZXing的源碼。

題外話

做項目時,筆者導入的ZXing的庫大小是541KB,我們項目組的同事是一個“Geek”(做事追求極致的人),它感覺我這種方式太偷懶了。

因爲ZXing這個庫不僅僅提供了二維碼生成的功能,還有比如二維碼識別等其他功能。也就是說,有很多功能我們項目是用不到的。換句話說:jar包(或庫文件)中的很多類都是用不到的

最後在同事的強烈建議下,我把ZXing生成二維碼需要用到的類都提取了出來,重新進行了封裝。最後提取了23個類,文件總大小144KB

但是,同事還是覺得庫還是有點大(o(>_<)o 這同事一定是“處女座”的)。最後我只能在源碼文件中一個類、一個類的找,把沒有使用到的方法一一剔除。最後文件大小削減到了130KB

看到這裏,我猜你是不是想問:還能不能再削減?

答案:能。還可以把源碼中的所有註釋全刪了。但估計一段時間後,你再看源碼就看不懂了。(一開始筆者也想刪註釋,但想了想還是放棄了)

發表一下自己的感慨:

best_friend.png

最後,附上Demo地址(包含ZXing刪減庫):https://github.com/sinawangnan7/QRCodeDemo
封裝工具類:Android進階 - 二維碼生成(花式效果)

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