Andorid基于ZXing實(shí)現(xiàn)二維碼生成與掃描的示例代碼
最近遇上了掃描條碼的需求,在查找資料過程中不是那么地順利,做個筆記,記錄下這篇文章,前人栽樹后人乘涼。
本篇文章Demo下載
ZXing介紹
說到二維碼,大量的資料都會提到ZXing,具體見ZXing,這是一個用Java語言實(shí)現(xiàn)的1D/2D 條形碼圖像處理庫。涉及專業(yè)知識不多做介紹,這篇文章只講使用。
二維碼生成
引入ZXing核心庫:
implementation 'com.google.zxing:core:3.5.1'
創(chuàng)建二維碼位圖,寫了一個工具類,可以直接使用:
object QrCodeUtil { /** * 創(chuàng)建二維碼位圖 (支持自定義配置和自定義樣式) * @param content 字符串內(nèi)容 * @param width 位圖寬度,要求>=0(單位:px) * @param height 位圖高度,要求>=0(單位:px) * @param character_set 字符集/字符轉(zhuǎn)碼格式 (支持格式:{@link CharacterSetECI })。傳null時,zxing源碼默認(rèn)使用 "ISO-8859-1" * @param error_correction 容錯級別 (支持級別:{@link ErrorCorrectionLevel })。傳null時,zxing源碼默認(rèn)使用 "L" * @param margin 空白邊距 (可修改,要求:整型且>=0), 傳null時,zxing源碼默認(rèn)使用"4"。 * @param color_black 黑色色塊的自定義顏色值 * @param color_white 白色色塊的自定義顏色值 * @return */ fun createQRCodeBitmap( content: String, width: Int, height: Int, character_set: String = "UTF-8", error_correction: String = "H", margin: String = "1", @ColorInt color_black: Int = Color.BLACK, @ColorInt color_white: Int = Color.WHITE, ): Bitmap? { /** 1.參數(shù)合法性判斷 */ if (width < 0 || height < 0) { // 寬和高都需要>=0 return null } try { /** 2.設(shè)置二維碼相關(guān)配置,生成BitMatrix(位矩陣)對象 */ val hints: Hashtable<EncodeHintType, String> = Hashtable() if (character_set.isNotEmpty()) { hints[EncodeHintType.CHARACTER_SET] = character_set // 字符轉(zhuǎn)碼格式設(shè)置 } if (error_correction.isNotEmpty()) { hints[EncodeHintType.ERROR_CORRECTION] = error_correction // 容錯級別設(shè)置 } if (margin.isNotEmpty()) { hints[EncodeHintType.MARGIN] = margin // 空白邊距設(shè)置 } val bitMatrix = QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints) /** 3.創(chuàng)建像素數(shù)組,并根據(jù)BitMatrix(位矩陣)對象為數(shù)組元素賦顏色值 */ val pixels = IntArray(width * height) for (y in 0 until height) { for (x in 0 until width) { if (bitMatrix[x, y]) { pixels[y * width + x] = color_black // 黑色色塊像素設(shè)置 } else { pixels[y * width + x] = color_white // 白色色塊像素設(shè)置 } } } /** 4.創(chuàng)建Bitmap對象,根據(jù)像素數(shù)組設(shè)置Bitmap每個像素點(diǎn)的顏色值,之后返回Bitmap對象 */ val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) bitmap.setPixels(pixels, 0, width, 0, 0, width, height) return bitmap } catch (e: WriterException) { e.printStackTrace() } return null } }
使用到了ZXing核心功能的就是這句 QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints), 傳入內(nèi)容、樣式、寬高以及配置。
在配置EncodeHintType.CHARACTER_SET字符格式時,我們使用了"UTF-8",這是為了兼容中文,ZXing源碼默認(rèn)使用的是"ISO-8859-1",而"ISO-8859-1"本身是不支持中文的。
看一下使用效果:
val imageView = findViewById<ImageView>(R.id.iv) val bitmap = createQRCodeBitmap("你好,初次見面,請多指教!", 480, 480) imageView.setImageBitmap(bitmap)
生成帶logo小圖的二維碼
很多時候見到的二維碼中間都會帶有一個logo小圖,我們也來實(shí)現(xiàn)一下這樣子的效果。
其實(shí)就是把兩個bitmap繪制在一塊,在原有的方法上補(bǔ)充即可,添加兩個參數(shù):
object QrCodeUtil { /** * 創(chuàng)建二維碼位圖 (支持自定義配置和自定義樣式) * @param content 字符串內(nèi)容 * @param width 位圖寬度,要求>=0(單位:px) * @param height 位圖高度,要求>=0(單位:px) * @param character_set 字符集/字符轉(zhuǎn)碼格式 (支持格式:{@link CharacterSetECI })。傳null時,zxing源碼默認(rèn)使用 "ISO-8859-1" * @param error_correction 容錯級別 (支持級別:{@link ErrorCorrectionLevel })。傳null時,zxing源碼默認(rèn)使用 "L" * @param margin 空白邊距 (可修改,要求:整型且>=0), 傳null時,zxing源碼默認(rèn)使用"4"。 * @param color_black 黑色色塊的自定義顏色值 * @param color_white 白色色塊的自定義顏色值 * @param logoBitmap logo小圖片 * @param logoPercent logo小圖片在二維碼圖片中的占比大小,范圍[0F,1F],超出范圍->默認(rèn)使用0.2F。 * @return */ fun createQRCodeBitmap( content: String, width: Int, height: Int, character_set: String = "UTF-8", error_correction: String = "H", margin: String = "1", @ColorInt color_black: Int = Color.BLACK, @ColorInt color_white: Int = Color.WHITE, logoBitmap: Bitmap? = null, logoPercent: Float = 0f ): Bitmap? { /** 1.參數(shù)合法性判斷 */ if (width < 0 || height < 0) { // 寬和高都需要>=0 return null } try { /** 2.設(shè)置二維碼相關(guān)配置,生成BitMatrix(位矩陣)對象 */ val hints: Hashtable<EncodeHintType, String> = Hashtable() if (character_set.isNotEmpty()) { hints[EncodeHintType.CHARACTER_SET] = character_set // 字符轉(zhuǎn)碼格式設(shè)置 } if (error_correction.isNotEmpty()) { hints[EncodeHintType.ERROR_CORRECTION] = error_correction // 容錯級別設(shè)置 } if (margin.isNotEmpty()) { hints[EncodeHintType.MARGIN] = margin // 空白邊距設(shè)置 } val bitMatrix = QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints) /** 3.創(chuàng)建像素數(shù)組,并根據(jù)BitMatrix(位矩陣)對象為數(shù)組元素賦顏色值 */ val pixels = IntArray(width * height) for (y in 0 until height) { for (x in 0 until width) { if (bitMatrix[x, y]) { pixels[y * width + x] = color_black // 黑色色塊像素設(shè)置 } else { pixels[y * width + x] = color_white // 白色色塊像素設(shè)置 } } } /** 4.創(chuàng)建Bitmap對象,根據(jù)像素數(shù)組設(shè)置Bitmap每個像素點(diǎn)的顏色值,之后返回Bitmap對象 */ val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) bitmap.setPixels(pixels, 0, width, 0, 0, width, height) /** 5.為二維碼添加logo小圖標(biāo) */ if (logoBitmap != null) { return addLogo(bitmap, logoBitmap, logoPercent) } return bitmap } catch (e: WriterException) { e.printStackTrace() } return null } private fun addLogo(srcBitmap: Bitmap?, logoBitmap: Bitmap?, logoPercent: Float): Bitmap? { /** 1.參數(shù)合法性判斷 */ if (srcBitmap == null || logoBitmap == null) { return null } var percent = logoPercent if (logoPercent < 0F || logoPercent > 1F) { percent = 0.2F } /** 2. 獲取原圖片和Logo圖片各自的寬、高值 */ val srcWidth = srcBitmap.width val srcHeight = srcBitmap.height val logoWidth = logoBitmap.width val logoHeight = logoBitmap.height /** 3. 計算畫布縮放的寬高比 */ val scaleWidth = srcWidth * percent / logoWidth val scaleHeight = srcHeight * percent / logoHeight /** 4. 使用Canvas繪制,合成圖片 */ val bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888) val canvas = Canvas(bitmap) canvas.drawBitmap(srcBitmap, 0f, 0f, null) canvas.scale(scaleWidth, scaleHeight, (srcWidth / 2).toFloat(), (srcHeight / 2).toFloat()) canvas.drawBitmap(logoBitmap, srcWidth * 1f / 2 - logoWidth / 2, srcHeight * 1f / 2 - logoHeight / 2, null) return bitmap } }
看一下使用效果:
val imageViewLogo = findViewById<ImageView>(R.id.iv_logo) val logo = BitmapFactory.decodeResource(resources, R.drawable.cat) val bitmapLogo = createQRCodeBitmap( content = "你好,初次見面,請多指教!", width = 480, height = 480, logoBitmap = logo, logoPercent = 0.3f ) imageViewLogo.setImageBitmap(bitmapLogo)
二維碼掃描
借助開源庫 ZXing Android Embedded 實(shí)現(xiàn)二維碼掃描。
ZXing Android Embedded 是用于Android的條形碼掃描庫,使用ZXing進(jìn)行解碼。
更多的使用可以下載源碼工程跑下樣例查看,包括設(shè)置前后攝像頭、設(shè)置掃描超時時間等,該篇文章就只介紹最基本的二維碼掃描使用。
引入庫:
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
使用相機(jī)掃描二維碼
跳轉(zhuǎn)到掃描頁面后會自動開始掃描,掃描到結(jié)果后會將結(jié)果返回,onActivityResult廢棄之后,使用Activity Result API獲取頁面回傳數(shù)據(jù)
private val barcodeLauncher = registerForActivityResult( ScanContract() ) { result: ScanIntentResult -> if (result.contents == null) { val originalIntent = result.originalIntent if (originalIntent == null) { Toast.makeText(this@MainActivity, "Cancelled", Toast.LENGTH_LONG).show() } else if (originalIntent.hasExtra(Intents.Scan.MISSING_CAMERA_PERMISSION)) { Toast.makeText(this@MainActivity, "Cancelled due to missing camera permission", Toast.LENGTH_LONG) .show() } } else { Toast.makeText(this@MainActivity, "Scanned: " + result.contents, Toast.LENGTH_LONG).show() } }
findViewById<Button>(R.id.bt).setOnClickListener { barcodeLauncher.launch(ScanOptions()) }
這是使用默認(rèn)的掃描頁面,使用方法很簡單,但是更多的情況下,我們都需要自定義掃描頁面樣式。
自定義CustomScannerActivity,啟動掃描時設(shè)置CaptureActivity即可:
findViewById<Button>(R.id.bt2).setOnClickListener { val options = ScanOptions().setOrientationLocked(false).setCaptureActivity( CustomScannerActivity::class.java ) barcodeLauncher.launch(options) }
從相冊中識別二維碼圖片
在樣例中并沒有找到從相冊中識別二維碼圖片的方法,最終在issue中發(fā)現(xiàn)有提到同樣的問題以及解答。
打開相冊獲取圖片:
private fun openGallery() { val intent = Intent() intent.type = "image/*" intent.action = Intent.ACTION_GET_CONTENT openGalleryRequest.launch(Intent.createChooser(intent, "識別相冊二維碼圖片")) }
private val openGalleryRequest = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { if (it.resultCode == RESULT_OK) { it.data?.data?.let { uri -> handleImage(uri) } } }
解析圖片二維碼:
private fun handleImage(uri: Uri) { try { val image = MediaStore.Images.Media.getBitmap(this.contentResolver, uri) val intArray = IntArray(image.width * image.height) image.getPixels(intArray, 0, image.width, 0, 0, image.width, image.height) val source = RGBLuminanceSource(image.width, image.height, intArray) val reader = MixedDecoder(MultiFormatReader()) var result = reader.decode(source) if (result == null) { result = reader.decode(source) } Toast.makeText(this@MainActivity, "Scanned: ${result?.text}", Toast.LENGTH_LONG).show() } catch (e: Exception) { e.printStackTrace() } }
上述方法從相冊中識別二維碼圖片,發(fā)現(xiàn)存在識別失敗的問題,尤其是商品條形碼,使用相機(jī)掃描商品條形碼是可以正常掃描識別出來的,但是將商品條形碼拍照保存進(jìn)相冊,使用從相冊中識別二維碼圖片方法,卻出現(xiàn)識別失敗的情況。
為此,又去查找了其他的資料,見下一篇文章Android基于MLKit實(shí)現(xiàn)條形碼掃碼的代碼示例_Android_腳本之家 (jb51.net)
以上就是Andorid基于ZXing實(shí)現(xiàn)二維碼生成與掃描的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Andorid ZXing實(shí)現(xiàn)二維碼的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android Handler消息派發(fā)機(jī)制源碼分析
這篇文章主要為大家詳細(xì)分析了Android Handler消息派發(fā)機(jī)制源碼,感興趣的小伙伴們可以參考一下2016-07-07Android 分析實(shí)現(xiàn)性能優(yōu)化之啟動速度優(yōu)化
在移動端程序中,用戶希望的是應(yīng)用能夠快速打開。啟動時間過長的應(yīng)用不能滿足這個期望,并且可能會令用戶失望。輕則鄙視你,重則直接卸載你的應(yīng)用2021-11-11Android MarginDesign控件TabLayout導(dǎo)航欄使用詳解
這篇文章主要為大家詳細(xì)介紹了Android MarginDesign控件TabLayout導(dǎo)航欄使用,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-01-01Android中ActionBar以及menu的代碼設(shè)置樣式
這篇文章主要介紹了Android中ActionBar以及menu的代碼設(shè)置樣式的相關(guān)資料,需要的朋友可以參考下2015-07-07android判斷應(yīng)用是否已經(jīng)啟動的實(shí)例
這篇文章主要介紹了android判斷應(yīng)用是否已經(jīng)啟動的實(shí)例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03Android實(shí)現(xiàn)有視差效果的ListView
這篇文章給大家詳解介紹了在Android中如何實(shí)現(xiàn)帶有視差效果的ListView,文章給出了示例代碼相信對大家的理解和學(xué)習(xí)更有幫助,有需要的朋友們下面來一起看看吧。2016-09-09Android中微信搶紅包助手的實(shí)現(xiàn)詳解
本篇文章主要介紹了Android中微信搶紅包助手的實(shí)現(xiàn)詳解,通過利用AccessibilityService輔助服務(wù),監(jiān)測屏幕內(nèi)容,如監(jiān)聽狀態(tài)欄的信息,屏幕跳轉(zhuǎn)等,以此來實(shí)現(xiàn)自動拆紅包的功能,有興趣的可以了解一下。2017-02-02RxJava和Retrofit2的統(tǒng)一處理單個請求示例詳解
這篇文章主要給大家介紹了關(guān)于RxJava和Retrofit2的統(tǒng)一處理單個請求的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11