Gerando QrCode com Java ZXing

Veja neste artigo como usar a biblioteca ZXing para gerar QRCode.

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.

Figura 1. Formulário para gerar QRCODE

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>
Listagem 1. FgeradorQrCode.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 }
Listagem 2. FgeradorQrCode.java

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.

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados