【Java,二维码】Java 使用 swetake 生成二维码图片

by Microanswer

标签: java 二维码 qrcode swetake


创建时间:Sep 13, 2019 11:12:51 AM | 最后更新:Sep 26, 2019 12:18:33 PM 


重要声明:本文章仅仅代表了作者个人对此观点的理解和表述。读者请查阅时持自己的意见进行讨论。

二维码又称二维条码,是1994年由日本DW公司发明。常见的二维码为QR Code,QR全称Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的Bar Code条形码能存更多的信息,也能表示更多的数据类型。

一、分类和步骤

按照二维码的原理,通常将二维码分为两类,分别是:堆叠式/行排式矩阵式。平时生活中常接触到的就是矩阵式二维码。偶尔也能看到在某些包装设备上看到堆叠式/行排式二维码。下图展示了它们的区别和外观。

图1

人们最常用的二维码是矩阵式,因此本文所介绍的内容也是矩阵式的二维码。二维码从原始数据到图像生成,笔者将其归纳为两步:

  1. 计算:根据原始数据,计算出一个二维数组。
  2. 绘制:根据二维数组里每个元素的true和false判断绘制黑点还是白点。

二、计算二维码

二维码绘制这里将通过第三方jar包完成,因此需首先下载jar包。

1、下载 jar

日本DW公司发明了二维码,同时我也发现了一个日本网站里写的二维码工具,我们不必深究二维码的具体原理,就可以直接下载开发工具包进行二维码绘制了。首先打开其工具官网 http://www.swetake.com 就可以很简单的下载到java开发包了,除了java开发包,该网站还提供了适用于php的,本文将只对java的进行讲解。如果你打开这个网站有难度,你可以直接通过此地址下载:链接: 百度网盘 提取码: qz22

2、使用 swetake

下载完成后,提取出其中 lib 目录下的 Qrcode.jar 包,将其放在你自己的项目依赖里就可以开始使用了。这个工具使用方式非常简单,它只提供了一个类,几乎没有比这更简单的实现二维码计算的了。这个类就是 com.swetake.util.Qrcode 它提供了一个公共的无参构造函数,意味着你可以不需要任何参数,直接构建一个对象。下面示列了一个非常容易的计算二维码的代码:

// 创建二维码工具对象。
Qrcode qrcode = new Qrcode();

// 使用内容计算二维码结果。
boolean[][] booleans = qrcode.calQrcode("Hello Qrcode!".getBytes("UTF-8"));

是的,只需要这两行代码即可将字符串"Hello Qrcode!"的二维码结果计算出来。calQrcode 方法需要二进制数组作为参数,因此将字符串通过UTF-8编码获取到二进制数组,然后传入进行计算。由于二维码图像只有两种颜色,要么某个点位是黑色的、要么某个点位不是黑色的。就这两种状态。何不就使用一个 boolean 二位数组来表达。咱们先不说如何得到图形,通过嵌套for循环,不如先看看这个二维数组的庐山真面目:

for (boolean[] aBoolean : booleans) {
    for (boolean b : aBoolean) {
        System.out.print((b ? "■" : " ") + " ");
    }
    System.out.println();
}

执行打印之后,即可看到下图的结果:

不过很遗憾,这张图很难用手机扫描出结果。不着急,这是因为我们使用了不规范的方式生成的图像,中间毕竟生成了一些空格。一旦你距离此图远一点扫描(1.5米左右),也一样能很快识别。但至少目前我们看到了这个二维数组的真实结果,它无疑可以作为依据来绘制一张无误的二维码图像。

三、绘制二维码

绘制二维码涉及到了图像的生成,不同平台下针对图像的生成有不同的解决方案,因此本文将针对不同平台进行分别介绍。

1、Java服务端绘制

使用Java绘制图像可使用 BufferedImage 进行实现。这个类是java提供的 AWT抽象窗口工具包 下的类。因此请注意,在部分 Android 平台下此类可能无法使用,下文将同样给出在Android 下绘制二维码的方法。那么使用 BufferedImage 可常用于Java服务端的二维码生成。

BufferedImage 的构造函数共有3个,最简单的一个构造函数都需要三个参数。宽度、高度以及图像类型,相信这些都非常好理解。那不如先来试一试绘制:

Qrcode qrcode = new Qrcode();
boolean[][] booleans = qrcode.calQrcode("Hello Qrcode!".getBytes("UTF-8"));

