zxing開源庫的基本使用

来源:https://www.cnblogs.com/yishaochu/archive/2018/10/25/9852730.html
-Advertisement-
Play Games

如果你的項目中有模塊跟二維碼相關的話,那你一定聽過或者用過大名鼎鼎的zxing開源庫。 什麼是zxing? ZXing是一個開源的,用Java實現的多種格式的1D/2D條碼圖像處理庫,它包含了聯繫到其他語言的埠。zxing可以實現使用手機的內置的攝像頭完成條形碼的掃描及解碼。 本篇文章就來學習zx ...


如果你的項目中有模塊跟二維碼相關的話,那你一定聽過或者用過大名鼎鼎的zxing開源庫。

什麼是zxing?

ZXing是一個開源的,用Java實現的多種格式的1D/2D條碼圖像處理庫,它包含了聯繫到其他語言的埠。zxing可以實現使用手機的內置的攝像頭完成條形碼的掃描及解碼。

本篇文章就來學習zxing的基本使用,學習了以下幾個內容就能滿足大部分項目中的二維碼相關需求:

  • 通過攝像頭掃描二維碼圖片,讀取圖片內容
  • 從相冊中選取二維碼圖片,讀取圖片內容
  • 自己輸入字元串內容,生成二維碼圖片
  • 長按識別自己生成的二維碼圖片

如何依賴zxing到項目中?

如果你還在使用zxing的jar包、或者你是把zxing的代碼複製到項目中,使用這兩種方式依賴的話那就out了,現在Android Studio可支持zxing線上依賴,目前最新版本是3.3.3。線上依賴的好處我就不多說了,相信大家都懂。

新建項目,在app/build.gradle文件中線上依賴:

implementation 'com.google.zxing:core:3.3.3'

加入許可權

因為掃描二維碼需要攝像頭許可權,把圖片保存到本地需要sdcard許可權,所以需要在AndroidManifest.xml中加入相應的許可權

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

當然現在市面上的手機大部分都是6.0以上的操作系統了,所以還得在MainActivity的onCreate方法中動態申請以上這兩個許可權。

 //6.0版本或以上需請求許可權
String[] permissions=new String[]{Manifest.permission.
        WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA};
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
    requestPermissions(permissions,PERMS_REQUEST_CODE);
}

掃描二維碼

項目依賴進來了,許可權也有了,開始用代碼實現第一個功能。點擊掃描二維碼按鈕,開啟一個ScanActivity,這個Activity是我之前封裝好的,裡面處理了掃描二維碼的整個流程,掃描成功後會把掃描結果返回。ScanActivity類的代碼有點多,就不貼出來了,有興趣的自己看源碼。

Intent intent = new Intent(MainActivity.this,ScanActivity.class);
startActivityForResult(intent,SCAN_REQUEST_CODE);

重寫onActivityResult方法,監聽掃描結果。

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);

    if (requestCode == SCAN_REQUEST_CODE && resultCode == RESULT_OK) {
        String input = intent.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
        showToast("掃描結果:"+input);
    }
}

從相冊中選擇二維碼圖片進行識別

首先啟動系統相冊,從相冊中選擇一張圖片。

Intent innerIntent = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
Intent wrapperIntent = Intent.createChooser(innerIntent, "選擇二維碼圖片");
startActivityForResult(wrapperIntent, SELECT_IMAGE_REQUEST_CODE);

然後在onActivityResult中獲取選擇圖片路徑,調用BitmapUtil.parseQRcode方法解析二維碼圖片。

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);

    if(requestCode==SELECT_IMAGE_REQUEST_CODE){//從圖庫選擇圖片
        String[] proj = {MediaStore.Images.Media.DATA};
        // 獲取選中圖片的路徑
        Cursor cursor = this.getContentResolver().query(intent.getData(),proj, null, null, null);
        if (cursor.moveToFirst()) {
            int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            String photoPath = cursor.getString(columnIndex);
            String result= BitmapUtil.parseQRcode(photoPath);
            if (!TextUtils.isEmpty(result)) {
                showToast("從圖庫選擇的圖片識別結果:"+result);
            } else {
                showToast("從圖庫選擇的圖片不是二維碼圖片");
            }
        }
        cursor.close();
    }
}

接下來看parseQRcode方法,

/**
 * 解析二維碼圖片
 * @param bitmapPath 文件路徑
 * @return
 */
public static String parseQRcode(String bitmapPath){
    Bitmap bitmap = BitmapFactory.decodeFile(bitmapPath, null);
    String result=parseQRcode(bitmap);
    return result;
}

從上面的方法中看到直接把文件路徑讀取成Bitmap,繼續調用parseQRcode方法把Bitmap對象傳進去,這裡用到了方法重載。

