Neste artigo veremos como fazer a conversão de um texto em QrCode, ou seja, o QrCode gerado poderá ser decodificado retornando o texto que desejamos, podendo ser um link ou um texto simples.
Iniciando o projeto
Neste projeto usaremos a biblioteca Zxing para realizar a conversão do texto em imagem, mais especificamente em QrCode. Esta pode ser encontrada na seção de downloads deste artigo.
Todo o projeto será simples e terá como objetivo mostrar a conversão de forma rápida sem utilizar padrões de projeto e etc., para isso criaremos apenas um formulário que terá 2 campos: Um campo texto para o usuário digitar o texto que deverá ser convertido e um botão para geração da imagem no diretório que o usuário desejar.
Na Figura 1 vemos como ficará nosso formulário.
Como explicamos logo acima o formulário é simples e conta com uma lógica apenas no botão “Gerar QRCode” que veremos logo a frente. Caso você esteja usando Netbeans para criação do formulário, poderá aproveitar a Listagem 1 para trata-se de um arquivo “.form” para criação do formulário no Netbeans.
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.3" maxVersion="1.9"
type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
<Properties>
<Property name="defaultCloseOperation"
type="int" value="3"/>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy"
type="int" value="1"/>
<SyntheticProperty name="generateCenter"
type="boolean" value="false"/>
</SyntheticProperties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing"
type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName"
type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN"
type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode"
type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode"
type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget"
type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle"
type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal"
type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier"
type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jButtonGerar" alignment="0" min="-2"
max="-2" attributes="0"/>
<Component id="jLabel1" alignment="0" min="-2"
max="-2" attributes="0"/>
<Component id="jTextTexto" alignment="0" min="-2"
pref="338" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="50" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" pref="34" max="-2" attributes="0"/>
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jTextTexto" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="175" max="32767" attributes="0"/>
<Component id="jButtonGerar" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="jButtonGerar">
<Properties>
<Property name="text" type="java.lang.String" value="Gerar QRCode"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener"
parameters="java.awt.event.ActionEvent" handler="jButtonGerarActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="jLabel1">
<Properties>
<Property name="text" type="java.lang.String" value="Texto a ser codificado"/>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="jTextTexto">
</Component>
</SubComponents>
</Form>
Temos até agora o nosso formulário estruturado, mas precisamos começar a adicionar o conteúdo a ele. Primeiramente vamos mostrar a Listagem 2, que contém o código completo da classe FgeradorQrCode.java.
/*
* To change this license header, choose License Headers
in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package br.com.lab;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Hashtable;
import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
/**
*
* @author ronaldo
*/
public class FGeradorQrCode extends javax.swing.JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
private static final String nomeQrCodeGerado = "qrcodeimage";
private static final String formatoQrCodeGerado = "png";
/**
* Creates new form FGeradorQrCode
*/
public FGeradorQrCode() {
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code"
>//GEN-BEGIN:initComponents
private void initComponents() {
jButtonGerar = new javax.swing.JButton();
jLabel1 = new javax.swing.JLabel();
jTextTexto = new javax.swing.JTextField();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jButtonGerar.setText("Gerar QRCode");
jButtonGerar.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButtonGerarActionPerformed(evt);
}
});
jLabel1.setText("Texto a ser codificado");
javax.swing.GroupLayout layout = new javax.swing
.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout
.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing
.GroupLayout.Alignment.LEADING)
.addComponent(jButtonGerar)
.addComponent(jLabel1)
.addComponent(jTextTexto, javax.swing.GroupLayout
.PREFERRED_SIZE, 338, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(50, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING,
layout.createSequentialGroup()
.addGap(34, 34, 34)
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jTextTexto, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED,
175, Short.MAX_VALUE)
.addComponent(jButtonGerar)
.addContainerGap())
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void jButtonGerarActionPerformed(java.awt.event.ActionEvent evt)
{//GEN-FIRST:event_jButtonGerarActionPerformed
gerarQrCode();
}//GEN-LAST:event_jButtonGerarActionPerformed
public void gerarQrCode(){
if (jTextTexto.getText() == null || jTextTexto.getText().isEmpty()){
JOptionPane.showMessageDialog(this, "O
texto a ser codificado é obrigatório");
}else{
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION){
String path = chooser.getSelectedFile().getAbsolutePath();
gerarComZXing(path, jTextTexto.getText());
}
}
}
private void gerarComZXing(String path, String texto){
try {
File myFile = new File(path+"/"+nomeQrCodeGerado+".
"+formatoQrCodeGerado);
Hashtable<EncodeHintType, ErrorCorrectionLevel>
hintMap = new Hashtable<EncodeHintType, ErrorCorrectionLevel>();
hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
QRCodeWriter qrCodeWriter = new QRCodeWriter();
BitMatrix byteMatrix =
qrCodeWriter.encode(texto,BarcodeFormat.QR_CODE, 100, 100, hintMap);
int CrunchifyWidth = byteMatrix.getWidth();
BufferedImage image = new BufferedImage(CrunchifyWidth, CrunchifyWidth,
BufferedImage.TYPE_INT_RGB);
image.createGraphics();
Graphics2D graphics = (Graphics2D) image.getGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, CrunchifyWidth, CrunchifyWidth);
graphics.setColor(Color.BLACK);
for (int i = 0; i < CrunchifyWidth; i++) {
for (int j = 0; j < CrunchifyWidth; j++) {
if (byteMatrix.get(i, j)) {
graphics.fillRect(i, j, 1, 1);
}
}
}
ImageIO.write(image, formatoQrCodeGerado, myFile);
JOptionPane.showMessageDialog(this, "QRCode gerado em:
"+myFile.getAbsolutePath());
} catch (WriterException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and
feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available,
stay with the default look and feel.
* For details see http://download.oracle.com/javase/
tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info :
javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(FGeradorQrCode.class.getName())
.log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(FGeradorQrCode.class.getName())
.log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(FGeradorQrCode.class.getName())
.log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(FGeradorQrCode.class.getName())
.log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new FGeradorQrCode().setVisible(true);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton jButtonGerar;
private javax.swing.JLabel jLabel1;
private javax.swing.JTextField jTextTexto;
// End of variables declaration//GEN-END:variables
}
Logo no início temos a declaração de duas variáveis de suma importância:
private static final String nomeQrCodeGerado = "qrcodeimage";
private static final String formatoQrCodeGerado = "png";
Com essas duas variáveis podemos identificar qual será o nome da imagem gerada e em que formato, no caso sempre será qrcodeimage.png, mas poderíamos deixar essa informação dinâmica a disposição do usuário, o que acrescentaria mais lógica e complexidade ao nosso formulário.
Em seguida temos o construtor da classe que faz chamada ao método initComponents(), que realiza a inicialização de todo os componentes gráficos além da disposição destes na tela.
O botão jButtonGerar tem associado a ele um método chamado jButtonGerarActionPerformed(), que é chamado toda vez que um clique é realizado, sendo assim, ao clicar neste botão fazemos a chamada ao nosso método gerarQrCode() que é o que realmente desejamos.
O método gerarQrCode() cuida de toda lógica necessária para criação do QrCode a partir do texto passado. Poderíamos colocar esse método em uma classe utilitária para não ficar no formulário, assim deixaríamos nosso sistema com menor acoplamento e mais reusabilidade, mas este não é o foco do nosso artigo.
O método gerarQrCode() primeiro verifica se o texto digitado é válido, ou seja, se ele não é vazio ou nulo. Caso ele seja válido, fazemos uso da classe JfileChooser para mostrar um janela ao usuário onde ele poderá optar onde será salva a imagem:
if (jTextTexto.getText() == null || jTextTexto.getText().isEmpty()){
JOptionPane.showMessageDialog(this, "O texto a ser codificado é obrigatório");
}else{
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
O JfileChooser tem um método chamado setFileSelectionMode(), onde setamos que o usuário poderá escolher apenas diretórios, que é o que precisamos. Não há porque ele escolher arquivos já que nada será aberto.
Quando chamamos o método showOpenDialog() do JfileChooser(), ele retorna um inteiro indicando o que foi feito na janela, sendo que, o que nos importa é que ele tenha apertado no botão Abrir e para isso usamos a constante JfileChooser.APPROVE_OPTION:
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION){
String path = chooser.getSelectedFile().getAbsolutePath();
gerarComZXing(path, jTextTexto.getText());
}
Ao clicar no botão Abrir a condição é satisfeita e capturamos o caminho que ele selecionou, armazenando este na variável path. O método gerarComZXing() já trabalha diretamente com a biblioteca Zxing recebendo as informações de path (caminho onde será salva a imagem) e “texto “ (valor que a imagem conterá).
Vamos entender o método gerarComZxing em partes:
File myFile = new File(path+"/"+nomeQrCodeGerado+"."+formatoQrCodeGerado);
Hashtable<EncodeHintType, ErrorCorrectionLevel>
hintMap = new Hashtable<EncodeHintType, ErrorCorrectionLevel>();
hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
QRCodeWriter qrCodeWriter = new QRCodeWriter();
Neste momento apenas realizarmos a inicialização de variáveis para geração posterior do QRCode. Precisamos do File que estará apontando para o local exato onde a imagem será gravada e para isso usamos as duas variáveis que mencionamos logo no início do artigo.
A variável hintMap descreve um Map de parâmetros que o QRCodeWriter recebe ao codificar o nosso texto em imagem. Em nosso caso passamos apenas um, que é referido pela constante ERROR_CONNECTION.
Criamos a variável qrCodeWriter e logo em seguida começamos a codificação do texto:
BitMatrix byteMatrix = qrCodeWriter.encode(texto,BarcodeFormat.QR_CODE, 100, 100, hintMap);
O método encode() recebe o texto que desejamos codificar, com o formato que desejamos (QR_CODE em nosso caso), o tamanho X,Y (em nosso caso 100,100) e o Map de parâmetros. O retorno é um objeto do tipo BitMatrix que poderá ser usado para conversão em BufferedImage e posterior gravação no diretório selecionado pelo usuário.
int CrunchifyWidth = byteMatrix.getWidth();
BufferedImage image = new BufferedImage(CrunchifyWidth, CrunchifyWidth,
BufferedImage.TYPE_INT_RGB);
image.createGraphics();
Graphics2D graphics = (Graphics2D) image.getGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, CrunchifyWidth, CrunchifyWidth);
graphics.setColor(Color.BLACK);
for (int i = 0; i < CrunchifyWidth; i++) {
for (int j = 0; j < CrunchifyWidth; j++) {
if (byteMatrix.get(i, j)) {
graphics.fillRect(i, j, 1, 1);
}
}
}
ImageIO.write(image, formatoQrCodeGerado, myFile);
} catch (WriterException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Nesse código os processos realizados já não dizem respeito ao Zxing, mas sim da escrita da imagem no disco. Para isso usamos o ImageIO.write() passando a imagem desejada e o caminho onde ela será salva. É necessário colocar o bloco try-catch com as exceções WriterException por conta do Zxing, e IOException por conta da escrita da imagem em disco.
Com isso temos a imagem em QrCode gerada através do texto descrito pelo usuário, podendo esta ser lida por qualquer software leitor de QRCode, redirecionando para uma URL pré-definida ou apenas mostrando um texto estático.
A ideia deste artigo foi mostrar de forma rápida e ágil como gerar QrCode através de qualquer informação digitada pelo usuário com um formulário simples. Você pode adaptar tal lógica para um Útil ou Helper do seu sistema, fazendo assim a reutilização deste em vários pontos.