// 建立图像对象,大小直接使用boolean二维数组的长度。
BufferedImage image = new BufferedImage(booleans.length, booleans.length, BufferedImage.TYPE_INT_RGB);
// 拿到图像画板,所有绘制操作都通过此对象完成。
Graphics graphics = image.getGraphics();

// 遍历绘制
for (int i = 0; i < booleans.length; i++) {
    boolean[] bs = booleans[i];
    for (int i1 = 0; i1 < bs.length; i1++) {
        boolean b = bs[i1];

        // 如果为真就绘制黑色,为假就绘制白色。
        graphics.setColor(b ? Color.BLACK : Color.WHITE);
        // 在对应位置绘制1像素点的矩形。
        graphics.fillRect(i, i1, 1, 1);
    }
}
graphics.dispose();

// 将图像输出到文件
ImageIO.write(image, "jpg", new File("D:/qrcode_demo.jpg"));

上述代码中图像类型为RGB,这是没有透明颜色支持,如果需要白色区域是透明的,可以使用ARGB类型的图像模式。现在运行程序,打开D盘,找到文件:"D:/qrcode_demo.jpg",是的你要的二维码图像来了,看下图:

图3 <=这里有图的。

很小。因为我们在构造图像大小的时候,使用数组的长度作为图像的宽度和高度,这样就使得一个点位仅有一个像素点。为了能让图像变大我们应该让一个点位绘制在更多的像素点上,让图像变大。

但是注意,考虑到图像的清晰度和完整性,目前我们已经有了二维码的boolean数组结果,也就意味着我们知道这个二维码的宽高有几个点了。如果我们随便指定一个图像宽度而这个宽度不是数组长度的整数倍,那么在绘制时势必会出现数组越界或者图像超出等问题。比如,上方的二维码结果数组长度是21, 希望将这个数组绘制在一个宽度为100的图像上,而恰巧100不是21的整数倍,这是无疑就为我们的绘制带来了更多的问题。

难道就不能画在一个100宽高的图上了吗,没有一个优雅的方法吗?当然有,不过这只是笔者想出的解决方案,自己如果有更完美的方案当然可以忽略此方案。具体实现就是:可以通过图像缩放的方式将图像缩放到我预期的大小。最初的绘制还是绘制为1点1像素的大小,后期通过缩放实现具体的大小。这样不就可以将任意点数的二维码绘制到我指定的图像大小上了。也保证扫描成功的可能性。那么现在可以据此来修缮一下上方的绘制代码:

Qrcode qrcode = new Qrcode();

boolean[][] booleans = qrcode.calQrcode("Hello Qrcode!".getBytes("UTF-8"));

// 建立图像对象,大小直接使用boolean二维数组的长度。
BufferedImage image = new BufferedImage(booleans.length, booleans.length, BufferedImage.TYPE_INT_RGB);

// 拿到图像画板,所有绘制操作都通过此对象完成。
Graphics graphics = image.getGraphics();

// 遍历绘制
for (int i = 0; i < booleans.length; i++) {
    boolean[] bs = booleans[i];
    for (int i1 = 0; i1 < bs.length; i1++) {
        boolean b = bs[i1];

        // 如果为真就绘制黑色,为假就绘制白色。
        graphics.setColor(b ? Color.BLACK : Color.WHITE);

        // 在对应位置绘制1像素点的矩形。
        graphics.fillRect(i, i1, 1, 1);
    }
}
graphics.dispose();

// 将图像缩放到指定大小。
// 构造目标大小的图像
BufferedImage qrImg = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
graphics = qrImg.getGraphics();

// 将原始二维码缩放绘制到目标二维码图像上
graphics.drawImage(image, 0, 0 , qrImg.getWidth(), qrImg.getHeight(), null);

graphics.dispose();

// 将目标二维码图像输出到文件
ImageIO.write(qrImg, "jpg", new File("D:/qrcode_demo.jpg"));

现在,重新运行程序。将生成一个比原来更加适合且美观的二维码。如图:

图5

最终,整理一下代码,一个完整的绘制二维码的工具方法诞生了:

/**
 * 根据指定内容生成二维码图像。 此方法不一定适用于android。
 * @param content 文本内容。
 * @param size 二维码大小。
 * @return 二维码图像文件的二进制内容。
 */