public static String parseQRcode(Bitmap bmp){
    bmp=comp(bmp);//bitmap壓縮  如果不壓縮的話在低配置的手機上解碼很慢

    int width = bmp.getWidth();
    int height = bmp.getHeight();
    int[] pixels = new int[width * height];
    bmp.getPixels(pixels, 0, width, 0, 0, width, height);

    QRCodeReader reader = new QRCodeReader();
    Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
    hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);//優化精度
    hints.put(DecodeHintType.CHARACTER_SET,"utf-8");//解碼設置編碼方式為:utf-8
    try {
        Result result = reader.decode(new BinaryBitmap(
        new HybridBinarizer(new RGBLuminanceSource(width, height, pixels))), hints);
        return result.getText();
    } catch (NotFoundException e) {
        Log.i("ansen",""+e.toString());
        e.printStackTrace();
    } catch (ChecksumException e) {
        e.printStackTrace();
    } catch (FormatException e) {
        e.printStackTrace();
    }
    return null;
}

如果傳入的是一個Bitmap對象,先調用comp方法對Bitmap進行壓縮(壓縮代碼這裡不貼出),獲取圖片寬高,把圖像的每個像素顏色轉為int值,存入pixels數組。

然後初始化QRCodeReader對象,調用decode方法進行解碼,這個方法有兩個參數,參數1是一個BinaryBitmap對象,第二個參數是一個Map類型,key的值是DecodeHintType枚舉類型,這裡我們put了兩個值,優化精度跟設置編碼方式為。這個方法還會返回一個Result對象,最後調用result.getText()方法獲取二維碼內容。

生成二維碼圖片

生成二維碼圖片調用CreateQRBitmp.createQRCodeBitmap方法生成,這個方法是我們自己封裝的,需要傳入兩個參數,參數1:圖片內容、參數2:二維碼圖片最中間顯示的logo(Bitmap對象)。

String contentString = etInput.getText().toString().trim();
if(TextUtils.isEmpty(contentString)){
    showToast("請輸入二維碼內容");
    return ;
}
Log.i("ansen","輸入的內容:"+contentString);
Bitmap portrait = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher);
//兩個方法,一個不傳大小,使用預設
qrCodeBitmap = CreateQRBitmp.createQRCodeBitmap(contentString, portrait);
ivQrImage.setImageBitmap(qrCodeBitmap);

createQRCodeBitmap源碼如下:

public static Bitmap createQRCodeBitmap(String content,Bitmap portrait) {
    // 用於設置QR二維碼參數
    Hashtable<EncodeHintType, Object> qrParam = new Hashtable<>();
    // 設置QR二維碼的糾錯級別——這裡選擇最高H級別
    qrParam.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
    // 設置編碼方式
    qrParam.put(EncodeHintType.CHARACTER_SET, "UTF-8");

    // 生成QR二維碼數據——這裡只是得到一個由true和false組成的數組
    // 參數順序分別為:編碼內容,編碼類型,生成圖片寬度,生成圖片高度,設置參數
    try {
        BitMatrix bitMatrix = new MultiFormatWriter().encode(content,
                BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, qrParam);

        // 開始利用二維碼數據創建Bitmap圖片,分別設為黑白兩色
        int w = bitMatrix.getWidth();
        int h = bitMatrix.getHeight();
        int[] data = new int[w * h];

        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {
                if (bitMatrix.get(x, y))
                    data[y * w + x] = 0xff000000;// 黑色
                else
                    data[y * w + x] = 0x00ffffff;// -1 相當於0xffffffff 白色
            }
        }

        // 創建一張bitmap圖片,採用最高的圖片效果ARGB_8888
        Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        // 將上面的二維碼顏色數組傳入,生成圖片顏色
        bitmap.setPixels(data, 0, w, 0, 0, w, h);
        if(portrait!=null){
            createQRCodeBitmapWithPortrait(bitmap,initProtrait(portrait));
        }
        return bitmap;
    } catch (WriterException e) {
        e.printStackTrace();
    }
    return null;
}

大部分代碼都有註釋,首先就是調用MultiFormatWriter對象的encode方法生成BitMatrix對象,這裡我們傳入5個參數,參數1:內容、參數2:二維碼格式、參數3:圖片寬、參數4:圖片高、參數5:二維碼生成的參數(例如編碼方法以及糾錯級別)。

拿到BitMatrix對象後開始利用二維碼數據創建Bitmap圖片,分別設為黑白兩色,創建一個寬高一樣的Bitmap對象,調用setPixels方法把上面的二維碼顏色數組傳入,生成圖片顏色。如果中間需要添加logo調用createQRCodeBitmapWithPortrait方法。最後把Bitmap對象返回。

長按識別二維碼以及保存圖片

識別二維碼跟從相冊中選擇圖片進行識別功能上很相似,所以就不在做重覆介紹了,就介紹一下保存圖片功能。

從下麵源碼中看到,首先獲取rootView,從rootView中獲取根佈局的Bitmap,然後調用ImageUtil.savePicToLocal方法保存圖片。

