本篇,笔者介绍下自己用java开发的一款QR码生成器图形化工具。
qrcode编码、解码功能依赖于google的二维码开源库google.zxing。
笔者项目github地址: https://github.com/jellyflu/qrTool
废话少讲,先上张最终运行效果图。
整体项目结构如下(开发IDE是STS)。
具体java实现代码如下 :
QRJFrame.java (主窗体)
package com.tingcream.qrTool.view;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.apache.commons.io.FileUtils;
import com.tingcream.qrTool.qrcode.QRCodeUtil;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class QRJFrame extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
private JPanel contentPane;
private JPanel panel1 ;//tab中面板1 面板2
private JLabel label1 ;//qr码 图形显示框
private JLabel label2;
private JLabel lblQr;
private JButton btn1;//panel1 生成
private JButton btn3;//panel1 清空
private JTextArea textArea1;//原文 文本域
private String recentDir;//最近访问的目录
private JButton btn4;// 打开img文件
private JButton btn5;//panel1 保存
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
QRJFrame frame = new QRJFrame();
frame.setVisible(true);
frame.setLocationRelativeTo(null);//主窗体居中
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
* @throws IOException
*/
public QRJFrame() throws IOException {
setResizable(false);
setFont(new Font("微软雅黑", Font.PLAIN, 18));
setTitle("QR生成器");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 464, 636);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
tabbedPane.setFont(new Font("微软雅黑", Font.PLAIN, 18));
contentPane.add(tabbedPane, BorderLayout.CENTER);
panel1 = new JPanel();
tabbedPane.addTab("QR生成", null, panel1, null);
label1 = new JLabel("");
label1.setVerticalAlignment(SwingConstants.TOP);
label1.setBackground(Color.WHITE);
setDefaultQrImg();
label1.setHorizontalAlignment(SwingConstants.LEFT);
label1.setFont(new Font("微软雅黑", Font.PLAIN, 18));
label2 = new JLabel("原文内容:");
label2.setFont(new Font("微软雅黑", Font.PLAIN, 18));
JScrollPane scrollPane = new JScrollPane();
lblQr = new JLabel("QR码 :");
lblQr.setFont(new Font("微软雅黑", Font.PLAIN, 18));
btn1 = new JButton("生成↓");
btn1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
generateQRImg(e);
}
});
btn1.setFont(new Font("微软雅黑", Font.PLAIN, 18));
btn3 = new JButton("清空");
btn3.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textArea1.setText("");
setDefaultQrImg();
}
});
btn3.setFont(new Font("微软雅黑", Font.PLAIN, 18));
btn5 = new JButton("保存");
btn5.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
saveQrImg(e);
}
});
btn5.setFont(new Font("微软雅黑", Font.PLAIN, 18));
btn4 = new JButton("选择");
btn4.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
openQrImg(e);
}
});
btn4.setFont(new Font("微软雅黑", Font.PLAIN, 18));
GroupLayout gl_panel1 = new GroupLayout(panel1);
gl_panel1.setHorizontalGroup(
gl_panel1.createParallelGroup(Alignment.LEADING)
.addGroup(gl_panel1.createSequentialGroup()
.addGroup(gl_panel1.createParallelGroup(Alignment.LEADING)
.addGroup(gl_panel1.createSequentialGroup()
.addGap(19)
.addGroup(gl_panel1.createParallelGroup(Alignment.TRAILING)
.addComponent(label2)
.addComponent(lblQr, GroupLayout.PREFERRED_SIZE, 76, GroupLayout.PREFERRED_SIZE)))
.addGroup(gl_panel1.createSequentialGroup()
.addContainerGap()
.addComponent(btn4, GroupLayout.PREFERRED_SIZE, 77, GroupLayout.PREFERRED_SIZE))
.addGroup(gl_panel1.createSequentialGroup()
.addContainerGap()
.addComponent(btn5, GroupLayout.PREFERRED_SIZE, 77, GroupLayout.PREFERRED_SIZE)))
.addPreferredGap(ComponentPlacement.RELATED)
.addGroup(gl_panel1.createParallelGroup(Alignment.LEADING, false)
.addGroup(gl_panel1.createSequentialGroup()
.addComponent(btn1)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(btn3, GroupLayout.PREFERRED_SIZE, 77, GroupLayout.PREFERRED_SIZE))
.addComponent(label1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(scrollPane))
.addContainerGap(39, Short.MAX_VALUE))
);
gl_panel1.setVerticalGroup(
gl_panel1.createParallelGroup(Alignment.LEADING)
.addGroup(gl_panel1.createSequentialGroup()
.addContainerGap()
.addGroup(gl_panel1.createParallelGroup(Alignment.LEADING)
.addComponent(label2)
.addComponent(scrollPane, GroupLayout.PREFERRED_SIZE, 152, GroupLayout.PREFERRED_SIZE))
.addPreferredGap(ComponentPlacement.RELATED)
.addGroup(gl_panel1.createParallelGroup(Alignment.BASELINE)
.addComponent(btn1)
.addComponent(btn3, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE))
.addGap(18)
.addGroup(gl_panel1.createParallelGroup(Alignment.LEADING)
.addGroup(gl_panel1.createSequentialGroup()
.addComponent(lblQr, GroupLayout.PREFERRED_SIZE, 24, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(btn5, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE)
.addGap(15)
.addComponent(btn4, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE))
.addComponent(label1))
.addContainerGap(21, Short.MAX_VALUE))
);
textArea1 = new JTextArea();
textArea1.setFont(new Font("微软雅黑", Font.PLAIN, 18));
scrollPane.setViewportView(textArea1);
panel1.setLayout(gl_panel1);
}
//显示默认qr图形 空白图片
private void setDefaultQrImg() {
label1.setIcon(new ImageIcon(QRJFrame.class.getResource("/com/tingcream/qrTool/img/blank_300_300.png")));
}
/**
* 保存qr图形 到指定文件中
* @author jelly
* @param e
*/
void saveQrImg(ActionEvent e) {
String text = textArea1.getText();
if(text==null||text.trim().equals("")) {
JOptionPane.showMessageDialog(null, "原文内容不能为空!");
return ;
}
FileOutputStream output=null;
try {
JFileChooser wjsave=null;
if(recentDir!=null&&recentDir.trim().length()>0){
wjsave=new JFileChooser(recentDir);
}else{
wjsave=new JFileChooser();
}
wjsave.setDialogType(JFileChooser.SAVE_DIALOG);
wjsave.setVisible(true);
wjsave.setDialogTitle("保存QR码到文件");
int value= wjsave.showSaveDialog(null);
if(value==JFileChooser.APPROVE_OPTION){//用户点击了保存
File savefile= wjsave.getSelectedFile();// 保存的文件
recentDir= savefile.getParent();//将文件目录保存到成员变量
output=new FileOutputStream(savefile);
QRCodeUtil.encode(text, null, output, true);
}
} catch (Exception e2) {
e2.printStackTrace();
}finally {
if(output!=null) {
try {
output.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
/**
* 从本地选择(打开)qr图片文件
* @author jelly
* @param e
*/
void openQrImg(ActionEvent e) {
try {
JFileChooser wjopen=null;
if(recentDir!=null&&recentDir.trim().length()>0){
wjopen=new JFileChooser(recentDir);
}else{
wjopen=new JFileChooser();
}
wjopen.setDialogTitle("打开QR码文件");
wjopen.setDialogType(JFileChooser.OPEN_DIALOG);
wjopen.setVisible(true);
wjopen.addChoosableFileFilter(new FileNameExtensionFilter("PNG文件(png)","png"));
int value=wjopen.showOpenDialog(null);
if(value==JFileChooser.APPROVE_OPTION){//用户点击了打开
File openfile= wjopen.getSelectedFile();
byte[] bytes =FileUtils.readFileToByteArray(openfile);
label1.setIcon(new ImageIcon(bytes));
String text =QRCodeUtil.decode(new FileInputStream(openfile));
textArea1.setText(text);
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
/**
* 生成qr图形 并在label1中显示
* @author jelly
* @param e
*/
void generateQRImg(ActionEvent e) {
String text = textArea1.getText();
if(text==null||text.trim().equals("")) {
JOptionPane.showMessageDialog(null, "原文内容不能为空!");
return ;
}
ByteArrayOutputStream output=new ByteArrayOutputStream();
try {
QRCodeUtil.encode(text, null, output, true);
} catch (Exception e1) {
e1.printStackTrace();
JOptionPane.showMessageDialog(null, e1.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
}
byte[] bytes =output.toByteArray();
label1.setIcon(new ImageIcon(bytes));
}
}
QRCodeUtil.java
package com.tingcream.qrTool.qrcode;
import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Shape;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Hashtable;
import javax.imageio.ImageIO;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.Result;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
/**
* 二维码工具类 google.zxing包
*
* @author jelly
*/
public class QRCodeUtil {
private static final String CHARSET = "utf-8";
private static final String FORMAT_NAME = "png";
// 二维码尺寸
private static final int QRCODE_SIZE = 300; //即 300 * 300 像素
// LOGO宽度
private static final int LOGO_WIDTH = 60;
// LOGO高度
private static final int LOGO_HEIGHT = 60;
/**
* 创建qrcode 二维码
* @param content 原文内容
* @param logoInput 可为null,若为null,则二维码中不会插入logo
* @param needCompress 是否需要颜色
* @return
* @throws Exception
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static BufferedImage createQrImage(String content,InputStream logoInput,
boolean needCompress) throws Exception {
Hashtable hints = new Hashtable();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix bitMatrix = new MultiFormatWriter().encode(content,
BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints);
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000
: 0xFFFFFFFF);
}
}
if (logoInput==null) {
return image;
}else {
// 插入logo图片
QRCodeUtil.insertLogo(image, logoInput, needCompress);
return image;
}
}
/**
* 图形二维码中插入logo图形
* @param source
* @param logoPath
* @param needCompress
* @throws Exception
*/
private static void insertLogo(BufferedImage source, InputStream logoInput,
boolean needCompress) throws Exception {
Image src =ImageIO.read(logoInput);
int width = src.getWidth(null);
int height = src.getHeight(null);
if (needCompress) { // 压缩LOGO
if (width > LOGO_WIDTH) {
width = LOGO_WIDTH;
}
if (height > LOGO_HEIGHT) {
height = LOGO_HEIGHT;
}
Image image = src.getScaledInstance(width, height,
Image.SCALE_SMOOTH);
BufferedImage tag = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
g.drawImage(image, 0, 0, null); // 绘制缩小后的图
g.dispose();
src = image;
}
// 插入LOGO
Graphics2D graph = source.createGraphics();
int x = (QRCODE_SIZE - width) / 2;
int y = (QRCODE_SIZE - height) / 2;
graph.drawImage(src, x, y, width, height, null);
Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
graph.setStroke(new BasicStroke(3f));
graph.draw(shape);
graph.dispose();
}
/**
* 二维码编码 xx
* @param text 原文内容
* @param logoIn logo图 ,可为null
* @param output 二维码图形输出outputstream
* @param needCompress 是否需要颜色
* @throws Exception
*/
public static void encode(String text,InputStream logoIn,OutputStream output,boolean needCompress) throws Exception {
BufferedImage image = createQrImage(text, logoIn, needCompress);
ImageIO.write(image, FORMAT_NAME, output);
}
/**
* 二维码解码 xx
* @param in 二维码图input流
* @return 原文内容
* @throws Exception
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static String decode(InputStream in) throws Exception {
BufferedImage image=ImageIO.read(in);
BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Hashtable hints = new Hashtable();
hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
Result result = new MultiFormatReader().decode(bitmap, hints);
return result.getText();
}
// public static void main(String[] args) throws Exception {
//
// //二维码编码
//// String text = "hello\n你好哈哈";
//// OutputStream output=new FileOutputStream(new File("d:/qr.png"));
//// QRCodeUtil.encode(text, null, output, true);
//
// //二维码解码
//// InputStream in=new FileInputStream(new File("d:/qr.png"));
//// String result=QRCodeUtil.decode(in);
//// System.out.println(result);
// }
}
BufferedImageLuminanceSource.java (google的LuminanceSource 封装)
package com.tingcream.qrTool.qrcode;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import com.google.zxing.LuminanceSource;
public class BufferedImageLuminanceSource extends LuminanceSource {
private final BufferedImage image;
private final int left;
private final int top;
public BufferedImageLuminanceSource(BufferedImage image) {
this(image, 0, 0, image.getWidth(), image.getHeight());
}
public BufferedImageLuminanceSource(BufferedImage image, int left,
int top, int width, int height) {
super(width, height);
int sourceWidth = image.getWidth();
int sourceHeight = image.getHeight();
if (left + width > sourceWidth || top + height > sourceHeight) {
throw new IllegalArgumentException(
"Crop rectangle does not fit within image data.");
}
for (int y = top; y < top + height; y++) {
for (int x = left; x < left + width; x++) {
if ((image.getRGB(x, y) & 0xFF000000) == 0) {
image.setRGB(x, y, 0xFFFFFFFF); // = white
}
}
}
this.image = new BufferedImage(sourceWidth, sourceHeight,
BufferedImage.TYPE_BYTE_GRAY);
this.image.getGraphics().drawImage(image, 0, 0, null);
this.left = left;
this.top = top;
}
public byte[] getRow(int y, byte[] row) {
if (y < 0 || y >= getHeight()) {
throw new IllegalArgumentException(
"Requested row is outside the image: " + y);
}
int width = getWidth();
if (row == null || row.length < width) {
row = new byte[width];
}
image.getRaster().getDataElements(left, top + y, width, 1, row);
return row;
}
public byte[] getMatrix() {
int width = getWidth();
int height = getHeight();
int area = width * height;
byte[] matrix = new byte[area];
image.getRaster().getDataElements(left, top, width, height, matrix);
return matrix;
}
public boolean isCropSupported() {
return true;
}
public LuminanceSource crop(int left, int top, int width, int height) {
return new BufferedImageLuminanceSource(image, this.left + left,
this.top + top, width, height);
}
public boolean isRotateSupported() {
return true;
}
public LuminanceSource rotateCounterClockwise() {
int sourceWidth = image.getWidth();
int sourceHeight = image.getHeight();
AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0,
0.0, 0.0, sourceWidth);
BufferedImage rotatedImage = new BufferedImage(sourceHeight,
sourceWidth, BufferedImage.TYPE_BYTE_GRAY);
Graphics2D g = rotatedImage.createGraphics();
g.drawImage(image, transform, null);
g.dispose();
int width = getWidth();
return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), getHeight(), width);
}
}
项目pom.xml
4.0.0
com.tingcream
qrTool
0.0.1-SNAPSHOT
jar
qrTool
http://maven.apache.org
UTF-8
com.google.zxing
javase
3.2.1
commons-io
commons-io
2.6