1. 程式人生 > >Android ZXing(二維碼)庫的全面使用解析

Android ZXing(二維碼)庫的全面使用解析

Android ZXing(二維碼)庫解析

本文原創,轉載請註明出處。
歡迎關注我的 簡書 ,關注我的專題 Android Class 我會長期堅持為大家收錄簡書上高質量的 Android 相關博文。

寫在前面:

春天到了,天氣轉暖,風吹走了北京的霧霾也帶來了睏倦。每天感覺就是睡不醒、起不來。前一陣研究了 View 的體系,還差滑動衝突和 View 的繪製沒有落筆成文,還看了很多關於 MVP 這種程式碼模式的文章,未來爭取把它們都整理分享出來,便於記憶和交流。

不知不覺二維碼已經深刻影響了我們的生活,為我們提供了極大的便利。線下付賬、租一輛單車、或者去要一個妹子的微訊號等等。張小龍把它稱為從線下到線上的入口。正因為二維碼如此的重要,並且出現的頻率越來越高,所以 Android 應用中掃面二維碼、條形碼的需求也很常見了。本文就是來接入使用一個不錯的二維碼庫 ZXing

二維碼是什麼

在研究 ZXing 之前,我一直好奇二維碼是根據什麼生成的,並且最大能儲存多少的資訊,去查閱了一下資料,正好解決了我的疑問,在此介紹一下。

二維條碼是指在一維條碼(條形碼)的基礎上擴展出另一維具有可讀性的條碼,使用黑白矩形圖案表示二進位制資料,被裝置掃描後可獲取其中所包含的資訊。一維條碼的寬度記載著資料,而其長度沒有記載資料。二維條碼的長度、寬度均記載著資料。二維條碼有一維條碼沒有的“定位點”和“容錯機制”。容錯機制在即使沒有辨識到全部的條碼、或是說條碼有汙損時,也可以正確地還原條碼上的資訊。二維條碼的種類很多,不同的機構開發出的二維條碼具有不同的結構以及編寫、讀取方法。

二維碼

總結起來就是二維碼是將有限的資訊轉成二進位制,並且表現為黑白矩陣圖,與一維的條形碼相比,二維碼擁有更好的容錯能力。

ZXing

現在我們大概知道了二維碼是怎麼生成的,有關 Android 上掃碼的庫有很多,這次來介紹的是 ZXing,一個出色的開源掃碼庫。

來看看它在 github 上的倉庫:

上面 ReadMe 檔案傳遞的資訊大概就是 ZXing 是個很厲害的庫,支援各種平臺等等…然後又找了它一些相關的連線,我發現有關 Android 的資訊少之又少,僅僅說了怎麼引入,具體使用上沒說,並且原始碼中給出的 Android Module 程式碼量有點多,掌握的成本太高,總之當時我覺得這個學習的姿勢不太對。

回到 google 重新搜尋了一下 Android ZXing,終於找到了一個正確的倉庫來引入並使用它:

這個庫是一個基於 ZXing 的 Android 二維碼解碼庫,使用起來還是非常簡單的。

我把它 fork 回之後,做了一些優化和更新,具體請檢視下文。

快速使用:

new IntentIntegrator(this).initiateScan(); // `this` is the current Activity


