1. 程式人生 > >玩轉Android之二維碼生成與識別

玩轉Android之二維碼生成與識別

二維碼,我們也稱作QRCode,QR表示quick response即快速響應,在很多App中我們都能見到二維碼的身影,最常見的莫過於微信了。那麼今天我們就來看看怎麼樣在我們自己的App中整合二維碼的掃描與生成功能。OK,廢話不多說,我們就開始做吧。

二維碼的使用我主要想分為兩部分來給大家介紹,一部分就是二維碼的生成,這裡的知識點都很簡單,還有一部分是二維碼的識別,這裡稍微麻煩一些,不過細心來做其實也很簡單。二維碼的開發使用我們大多都是使用Google提供的zxing這個類庫,使用這個類庫我們需要先下載核心jar包,下載地址,如果我們只想生成二維碼那麼這個就夠了,但是如果我們還想做二維碼的識別,那麼我們需要在剛才的基礎上繼續新增GitHub上的開源專案,這個我們在後面再說。

1.二維碼的生成

先來看一張效果圖:


1.1  準備工作

如果我們只做二維碼的生成,那麼只需要新增核心jar包即可,如下:


1.2  二維碼生成

OK,新增完jar包之後我們就可以開始寫二維碼生成程式碼了,二維碼本身就是一張Bitmap圖片,所以我們這裡主要就是看怎麼樣來生成這張圖片,我在主介面新增一個按鈕和一個ImageView,當點選按鈕時生成一張二維碼圖片顯示在ImageView上。佈局如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="org.mobiletrain.qrwriter.MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="generate"
        android:text="生成二維碼"/>

    <ImageView
        android:id="@+id/iv"
        android:layout_width="256dp"
        android:layout_height="256dp"
        android:layout_centerInParent="true"/>
</RelativeLayout>

當我點選按鈕時生成二維碼圖片,那我們就來看看生成二維碼圖片的核心程式碼:
    private Bitmap generateBitmap(String content,int width, int height) {
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        Map<EncodeHintType, String> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        try {
            BitMatrix encode = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
            int[] pixels = new int[width * height];
            for (int i = 0; i < height; i++) {
                for (int j = 0; j < width; j++) {
                    if (encode.get(j, i)) {
                        pixels[i * width + j] = 0x00000000;
                    } else {
                        pixels[i * width + j] = 0xffffffff;
                    }
                }
            }
            return Bitmap.createBitmap(pixels, 0, width, width, height, Bitmap.Config.RGB_565);
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return null;
    }

首先這個方法接收三個引數,這三個引數分別表示生成二維碼的文字內容(你要把哪一個文字用二維碼圖片表示出來),第二個和第三個引數分別表示生成的二維碼圖片的寬和高。在這裡,我們首先要獲得一個QRCodeWriter例項,該例項中有一個方法叫做encode,通過該方法對文字內容進行編碼,該方法共有五個引數,第一個引數表示生成二維碼的文字內容,第二個引數表示編碼格式,第三個引數表示生成的二維碼的寬度,第四個引數表示生成的二維碼的高度,第五個引數可選,可以用來設定文字的編碼,encode方法的返回值是一個BitMatrix,你可以把BitMatrix理解成一個二維陣列,這個二維陣列的每一個元素都表示一個畫素點是否有資料。OK,接下來我們需要定義一個int陣列用來存放Bitmap中所有畫素點的顏色,可是我們又怎麼知道每一個畫素點是什麼顏色呢?這個時候就需要我們遍歷BitMatrix了,如果BitMatrix上的點表示 該點有資料,那麼對應在Bitmap上的畫素點就是黑色,否則就是白色。BitMatrix中的get方法的返回值為一個boolean型別,true表示該點有資料,false表示該點沒有資料。通過兩個巢狀的for迴圈將BitMatrix遍歷一遍,然後給pixels陣列都賦上值,OK,pixels陣列有值之後,接下來呼叫Bitmap的createBitmap方法建立一個Bitmap出來就可以了,createBitmap方法共接收6個引數,第一個引數表示Bitmap中所有畫素點的顏色,第二個引數表示畫素點的偏移量,第三個引數表示Bitmap每行有多少個畫素點,第四個引數表示生成的Bitmap的寬度,第五個引數表示生成的Bitmap的高度,第六個引數表示生成的Bitmap的色彩模式,因為二維碼只有黑白兩種顏色,所以我們可以不用考慮透明度,直接使用RGB_565即可。OK,這樣的話我們就獲取到了二維碼的圖片了,最後我們再來看看點選事件:
    public void generate(View view) {
        Bitmap qrBitmap = generateBitmap("http://www.csdn.net",400, 400);
        iv.setImageBitmap(qrBitmap);
    }

效果圖如下:


1.3  給二維碼中心新增Logo

OK,如果你沒有特殊的需求那麼這樣就OK了,但是我們見到的大多數二維碼的正中心都有一個Logo,那麼這個效果要怎麼實現呢?這裡就是圖片繪製的內容了,我封裝了一個方法專門來解決這個問題,程式碼如下:

    private Bitmap addLogo(Bitmap qrBitmap, Bitmap logoBitmap) {
        int qrBitmapWidth = qrBitmap.getWidth();
        int qrBitmapHeight = qrBitmap.getHeight();
        int logoBitmapWidth = logoBitmap.getWidth();
        int logoBitmapHeight = logoBitmap.getHeight();
        Bitmap blankBitmap = Bitmap.createBitmap(qrBitmapWidth, qrBitmapHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(blankBitmap);
        canvas.drawBitmap(qrBitmap, 0, 0, null);
        canvas.save(Canvas.ALL_SAVE_FLAG);
        float scaleSize = 1.0f;
        while ((logoBitmapWidth / scaleSize) > (qrBitmapWidth / 5) || (logoBitmapHeight / scaleSize) > (qrBitmapHeight / 5)) {
            scaleSize *= 2;
        }
        float sx = 1.0f / scaleSize;
        canvas.scale(sx, sx, qrBitmapWidth / 2, qrBitmapHeight / 2);
        canvas.drawBitmap(logoBitmap, (qrBitmapWidth - logoBitmapWidth) / 2, (qrBitmapHeight - logoBitmapHeight) / 2, null);
        canvas.restore();
        return blankBitmap;
    }

addLogo這個方法接收兩個引數,第一個引數就是我們在1.2節中生成的二維碼的Bitmap圖片,第二個引數就是我們的logo圖片,在該方法中我先獲取到兩張Bitmap各自的寬高,然後建立一個新的空白的Bitmap,這個新的空白的Bitmap的寬高和二維碼的寬高一致,然後建立一個Canvas物件,建立Canvas物件的時候將blankBitmap傳入,這樣我一會繪製的東西相當於都是繪製在了blankBitmap上了。canvas的drawBitmap方法接收四個引數,第一個是你要繪製的Bitmap物件,第二個和第三個是你要繪製的Bitmap的左上角的座標,第四個引數是一個畫筆,一般情況下我們給一個null就可以了,如果你要設定重複模式等等效果的時候可以不給null。我們使用drawBitmap方法先將原本的二維碼圖片繪製出來,繪製完成之後,呼叫canvas的save方法,將當前的繪製狀態儲存下來,然後對畫布進行縮放,縮小畫布之後我們來繪製Logo,一幫情況下logo的寬高為二維碼原圖寬高的1/5(中心logo圖片不宜過大,否則會影響到二維碼的識別),所以我們先通過一個while迴圈獲得縮放比例,然後呼叫canvas的scale方法對畫布進行縮放,前兩個引數表示寬高的縮放比例,大於1表示放大,小於1表示縮小,後兩個引數表示縮放的中心點。縮放完成之後我們就可以繪製logo了,logo繪製完成之後,呼叫canvas的restore方法將畫布恢復為原來的狀態,最後將blankBitmap返回。在點選事件中呼叫這個方法即可,程式碼如下:
    public void generate(View view) {
        Bitmap qrBitmap = generateBitmap("http://www.csdn.net",400, 400);
        Bitmap logoBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
        Bitmap bitmap = addLogo(qrBitmap, logoBitmap);
        iv.setImageBitmap(bitmap);
    }

效果圖如下:

OK,至此,我們的二維碼生成就說完了,就是這麼簡單。

2.二維碼的識別

二維碼的識別是一個稍微麻煩的事情,一般情況下,我們直接使用GitHub上的開源專案zxing即可,這個專案就是在我們之前的那個核心包的基礎上完成的(下載地址)。當然,如果你需要自己定義相關的頁面等等也都可以,這個我們到後面再說。這裡我們先來看看怎麼把GitHub上的開源專案引入到我們的專案中來。

匯入工程我們主要分為如下幾個步驟:

1.建立一個新的Project,命名為QRReader(這一步不是必須的,可以根據你的專案需求來)

2.下載ZXing專案

3.在新的Project中建立一個新的Module,在建立的過程中選擇Android Library,如下圖:

4.在資料夾中開啟我們的專案,找到第三步建立的Library,將第二步下載的ZXing專案中的android和android-core兩個資料夾合併到library中,如下圖:

其中android資料夾中主要合併如下幾個專案檔案:

合併完成之後再將我們之前下載的核心jar包夜拷貝到我們library的libs資料夾中,然後我們再來看看我們的library:

5.在我們的專案中引用這個module,然後對專案進行編譯。

6.編譯之後專案會報錯,這個時候需要我們將library中所有的switch語句改為if...else if ...else if的形式。

7.完成第6步之後還是會報錯,這個時候需要我們將library的清單檔案中Application節點的icon屬性刪除,再編譯就沒有任何問題了。

OK,經過上面7個步驟這個開源專案就被我們成功的引入到我們自己的專案中了,在這個開源專案中有一個CaptureActivity,這個Activity專門用來掃描二維碼,所以我們只需要在自己的專案中直接來啟動這個Activity就可以了,另外,由於預設情況下CaptureActivity是啟動項,所以我們要在library的清單檔案中刪除CaptureActivity作為啟動項的配置。

OK,現在我的專案中有一個按鈕,點選這個按鈕我就可以掃描二維碼,程式碼如下:

    public void go(View view) {
        startActivity(new Intent(this, CaptureActivity.class));
    }

OK,至此,一個簡陋的二維碼掃描就完成了,大家有木有覺得很麻煩啊?麻煩也就這一次,因為我把ZXing當作library使用的時候系統會自動生成一個aar包,有了這個aar包,以後的開發就會變得非常簡單了,那麼這個aar包在哪裡呢?如下圖:

對應的資料夾路徑大家自己去找,有了這個aar包之後,如果我再需要使用二維碼掃描功能的時候就只需要如下幾個簡單的步驟:

1.建立專案

2.建立Module,在建立Module時選擇Import .JAR/.AAR Package,然後選擇剛剛的aar包

3.在我的app中引用這個Module即可。


最後,我將我自己生成的aar包提供給大家,可以直接在專案中使用,非常方便,不過建議還是大家自己嘗試生成一個aar包,整個過程還是非常有意思。

以上。