public static byte[] getQrcodeImg(String content, int size) throws Exception {

    Qrcode qrcode = new Qrcode();

    boolean[][] booleans = qrcode.calQrcode(content.getBytes("UTF-8"));

    // 建立图像对象,大小直接使用boolean二维数组的长度。
    BufferedImage image = new BufferedImage(booleans.length, booleans.length, BufferedImage.TYPE_INT_RGB);

    // 拿到图像画板,所有绘制操作都通过此对象完成。
    Graphics graphics = image.getGraphics();

    // 遍历绘制
    for (int i = 0; i < booleans.length; i++) {
        boolean[] bs = booleans[i];
        for (int i1 = 0; i1 < bs.length; i1++) {
            boolean b = bs[i1];

            // 如果为真就绘制黑色,为假就绘制白色。
            graphics.setColor(b ? Color.BLACK : Color.WHITE);

            // 在对应位置绘制1像素点的矩形。
            graphics.fillRect(i, i1, 1, 1);
        }
    }
    graphics.dispose();

    // 将图像缩放到指定大小。
    // 构造目标大小的图像
    BufferedImage qrImg = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
    graphics = qrImg.getGraphics();

    // 将1原始二维码缩放绘制到目标二维码图像上
    graphics.drawImage(image, 0, 0 , qrImg.getWidth(), qrImg.getHeight(), null);

    graphics.dispose();

    // 将目标二维码图像输出
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    ImageIO.write(qrImg, "jpg", out);
    out.flush();

    return out.toByteArray();
}

2、Android端绘制

Android 中提供了 Bitmap 实现图像的绘制,因此使用 Bitmap 可以在任何一台 android设备中得到兼容。为了支持任意大小图形绘制二维码,android同样的将采取和上一节相同的方式,先绘制1点一像素的二维码,然后进行缩放到指定大小。由于原理相同,本节只对绘制做介绍。

Canvas 类提供了Android上绘制功能的完成,因此,绘制图像时,需要 canvas 和 bitmap 结合,才能完成。而完整的绘制还需要画笔Paint的加入,因此完整的绘制使用如下:

// 建立图像对象
Bitmap bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.RGB_565);

// 建立图像画板,所有绘制操作都通过此对象完成。
Canvas canvas = new Canvas(bitmap);
// 建立画笔
Paint paint = new Paint();

// 在画板左上角第一个像素点绘制一个红点:
paint.setColor(Color.red);
canvas.drawPoint(0, 0, paint);

有了上面的基础绘制知识后,便可以完成Android平台上的二维码图像的绘制了:

/**
 * 获取指定字符串的二维码图像。
 * @param content 字符串内容。
 * @param size 图像大小,单位px。
 * @return 返回Bitmap的二维码图形。
 * @throws Exception 错误。
 */
public static Bitmap getQrcodeImg(String content, int size) throws Exception {
    Qrcode qrcode = new Qrcode();
    boolean[][] booleans = qrcode.calQrcode(content.getBytes(Charset.forName("UTF-8")));
    // 建立图像对象,大小直接使用boolean二维数组的长度。
    Bitmap bitmap = Bitmap.createBitmap(booleans.length, booleans.length, Bitmap.Config.RGB_565);
    // 建立图像画板,所有绘制操作都通过此对象完成。
    Canvas canvas = new Canvas(bitmap);
    // 建立画笔
    Paint paint = new Paint();
    // 遍历绘制
    for (int i = 0; i < booleans.length; i++) {
        boolean[] bs = booleans[i];
        for (int i1 = 0; i1 < bs.length; i1++) {
            boolean b = bs[i1];
            // 如果为真就绘制黑色,为假就绘制白色。
            paint.setColor(b ? Color.BLACK : Color.WHITE);
            // 在对应位置绘制1像素点。
            canvas.drawPoint(i, i1, paint);
        }
    }

    // 建立目标大小的图像
    Bitmap qrBitMap = Bitmap.createBitmap(size, size, Bitmap.Config.RGB_565);
    Canvas qrCanvas = new Canvas(qrBitMap);

    // 将上面已绘制好的二维码缩放绘制到目标图上中。
    qrCanvas.drawBitmap(bitmap,
            new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()),
            new Rect(0, 0, qrBitMap.getWidth(), qrBitMap.getHeight()),
            paint
    );

    // 丢弃已不再需要的初始二维码。
    bitmap.recycle();

    // 返回目标图像。
    return qrBitMap;
}

使用本方法,案例效果如图:

四、注意事项

本文绘制的所有二维码都不包含图像四周的空白,一般来说,二维码四周都应该有一点空白。使用时务必按自己的需求对代码稍作修改,以达到四周留白的效果。


全文完, 转载请注明出处。 对你有帮助?不如赞一个吧:
发表评论(发表评论需要登录,你现在还没有登录。)

评论列表 (1条)

导航加载中…