// Get the results:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
    if(result != null) {
        if(result.getContents() == null) {
            Toast.makeText(this, "Cancelled", Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show();
        }
    } else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

可以看到使用起來非常便捷,onActivityResult帶回掃碼出來的結果,然後進行處理就OK了。下文中就不再介紹 ReadMe 中的內容,轉而介紹一下這個庫一些其他的配置。

    protected Class<?> getDefaultCaptureActivity() {
        return CaptureActivity.class;
    }

一步步跟進 IntentIntegrator(this).initiateScan() 方法可以發現,當我不設定 CaptureActivity 時,呼叫預設的 Activity 就是 CaptureActivity

所以對於吊起的掃碼 Activity 來說,他的方向可以在 manifest 檔案中的 android:screenOrientation 屬性直接指定:

        <activity
            android:name="com.journeyapps.barcodescanner.CaptureActivity"
            android:screenOrientation="landscape"
            tools:replace="screenOrientation" />

除了最基本的掃碼使用,再來看看他 IntentIntegrator 中其它的 setXXX 屬性吧。

  • integrator.setPrompt 在掃描頁面新增一個文字描述,空字串時,可以取消顯示它。

  • integrator.setOrientationLocked 設定是否鎖定掃碼 Activity 的方向。預設為 true

  • integrator.setCameraId 設定使用攝像頭的 id,0 為後置攝像頭,1 為前置攝像頭。這是系統 CameraInfo 的屬性。

        /**
         * The facing of the camera is opposite to that of the screen.
         */
        public static final int CAMERA_FACING_BACK = 0;

        /**
         * The facing of the camera is the same as that of the screen.
         */
        public static final int CAMERA_FACING_FRONT = 1;
  • integrator.setBeepEnabled 設定掃描完成時是否允許“嘟嘟”的聲音。預設為 true。原始碼中的這個設定位於 BeepManager,通過 MediaPlayer 播放了一段 .ogg 檔案。

播放的檔案

  • integrator.setBarcodeImageEnabled 儲存掃描完成後二維碼的影象。原始碼在這裡:
    /**
     * Save the barcode image to a temporary file stored in the application's cache, and return its path.
     * Only does so if returnBarcodeImagePath is enabled.
     *
     * @param rawResult the BarcodeResult, must not be null
     * @return the path or null
     */
    private String getBarcodeImagePath(BarcodeResult rawResult) {
        String barcodeImagePath = null;
        if (returnBarcodeImagePath) {
            Bitmap bmp = rawResult.getBitmap();
            try {
                File bitmapFile = File.createTempFile("barcodeimage", ".jpg", activity.getCacheDir());
                FileOutputStream outputStream = new FileOutputStream(bitmapFile);
                bmp.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
                outputStream.close();
                barcodeImagePath = bitmapFile.getAbsolutePath();
            } catch (IOException e) {
                Log.w(TAG, "Unable to create temporary file and store bitmap! " + e);
            }
        }
        return barcodeImagePath;
    }
  • integrator.setDesiredBarcodeFormats 設定掃描的二維碼格式

  • integrator.setTimeout 設定一個超時時間,超過這個時間之後,掃描的 Activity 將會被 finish 。

自定義

通過昨天半天的時間,算是把這個庫看明白了,寫得很好值得推薦,有心的朋友可以好好看看原始碼,有的可學。

專案中的需求各有不同,所以在 UI 上可定製就成了很重要的一點,這個庫雖然沒開放出很多方法,但是自定義起來依然自由方便,一起來看看姿勢吧。

  • integrator.setCaptureActivity 我們可以通過這個方法,指定要傳入的自定義的掃描 Activity,那這個 Activity 應該怎麼去定製呢?
    <com.journeyapps.barcodescanner.DecoratedBarcodeView
        android:id="@+id/zxing_barcode_scanner"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:zxing_scanner_layout="@layout/custom_barcode_scanner">
    </com.journeyapps.barcodescanner.DecoratedBarcodeView>

DecoratedBarcodeView 是掃描 View 的一個封裝類,具體的 UI 屬性,在 app:zxing_scanner_layout="@layout/custom_barcode_scanner" 引入的 custom_barcode_scanner 佈局檔案中進行定製修改。來看看這個佈局:

<merge xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:tools="http://schemas.android.com/tools"
       xmlns:app="http://schemas.android.com/apk/res-auto">

    <com.journeyapps.barcodescanner.BarcodeView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/zxing_barcode_surface"
        app:zxing_framing_rect_width="250dp"
        app:zxing_framing_rect_height="50dp"/>

    <com.journeyapps.barcodescanner.ViewfinderView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/zxing_viewfinder_view"
        app:zxing_possible_result_points="@color/zxing_custom_possible_result_points"
        app:zxing_result_view="@color/zxing_custom_result_view"
        app:zxing_viewfinder_laser="@color/zxing_custom_viewfinder_laser"
        app:zxing_viewfinder_mask="@color/zxing_custom_viewfinder_mask"/>

    <TextView
        android:id="@+id/zxing_status_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center_horizontal"
        android:background="@color/zxing_transparent"
        android:text="@string/zxing_msg_default_status"
        android:textColor="@color/zxing_status_text"/>

</merge>

自定義掃描框

這個圖很直觀了吧,要是定製掃描線的顏色啊,或者掃描框的大小啊,都沒有問題了~

當然我把它 fork 回來之後,做了一些改動,比如修了兩個 bug,然後開放了兩個我認為比較合適的新的設定項:

  • setBeepResource 讓掃描之後發出的嘟嘟聲可定製,需要傳入本地的一個 raw 檔案。

  • setVibrateEnable 設定掃描完成之後,是否讓手機震動。

我做了修改之後的倉庫連線:

一直會持續更新它,需要的朋友保持關注吧~