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.

QRCODE
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.