View view = getWindow().getDecorView().getRootView();//找到當前頁面的根佈局
view.setDrawingCacheEnabled(true);//禁用繪圖緩存
view.buildDrawingCache();

Bitmap temBitmap = view.getDrawingCache();
ImageUtil.savePicToLocal(temBitmap,MainActivity.this);

//禁用DrawingCahce否則會影響性能 ,而且不禁止會導致每次截圖到保存的是緩存的點陣圖
view.setDrawingCacheEnabled(false);//識別完成之後開啟繪圖緩存

showToast("保存圖片到本地成功");

ImageUtil.savePicToLocal方法也比較簡單,就是把一個Bitmap保存到本地Sdcard上。需要註意的是記得發送一個廣播,不然需要重啟手機才能在系統相冊中看到這個圖片。

public static void savePicToLocal(Bitmap bitmap, Context context) {
    String filePath=Environment.getExternalStorageDirectory()
    .getAbsolutePath() + "/screen"+File.separator + System.currentTimeMillis() + ".png";
    if (bitmap != null) {
        try {
            // 圖片文件路徑
            Log.i("ansen", "filePath:" + filePath);
            File file = new File(filePath);
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            FileOutputStream os = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
            Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
            Uri uri = Uri.fromFile(new File(filePath));
            intent.setData(uri);
            context.sendBroadcast(intent);
            os.flush();
            os.close();
        } catch (Exception e) {
        }
    }
}

代碼終於寫完了,接下來看看效果,由於模擬器沒有攝像頭,而真機又不能錄製Gif圖片,所以攝像頭掃描二維碼就不演示啦,大家自己下載源碼運行查看效果。

image

當然少不了源碼,下載地址如下:

https://github.com/ansen666/ZxingTest

如果你想第一時間看我的後期文章,掃碼關註公眾號,長期推送Android開發文章、最新動態、開源項目,讓你各種漲姿勢。

      Android開發666 - 安卓開發技術分享
             掃描二維碼加關註

Android開發666


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 任務Runnable定義了一個可以獨立運行的代碼片段,通常用於界面控制項的延遲處理,比如有時為了避免同時占用某種資源造成衝突,有時則是為了反覆間隔刷新界面從而產生動畫效果。運行一個任務也有多種形式,既可在UI線程中調用處理器對象的post或者postDelayed方法,也能另外開啟分線程來執行Runn ...
  • 微信里搜“青少兒書畫”就行了。任何人可以任意發佈自己的寶寶的作品啊。發佈作品方式有2種,一種是電腦登錄網站上用瀏覽器(chrome瀏覽器、opera瀏覽器,firefox瀏覽器等,ie的不行)發佈作品。網址是https://zsj.itdos.com/project/26171另外一種方式是用小程式 ...
  • 註釋以 <!-- 開始並以 --> 結束,例如 <!--註釋內容-->。註釋可以出現在文檔序言中,包括文檔類型定義 (DTD);文檔之後;或文本內容中。 註釋不能出現在屬性值中。 不能出現在標記中。分析器在遇到 > 時,就認為註釋已結束;然後繼續將文檔作為正常的 XML 處理。 因此,字元串 > 不 ...
  • 筆者在進行安卓開發時,發現自己的代碼語法完全沒有問題。尤其是創建intent對象的時候,語法完全是正確的,但是Android Stuidio卻顯示報錯,Intent類顯示為紅色,如圖所示: 代碼如下所示: 利用滑鼠指向Intent編譯器就會顯示: 我相信很多朋友也遇到了這個問題,至於我們該如何解決這 ...
  • 本篇針對Surface模塊進行分析,從Java層的Activity創建開始,到ViewRoot、WindowsManagerService,再到JNI層和Native層。 首先推薦一個Android源碼查看的網站:http://androidxref.com/ Surface的創建涉及三個部分: A ...
  • 有些App在點擊下載按鈕的時候,可以在按鈕上顯示進度,我們可以通過繼承原生Button,重寫onDraw來實現帶進度條的按鈕。 Github:https://github.com/imcloudfloating/ProgressBar 1.效果: 2.原理: 創建三個GradientDrawable ...
  • 這次想來講講系統應用集成過程中遇到的一些坑,尤其是 so 文件相關的坑。 背景 埋這些坑的最初來源是由於測試人員在集成新終端設備時提了個 bug: app 在這個設備上無法啟動。 隨後拋來了一份日誌,過濾了下,最重要的其實就一條,crash 日誌: app 使用了 fresco 圖片庫,最初猜想是不 ...
  • 歡迎大家前往 "騰訊雲+社區" ,獲取更多騰訊海量技術實踐乾貨哦~ 本文由 "WeTest質量開放平臺團隊" 發表於 "雲+社區專欄" 作者:陳裕發, 騰訊系統測試工程師 商業轉載請聯繫騰訊WeTest獲得授權,非商業轉載請註明出處。 原文鏈接: " http://wetest.qq.com/lab ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...