之前星之音乐下载器有需要生成二维码功能,当时用的是一个开源库来实现的,但是没过多久,发现那个库依赖太多,有个http-client的依赖,把软件都搞大了一倍,而且有时候开发的时候下载依赖还报错,就想换个方案
于是在网上找了下解决方案,最终只需要依赖两个zxing的两个依赖即可实现功能
本文基于TornadoFx框架进行编写,封装工具代码是kotlin版本,工具类已经封装在common-controls库中
工具支持带logo图标,带底部文本的二维码生成
代码封装
1.引入依赖
<dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.0</version>
</dependency>
<dependency><groupId>com.google.zxing</groupId><artifactId>javase</artifactId><version>3.5.0</version>
</dependency>
2.使用
由于工具代码过多不便阅读,就先讲些使用,工具代码就放下面了
比较核心的就两个方法,如下面代码所示,其他的方法是带Swing关键字,就是生成Swing包中的Image对象
getQRcodeFxImg()
方法就是直接生成Fx的Image对象,可以JavaFx中直接使用
/*** 初始化设置** @param qrcodeSize 二维码尺寸,默认为320(即320*320)* @param logoSize logo图标尺寸,默认为80(即80*80)* @param bottomTextSize 底部文字大小,默认20px* @param qrcodeType 二维码图片格式,默认为png*/
fun initConfig(qrcodeSize: Int = 320, logoSize: Int = 80, bottomTextSize: Int = 20, qrcodeType: String = "PNG")/*** 生成二维码图片** @param data 二维码文本内容* @param logoPath 图标图片的路径* @param bottomText 底部文字* @return fx的img对象*/
fun getQRcodeFxImg(data: String?, logoPath: String?=null, bottomText: String?=null): WritableImage
使用的话也比较简单:
//得到的swing的image对象
val buImg = QRCodeUtil.getQRcodeFxImg("这是测试文本")
val buImg1 = QRCodeUtil.getQRcodeFxImg("这是测试文本", null, "底部文字")
val buImg2 = QRCodeUtil.getQRcodeFxImg("这是测试文本", "/x5.jpg", "底部文字")val list = listOf(buImg, buImg1, buImg2)hbox(20.0) {list.forEach {imageview(it) {fitWidth = 200.0fitHeight = 200.0}}
}
3.工具库代码
/*** 二维码生成工具类* Created by stars-one*/
object QRCodeUtil {private var QRCODE_SIZE = 320 // 二维码尺寸,宽度和高度均是320private var LOGO_SIZE = 80 // 二维码里logo的尺寸,宽高一致 80*80private var BOTTOM_TEXT_SIZE = 20 // 底部文本的文字大小private var FORMAT_TYPE = "PNG" // 二维码图片类型/*** 初始化设置** @param qrcodeSize 二维码尺寸,默认为320(即320*320)* @param logoSize logo图标尺寸,默认为80(即80*80)* @param bottomTextSize 底部文字大小,默认20px* @param qrcodeType 二维码图片格式,默认为png*/fun initConfig(qrcodeSize: Int = 320, logoSize: Int = 80, bottomTextSize: Int = 20, qrcodeType: String = "PNG") {QRCODE_SIZE = qrcodeSizeLOGO_SIZE = logoSizeBOTTOM_TEXT_SIZE = bottomTextSizeFORMAT_TYPE = qrcodeType}/*** 生成二维码图片** @param data 二维码文本内容* @param logoPath 图标图片的路径* @param bottomText 底部文字* @return*/fun getQRcodeFxImg(data: String?, logoPath: String?=null, bottomText: String?=null): WritableImage {val resources = ResourceLookup(this)val url = if (logoPath == null) {null} else {resources.url(logoPath)}val swingImg = getQRCodeSwingImg(data, url, bottomText)return SwingFXUtils.toFXImage(swingImg,null)}/*** 默认需要logo,无底部文字* 返回 BufferedImage 可以使用ImageIO.write(BufferedImage, "png", outputStream);输出** @param dataStr* @return 返回 BufferedImage 可以使用ImageIO.write(BufferedImage, "png", outputStream);输出*/@Throws(Exception::class)fun getQRCodeSwingImg(dataStr: String?): BufferedImage {return getQRCodeSwingImg(dataStr, null, null)}/*** 默认需要logo,无底部文字** @param dataStr* @return 返回字节数组*/@Throws(Exception::class)fun getQRCodeByte(dataStr: String?): ByteArray {val bufferedImage = getQRCodeSwingImg(dataStr, null, null)val outputStream = ByteArrayOutputStream()ImageIO.write(bufferedImage, FORMAT_TYPE, outputStream)return outputStream.toByteArray()}/*** 默认需要logo,包含底部文字 文字为空则不显示文字* 返回 BufferedImage 可以使用ImageIO.write(BufferedImage, "png", outputStream);输出** @param dataStr* @return*/@Throws(Exception::class)fun getQRCodeSwingImg(dataStr: String?, bottomText: String?): BufferedImage {return getQRCodeSwingImg(dataStr, null, bottomText)}/*** 默认需要logo,包含底部文字 文字为空则不显示文字** @param dataStr* @return 返回字节数组*/@Throws(Exception::class)fun getQRCodeByte(dataStr: String?, bottomText: String?): ByteArray {val bufferedImage = getQRCodeSwingImg(dataStr, null, bottomText)val outputStream = ByteArrayOutputStream()ImageIO.write(bufferedImage, FORMAT_TYPE, outputStream)return outputStream.toByteArray()}/*** 获取二维码图片** @param dataStr 二维码内容* @param needLogo 是否需要添加logo* @param bottomText 底部文字 为空则不显示* @return*/@Throws(Exception::class)fun getQRCodeSwingImg(dataStr: String?, url: URL?, bottomText: String?): BufferedImage {if (dataStr == null) {throw RuntimeException("未包含任何信息")}val hints = HashMap<EncodeHintType, Any?>()hints[EncodeHintType.CHARACTER_SET] = "utf-8" //定义内容字符集的编码hints[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.L //定义纠错等级hints[EncodeHintType.MARGIN] = 1val qrCodeWriter = QRCodeWriter()val bitMatrix = qrCodeWriter.encode(dataStr, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints)val width = bitMatrix.widthval height = bitMatrix.heightvar tempHeight = heightif (StringUtils.isNotBlank(bottomText)) {tempHeight = tempHeight + 12}val image = BufferedImage(width, tempHeight, BufferedImage.TYPE_INT_RGB)for (x in 0 until width) {for (y in 0 until height) {image.setRGB(x, y, if (bitMatrix[x, y]) -0x1000000 else -0x1)}}// 判断是否添加logoif (url != null) {insertLogoImage(image, url)}// 判断是否添加底部文字if (StringUtils.isNotBlank(bottomText)) {addFontImage(image, bottomText)}return image}/*** 插入logo图片** @param source 二维码图片* @throws Exception*/@Throws(Exception::class)private fun insertLogoImage(source: BufferedImage, url: URL) {var src: Image = ImageIO.read(url)val width = LOGO_SIZEval height = LOGO_SIZEval image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH)val tag = BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)val g = tag.graphicsg.drawImage(image, 0, 0, null) // 绘制缩小后的图g.dispose()src = image// 插入LOGOval graph = source.createGraphics()val x = (QRCODE_SIZE - width) / 2val y = (QRCODE_SIZE - height) / 2graph.drawImage(src, x, y, width, height, null)val shape: Shape = RoundRectangle2D.Float(x.toFloat(), y.toFloat(), width.toFloat(), width.toFloat(), 6f, 6f)graph.stroke = BasicStroke(3f)graph.draw(shape)graph.dispose()}private fun addFontImage(source: BufferedImage, declareText: String?) {//生成imageval defineWidth = QRCODE_SIZEval defineHeight = 20val textImage = BufferedImage(defineWidth, defineHeight, BufferedImage.TYPE_INT_RGB)val g2 = textImage.graphics as Graphics2D//开启文字抗锯齿g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON)g2.background = Color.WHITEg2.clearRect(0, 0, defineWidth, defineHeight)g2.paint = Color.BLACKval context = g2.fontRenderContext//部署linux需要注意 linux无此字体会显示方块val font = Font("宋体", Font.BOLD, BOTTOM_TEXT_SIZE)g2.font = fontval lineMetrics = font.getLineMetrics(declareText, context)val fontMetrics: FontMetrics = FontDesignMetrics.getMetrics(font)val offset = ((defineWidth - fontMetrics.stringWidth(declareText)) / 2).toFloat()val y = (defineHeight + lineMetrics.ascent - lineMetrics.descent - lineMetrics.leading) / 2g2.drawString(declareText, offset.toInt(), y.toInt())val graph = source.createGraphics()//开启文字抗锯齿graph.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON)//添加imageval width = textImage.getWidth(null)val height = textImage.getHeight(null)val src: Image = textImagegraph.drawImage(src, 0, QRCODE_SIZE - 8, width, height, Color.WHITE, null)graph.dispose()}
}