【问题标题】:How to generate qr code with Image overlay in Gluon?如何在 Gluon 中生成带有图像叠加层的二维码?
【发布时间】:2019-02-11 10:55:10
【问题描述】:

我想生成必须将徽标放在中心的二维码。我检查了 zxing 库,并通过阅读此代码 (How to generate QR code with logo inside it?) 使用 Java 应用程序进行了检查。但据我所知,我们不能在 android/ios 中使用 swing。那么如何处理呢?

【问题讨论】:

  • 检查blog.idrsolutions.com/2012/11/…。不要使用SwingFXUtils#toFXImage,因为 Gluon Mobile 不支持 Swing 模块。
  • 什么意思?我不明白。
  • 拥有BufferedImage 后,您可以使用我在上面评论中发布的链接将其转换为JavaFX 图像。
  • 那么当我使用 WritableImage 和 PixelWriter 生成条形码时,如何转换为 bufferedImage?因为我需要 bufferImage 来将叠加层与条码结合起来
  • 根据问题中的“生成 QR”链接,叠加后没有 BufferedImage 吗?

标签: java gluon gluon-mobile


【解决方案1】:

根据您所指的link,接受的答案指向此post,您可以在其中看到生成QR 的技巧,该技巧允许隐藏其中心部分而不影响其通过QR 扫描仪的可读性,可以通过增加错误level来完成:

30% (H) 的纠错是 H 级的纠错应该导致 QRCode 仍然有效,即使它被 30% 遮挡

作为question 的后续,您可以在对文本进行编码时包含一个提示:

public Image generateQR(int width, int height) {
    File root = Services.get(StorageService.class)
            .flatMap(StorageService::getPrivateStorage)
            .orElseThrow(() -> new RuntimeException("Storage Service not found"));

    String uuid = Services.get(DeviceService.class)
            .map(DeviceService::getUuid)
            .orElse("123456789");

    MultiFormatWriter codeWriter = new MultiFormatWriter();

    // Add maximum correction
    Map<EncodeHintType, ErrorCorrectionLevel> hints = new HashMap<>();
    hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);

    try {
        BarcodeFormat format = BarcodeFormat.QR_CODE;
        BitMatrix bitMatrix = codeWriter.encode(uuid, format, 
               width, height, hints);  // <--- add hints
        ...
        return writableImage;
    }
}

您将获得一个没有覆盖的二维码图像。

下一步是将此图像与您的徽标图像结合起来,而不使用 Swing。但实际上我们不需要这样做:我们可以使用两个 ImageView 节点!

ImageView imageView = new ImageView();
imageView.setFitWidth(256);
imageView.setFitHeight(256);
imageView.setImage(service.generateQR(256, 256));

ImageView overlay = new ImageView(
     new Image(getClass().getResourceAsStream("/icon.png")));
overlay.setFitWidth(80);
overlay.setFitHeight(80);

StackPane combinedQR = new StackPane(imageView, overlay);
...

生成的代码仍然可读。

您还可以使用结果,为图像添加混合效果,例如:

overlay.setBlendMode(BlendMode.ADD);

但这将取决于您的徽标图像如何与 QR 融合。

最后,如果您仍然需要单个组合图像,您可以创建堆栈窗格的快照:

WritableImage snapshot = combinedQR.snapshot(new SnapshotParameters(), null);

【讨论】:

  • 它运行良好。请解释一下您是如何找到解决方案的。将来可能会对我有所帮助。提前谢谢
  • 我已经编辑了我的答案,链接到解释使用提示的帖子
  • 对于提示,我明白了。但我想知道为什么你知道使用两个 ImageView 的方式。我试图通过尝试找到不依赖于 Swing 来绘制图像的方法来解决这个问题(想想 WritableImage 和 PixelWriter 等),我也尝试使用 BufferedImage。但不是运气
  • 使用 JavaFX,您可以使用任意数量的节点和 StackPane 进行叠加,因为它会在保持居中的同时将它们堆积起来,因此如果您必须合成两个图像,自然的解决方案是在StackPane 中使用两个ImageView 节点。根本不需要 Swing。有意义吗?
  • 嗯,非常感谢您的解释
【解决方案2】:

受 José 回答的启发,我用 Kotlin 为 Android 设备编写了自己的解决方案。 首先,将 ZXing 添加到您的项目中:

implementation "com.google.zxing:core:3.4.0"

创建一个虚拟可绘制对象并将其放入您的res/drawable 文件夹中:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="@color/colorAccent" />
    <size
        android:width="48dp"
        android:height="48dp" />
</shape>

然后将以下扩展添加到您的项目中:

String.encodeAsQrCodeBitmap 将字符串转换为二维码位图。它还接受overlayBitmap,如果为null,则仅将字符串转换为二维码,您可以自定义二维码的颜色。

Bitmap.addOverlayToCenter 将两个位图合并为一个,并将叠加位图置于中心。

Int.dpToPx() 将 DP 转换为像素。

@Throws(WriterException::class)
fun String.encodeAsQrCodeBitmap(
    dimension: Int,
    overlayBitmap: Bitmap? = null,
    @ColorInt color1: Int = Color.BLACK,
    @ColorInt color2: Int = Color.WHITE
): Bitmap? {

    val result: BitMatrix
    try {
        result = MultiFormatWriter().encode(
            this,
            BarcodeFormat.QR_CODE,
            dimension,
            dimension,
            hashMapOf(EncodeHintType.ERROR_CORRECTION to ErrorCorrectionLevel.H)
        )
    } catch (e: IllegalArgumentException) {
        // Unsupported format
        return null
    }

    val w = result.width
    val h = result.height
    val pixels = IntArray(w * h)
    for (y in 0 until h) {
        val offset = y * w
        for (x in 0 until w) {
            pixels[offset + x] = if (result.get(x, y)) color1 else color2
        }
    }
    val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
    bitmap.setPixels(pixels, 0, dimension, 0, 0, w, h)

    return if (overlayBitmap != null) {
        bitmap.addOverlayToCenter(overlayBitmap)
    } else {
        bitmap
    }
}

fun Bitmap.addOverlayToCenter(overlayBitmap: Bitmap): Bitmap {

    val bitmap2Width = overlayBitmap.width
    val bitmap2Height = overlayBitmap.height
    val marginLeft = (this.width * 0.5 - bitmap2Width * 0.5).toFloat()
    val marginTop = (this.height * 0.5 - bitmap2Height * 0.5).toFloat()
    val canvas = Canvas(this)
    canvas.drawBitmap(this, Matrix(), null)
    canvas.drawBitmap(overlayBitmap, marginLeft, marginTop, null)
    return this
}

fun Int.dpToPx(): Int {
    return (this * Resources.getSystem().displayMetrics.density).toInt()
}

然后在您的 Fragment/Activity 中执行以下操作:从资源中获取叠加层,将 String 转换为 QR-Code 并将其显示在 ImageView 中:

try {
     val displayMetrics = DisplayMetrics()
     activity?.windowManager?.defaultDisplay?.getMetrics(displayMetrics)

     val size = displayMetrics.widthPixels.coerceAtMost(displayMetrics.heightPixels)

     val overlay = ContextCompat.getDrawable(requireContext(), R.drawable.dummy_round)
                ?.toBitmap(72.dpToPx(), 72.dpToPx())

     val bitmap = "https://www.example.com".encodeAsQrCodeBitmap(size, overlay)

     imageView.setImageBitmap(bitmap)
} catch (e: Exception) {
      // handle Errors here
}

结果:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-08
    • 1970-01-01
    • 1970-01-01
    • 2013-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多