zxing開源庫的基本使用

發布日期:2019-06-25

如果你的項目中有模塊跟二維碼相關的話,那你一定聽過或者用過大名鼎鼎的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方法,監聽掃描結果。

@Overrideprotected 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方法解析二維碼圖片。

@Overrideprotected 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圖片,所以攝像頭掃描二維碼就不演示啦,大家自己下載源碼運行查看效果。

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

https://github.com/ansen666/ZxingTest

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

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

pc蛋蛋在线下载