Andorid基于ZXing实现二维码生成与扫描的示例代码
作者:愿天深海
最近遇上了扫描条码的需求,在查找资料过程中不是那么地顺利,做个笔记,记录下这篇文章,前人栽树后人乘凉。
本篇文章Demo下载
ZXing介绍
说到二维码,大量的资料都会提到ZXing,具体见ZXing,这是一个用Java语言实现的1D/2D 条形码图像处理库。涉及专业知识不多做介绍,这篇文章只讲使用。
二维码生成
引入ZXing核心库:
implementation 'com.google.zxing:core:3.5.1'
创建二维码位图,写了一个工具类,可以直接使用:
object QrCodeUtil { /** * 创建二维码位图 (支持自定义配置和自定义样式) * @param content 字符串内容 * @param width 位图宽度,要求>=0(单位:px) * @param height 位图高度,要求>=0(单位:px) * @param character_set 字符集/字符转码格式 (支持格式:{@link CharacterSetECI })。传null时,zxing源码默认使用 "ISO-8859-1" * @param error_correction 容错级别 (支持级别:{@link ErrorCorrectionLevel })。传null时,zxing源码默认使用 "L" * @param margin 空白边距 (可修改,要求:整型且>=0), 传null时,zxing源码默认使用"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.参数合法性判断 */ if (width < 0 || height < 0) { // 宽和高都需要>=0 return null } try { /** 2.设置二维码相关配置,生成BitMatrix(位矩阵)对象 */ val hints: Hashtable<EncodeHintType, String> = Hashtable() if (character_set.isNotEmpty()) { hints[EncodeHintType.CHARACTER_SET] = character_set // 字符转码格式设置 } if (error_correction.isNotEmpty()) { hints[EncodeHintType.ERROR_CORRECTION] = error_correction // 容错级别设置 } if (margin.isNotEmpty()) { hints[EncodeHintType.MARGIN] = margin // 空白边距设置 } val bitMatrix = QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints) /** 3.创建像素数组,并根据BitMatrix(位矩阵)对象为数组元素赋颜色值 */ 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 // 黑色色块像素设置 } else { pixels[y * width + x] = color_white // 白色色块像素设置 } } } /** 4.创建Bitmap对象,根据像素数组设置Bitmap每个像素点的颜色值,之后返回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), 传入内容、样式、宽高以及配置。
在配置EncodeHintType.CHARACTER_SET字符格式时,我们使用了"UTF-8",这是为了兼容中文,ZXing源码默认使用的是"ISO-8859-1",而"ISO-8859-1"本身是不支持中文的。
看一下使用效果:
val imageView = findViewById<ImageView>(R.id.iv) val bitmap = createQRCodeBitmap("你好,初次见面,请多指教!", 480, 480) imageView.setImageBitmap(bitmap)
生成带logo小图的二维码
很多时候见到的二维码中间都会带有一个logo小图,我们也来实现一下这样子的效果。
其实就是把两个bitmap绘制在一块,在原有的方法上补充即可,添加两个参数:
object QrCodeUtil { /** * 创建二维码位图 (支持自定义配置和自定义样式) * @param content 字符串内容 * @param width 位图宽度,要求>=0(单位:px) * @param height 位图高度,要求>=0(单位:px) * @param character_set 字符集/字符转码格式 (支持格式:{@link CharacterSetECI })。传null时,zxing源码默认使用 "ISO-8859-1" * @param error_correction 容错级别 (支持级别:{@link ErrorCorrectionLevel })。传null时,zxing源码默认使用 "L" * @param margin 空白边距 (可修改,要求:整型且>=0), 传null时,zxing源码默认使用"4"。 * @param color_black 黑色色块的自定义颜色值 * @param color_white 白色色块的自定义颜色值 * @param logoBitmap logo小图片 * @param logoPercent logo小图片在二维码图片中的占比大小,范围[0F,1F],超出范围->默认使用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.参数合法性判断 */ if (width < 0 || height < 0) { // 宽和高都需要>=0 return null } try { /** 2.设置二维码相关配置,生成BitMatrix(位矩阵)对象 */ val hints: Hashtable<EncodeHintType, String> = Hashtable() if (character_set.isNotEmpty()) { hints[EncodeHintType.CHARACTER_SET] = character_set // 字符转码格式设置 } if (error_correction.isNotEmpty()) { hints[EncodeHintType.ERROR_CORRECTION] = error_correction // 容错级别设置 } if (margin.isNotEmpty()) { hints[EncodeHintType.MARGIN] = margin // 空白边距设置 } val bitMatrix = QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints) /** 3.创建像素数组,并根据BitMatrix(位矩阵)对象为数组元素赋颜色值 */ 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 // 黑色色块像素设置 } else { pixels[y * width + x] = color_white // 白色色块像素设置 } } } /** 4.创建Bitmap对象,根据像素数组设置Bitmap每个像素点的颜色值,之后返回Bitmap对象 */ val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) bitmap.setPixels(pixels, 0, width, 0, 0, width, height) /** 5.为二维码添加logo小图标 */ 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.参数合法性判断 */ 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 实现二维码扫描。
ZXing Android Embedded 是用于Android的条形码扫描库,使用ZXing进行解码。
更多的使用可以下载源码工程跑下样例查看,包括设置前后摄像头、设置扫描超时时间等,该篇文章就只介绍最基本的二维码扫描使用。
引入库:
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
使用相机扫描二维码
跳转到扫描页面后会自动开始扫描,扫描到结果后会将结果返回,onActivityResult废弃之后,使用Activity Result API获取页面回传数据
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()) }
这是使用默认的扫描页面,使用方法很简单,但是更多的情况下,我们都需要自定义扫描页面样式。
自定义CustomScannerActivity,启动扫描时设置CaptureActivity即可:
findViewById<Button>(R.id.bt2).setOnClickListener { val options = ScanOptions().setOrientationLocked(false).setCaptureActivity( CustomScannerActivity::class.java ) barcodeLauncher.launch(options) }
从相册中识别二维码图片
在样例中并没有找到从相册中识别二维码图片的方法,最终在issue中发现有提到同样的问题以及解答。
打开相册获取图片:
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() } }
上述方法从相册中识别二维码图片,发现存在识别失败的问题,尤其是商品条形码,使用相机扫描商品条形码是可以正常扫描识别出来的,但是将商品条形码拍照保存进相册,使用从相册中识别二维码图片方法,却出现识别失败的情况。
为此,又去查找了其他的资料,见下一篇文章Android基于MLKit实现条形码扫码的代码示例_Android_脚本之家 (jb51.net)
以上就是Andorid基于ZXing实现二维码生成与扫描的示例代码的详细内容,更多关于Andorid ZXing实现二维码的资料请关注脚本之家其它相关文章!