之前星之音乐下载器有需要生成二维码功能,当时用的是一个开源库来实现的,但是没过多久,发现那个库依赖太多,有个http-client的依赖,把软件都搞大了一倍,而且有时候开发的时候下载依赖还报错,就想换个方案
本文基于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.使用
由于工具代码过多不便阅读,就先讲些使用,工具代码就放下面了
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.0 fitHeight = 200.0 } } }
3.工具库代码
/** * 二维码生成工具类 * Created by stars-one */ object QRCodeUtil { private var QRCODE_SIZE = 320 // 二维码尺寸,宽度和高度均是320 private var LOGO_SIZE = 80 // 二维码里logo的尺寸,宽高一致 80*80 private 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 = qrcodeSize LOGO_SIZE = logoSize BOTTOM_TEXT_SIZE = bottomTextSize FORMAT_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] = 1 val qrCodeWriter = QRCodeWriter( val bitMatrix = qrCodeWriter.encode(dataStr, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints val width = bitMatrix.width val height = bitMatrix.height var tempHeight = height if (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 } } // 判断是否添加logo if (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_SIZE val height = LOGO_SIZE val image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH val tag = BufferedImage(width, height, BufferedImage.TYPE_INT_RGB val g = tag.graphics g.drawImage(image, 0, 0, null // 绘制缩小后的图 g.dispose( src = image // 插入LOGO val graph = source.createGraphics( val x = (QRCODE_SIZE - width / 2 val y = (QRCODE_SIZE - height / 2 graph.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? { //生成image val defineWidth = QRCODE_SIZE val defineHeight = 20 val 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.WHITE g2.clearRect(0, 0, defineWidth, defineHeight g2.paint = Color.BLACK val context = g2.fontRenderContext //部署linux需要注意 linux无此字体会显示方块 val font = Font("宋体", Font.BOLD, BOTTOM_TEXT_SIZE g2.font = font val 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 / 2 g2.drawString(declareText, offset.toInt(, y.toInt( val graph = source.createGraphics( //开启文字抗锯齿 graph.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON //添加image val width = textImage.getWidth(null val height = textImage.getHeight(null val src: Image = textImage graph.drawImage(src, 0, QRCODE_SIZE - 8, width, height, Color.WHITE, null graph.dispose( } }