Dentre os muitos recursos que o Java oferece, um dos mais úteis e usados são os Listeners, que dão “vida” as ações realizadas no nosso formulário, fazendo com que a comunicação Homem-Máquina seja realmente possível através de um “simples” clique do botão. E optamos por usar o adjetivo simples com aspas exatamente por se tratar de algo simples apenas na visão do usuário, pois para o desenvolvedor vai muito além de um simples clique do botão.

A linguagem Java possui muitos listeners e neste artigo aprenderemos sobre os principais e mais utilizados no dia a dia. Sabendo como usar estes você estará apto a utilizar os outros listeners com maior facilidade.

Listeners

Do inglês “Ouvinte”, vem de um padrão de projeto chamado Observer, que resumidamente tem por conceito o seguinte: Um objeto tem vários dependentes que são anexados a ele, quando este objeto muda de estado todos os seus dependentes são avisados. Vamos entender melhor este conceito utilizando o mesmo na prática, por agora basta saber que o Listener é a implementação do padrão de projeto Observer e que este tem por função ficar “ouvindo” alterações que ocorram no objeto que está sendo monitorado.

Primeiro apresentaremos nosso formulário, conforme mostra a Figura 1, onde faremos os testes com nossos listeners.

FappListener

Figura 1. FappListener

Quer evoluir cada vez mais na linguagem Java? Faça nosso Checklist Programador Java e aprenda Java de uma vez por todas.

Acima nosso formulário possui quatro componentes básicos: 1 label, 1 jTextField e 2 jButton's. São com esses componentes que realizaremos os testes necessários para os listeners. Vejamos nosso arquivo “.form” (Listagem 1) para que você possa criar o formulário acima no seu Netbeans.

Listagem 1. FappListener.form


  <?xml version="1.0" encoding="UTF-8" ?>
   
  <Form version="1.3" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
    <Properties>
      <Property name="defaultCloseOperation" type="int" value="3"/>
    </Properties>
    <SyntheticProperties>
      <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
    </SyntheticProperties>
    <AuxValues>
      <AuxValue name="FormSettings_generateMnemonicsCode" 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">
                    <Group type="102" alignment="0" attributes="0">
                        <Component id="jButtonConfirmar" min="-2" max="-2" attributes="0"/>
                        <EmptySpace max="-2" attributes="0"/>
                        <Component id="jButtonCancelar" min="-2" max="-2" attributes="0"/>
                    </Group>
                    <Component id="jLabelTexto" alignment="0" min="-2" max="-2" attributes="0"/>
                    <Component id="jTextFieldTexto" alignment="0" min="-2" pref="363" max="-2" attributes="0"/>
                </Group>
                <EmptySpace pref="25" 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 max="-2" attributes="0"/>
                <Component id="jLabelTexto" min="-2" max="-2" attributes="0"/>
                <EmptySpace max="-2" attributes="0"/>
                <Component id="jTextFieldTexto" min="-2" max="-2" attributes="0"/>
                <EmptySpace pref="23" max="32767" attributes="0"/>
                <Group type="103" groupAlignment="3" attributes="0">
                    <Component id="jButtonConfirmar" alignment="3" min="-2" max="-2" attributes="0"/>
                    <Component id="jButtonCancelar" alignment="3" min="-2" max="-2" attributes="0"/>
                </Group>
                <EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
            </Group>
        </Group>
      </DimensionLayout>
    </Layout>
    <SubComponents>
      <Component class="javax.swing.JButton" name="jButtonConfirmar">
        <Properties>
          <Property name="text" type="java.lang.String" value="Confirmar"/>
        </Properties>
        <Events>
          <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButtonConfirmarActionPerformed"/>
        </Events>
      </Component>
      <Component class="javax.swing.JButton" name="jButtonCancelar">
        <Properties>
          <Property name="text" type="java.lang.String" value="Cancelar"/>
        </Properties>
        <Events>
          <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButtonCancelarActionPerformed"/>
        </Events>
      </Component>
      <Component class="javax.swing.JLabel" name="jLabelTexto">
        <Properties>
          <Property name="text" type="java.lang.String" value="Texto"/>
        </Properties>
      </Component>
      <Component class="javax.swing.JTextField" name="jTextFieldTexto">
        <Properties>
          <Property name="text" type="java.lang.String" value="Digite seu texto"/>
        </Properties>
        <Events>
          <EventHandler event="keyPressed" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="jTextFieldTextoKeyPressed"/>
          <EventHandler event="keyReleased" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="jTextFieldTextoKeyReleased"/>
          <EventHandler event="keyTyped" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="jTextFieldTextoKeyTyped"/>
        </Events>
      </Component>
    </SubComponents>
  </Form>

ActionListener

Nosso primeiro Listener a ser estudado é o ActionListener e com base nele ficará muito mais simples de entender os próximos. Vamos pegar como bate nosso botão “Confirmar”, como mostra a Listagem 2.

Listagem 2. ActionListener do botão 'Confirmar'


  jButtonConfirmar.addActionListener(new java.awt.event.ActionListener() {
              public void actionPerformed(java.awt.event.ActionEvent evt) {
                  processaTexto();
              }
          });
   
   
   
  private void processaTexto() {
         String texto = jTextFieldTexto.getText();
         StringBuilder textoInverso = new StringBuilder();
         
         for(int i = texto.length()-1; i >= 0; i --){
               textoInverso.append(texto.charAt(i));
         }
         
         JOptionPane.showMessageDialog(this, "O texto que você digitou na forma inversa: \n "+
         textoInverso.toString().toUpperCase());
      }

Os componentes Jbutton possuem um método chamado addActionListener() onde podemos passar como parâmetro um objeto do tipo ActionListener. A interface ActionListener exige a implementação de apenas um método: actionPerformed(ActionEvent e). Este método é responsável por realizar uma determinada ação quando for disparado este evento.

Optamos por implementar a interface direto na passagem de parâmetro e logo somos obrigados a implementar também os métodos que ela exige. Dentro do método actionPerformed nós fazemos uma chamada ao método: processaTexto() que irá executar a ação que nós desejamos quando o botão confirmar for clicado.

Vamos entender passo a passo o que aconteceu:

  1. Usamos o método addActionListener() para anexar um listener ao nosso jButton, assim quando o botão for clicado o nosso listener será disparado;
  2. Poderíamos criar uma classe separada que implemente o ActionListener com o método actionPerformed, mas optamos por implementar a interface diretamente (de forma anônima) para poupar trabalho, já que só usaremos ela neste ponto;
  3. O nosso método actionPerformed possui uma chamada a outro método: processaTexto(). O método processaTexto() é quem realmente possui a lógica que será executada ao clicar no botão confirmar. Você pode perguntar-se porque não colocamos a lógica direto dentro do actionPerformed!?
    A resposta é que poderíamos mudar o listener posteriormente, sem muita interferência na estrutura do nosso programa. Imagine que amanhã a lógica que era acionada no clique do botão seja acionada no clique de um outro botão, ou no pressionamento de uma tecla, ou na perda de um foco em uma jTextField, isso faz com que apenas realizemos a chamada ao método que possui a lógica correta sem se preocupar com o listener.
  4. O nosso método actionPerformed possui um parâmetro do tipo ActionEvent, este possui alguns métodos que podem ser importantes durante o processamento da nossa lógica, em nosso caso não usaremos ele mas fica o lembrete de que o mesmo pode ser usado como um facilitador para determinadas tarefas. Fica como exercício, para você leitor, buscar o uso mais detalhado do ActionEvent.
  5. Em nosso processaTexto() a lógica deste é bem simples, apenas capturamos o texto que o usuário digitou e mostramos este de forma inversa.

Na próxima seção veremos o uso de outro listener, o KeyListener. Você entenderá porque colocamos a lógica do processaTexto() separada dos listeners.

KeyListener

Em nossa segunda seção iremos aprender sobre o uso de outro listener, o KeyListener. Utilizado para “ouvir” eventos que são disparados pelo pressionamento, liberação ou digitação de uma tecla qualquer, em resumo: O uso do teclado.

Imagine agora que além do clique do botão Confirmar, o usuário possa apenas clicar enter dentro do campo jTextField e o processamento ocorra, o mesmo processamento disparado pelo clique do botão Confirmar. Observe a Listagem 3.

Listagem 3. Usando keyPressed


  jTextFieldTexto.addKeyListener(new java.awt.event.KeyAdapter() {
              public void keyPressed(java.awt.event.KeyEvent evt) {
                 if (evt.getKeyCode() == KeyEvent.VK_ENTER){
                  processaTexto();
                 }
              }
          });

A lógica é quase a mesma do jButton, adicionamos um listener ao componente usando o método addKeyListener, onde este aceita apenas alguém que implemente a interface KeyListener. Optamos por usar a classe KeyAdapter, assim iremos fazer um override apenas do método keypressed que é o que nos interessa atualmente.

O método keyPressed é disparado sempre que qualquer tecla do teclado é pressionada, mas o que nós queremos aqui não é qualquer tecla mas sim apenas a tecla ENTER. Então usando o KeyEvent, que possui informações importantes sobre o Evento, nós capturamos o código da tecla pressionada (getKeyCode()) e comparamos com uma constante VK_ENTER que está na classe KeyEvent, se o keyCode pressionado for igual ao VK_ENTER então significa que foi pressionada a tecla ENTER e podemos chamar o método processaTexto().

Repare aqui que fizemos a mesma chamada que o actionPerformed fez no clique do botão. Desta forma, evitamos de criar códigos duplicados. Poderíamos aplicar a lógica acima para muitas outras teclas, como mostra a Listagem 4.

Listagem 4. Usando keyPressed com diversas teclas


  jTextFieldTexto.addKeyListener(new java.awt.event.KeyAdapter() {
    public void keyPressed(java.awt.event.KeyEvent evt) {
      if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
      processaTexto();
      } else if (evt.getKeyCode() == KeyEvent.VK_F1) {
        System.out.println("Tecla inválida");
      } else if (evt.getKeyCode() == KeyEvent.VK_F2) {
        System.out.println("Tecla inválida");
      } else if (evt.getKeyCode() == KeyEvent.VK_ESCAPE) {
        jTextFieldTexto.setText("");
      } else if (evt.getKeyCode() == KeyEvent.VK_EQUALS) {
        processaTexto();
      }
    }
  });

Veja que adicionamos mais algumas condições dependendo da tecla que for pressionada, por exemplo: Quando o usuário pressionar a tecla ESC, o campo de texto será limpo, quando ele pressionar a tela “=” o resultado será processado e assim por diante.

Perceba que a ideia por trás do listener é a mesma, todos “ouvem” algo. Uns ficam ouvindo o clique do botão, outros o pressionamento de uma tecla, outro a perda de um foco, outro a mudança de uma aba e assim por diante. Existem diversos listeners, mas através destes dois que demonstramos você já poderá começar a estudar o uso dos outros que achar necessário ao seu projeto.

Veja como ficou nossa classe finalizada com todos os listeners juntos na Listagem 5.

Listagem 5. FappListener.java


  import java.awt.event.KeyEvent;
   
  import javax.swing.JOptionPane;
   
  public class FAppListener extends javax.swing.JFrame {
      
      /**
          *
          */
         private static final long serialVersionUID = 1L;
         
         
         /** Creates new form FAppListener */
      public FAppListener() {
          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.
       */
      // <editor-fold defaultstate="collapsed" desc=" Código Gerado ">//GEN-BEGIN:initComponents
      private void initComponents() {
          jButtonConfirmar = new javax.swing.JButton();
          jButtonCancelar = new javax.swing.JButton();
          jLabelTexto = new javax.swing.JLabel();
          jTextFieldTexto = new javax.swing.JTextField();
   
          setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
          jButtonConfirmar.setText("Confirmar");
          jButtonConfirmar.addActionListener(new java.awt.event.ActionListener() {
              public void actionPerformed(java.awt.event.ActionEvent evt) {
               processaTexto();
              }
          });
   
          jButtonCancelar.setText("Cancelar");
          jButtonCancelar.addActionListener(new java.awt.event.ActionListener() {
              public void actionPerformed(java.awt.event.ActionEvent evt) {
                  jButtonCancelarActionPerformed(evt);
              }
          });
   
          jLabelTexto.setText("Texto");
   
          jTextFieldTexto.setText("Digite seu texto");
               jTextFieldTexto.addKeyListener(new java.awt.event.KeyAdapter() {
                      public void keyPressed(java.awt.event.KeyEvent evt) {
                             if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
                                    processaTexto();
                             } else if (evt.getKeyCode() == KeyEvent.VK_F1) {
                                    System.out.println("Tecla inválida");
                             } else if (evt.getKeyCode() == KeyEvent.VK_F2) {
                                    System.out.println("Tecla inválida");
                             } else if (evt.getKeyCode() == KeyEvent.VK_ESCAPE) {
                                    jTextFieldTexto.setText("");
                             } else if (evt.getKeyCode() == KeyEvent.VK_EQUALS) {
                                    processaTexto();
                             }
                      }
               });
   
          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)
                      .addGroup(layout.createSequentialGroup()
                          .addComponent(jButtonConfirmar)
                          .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                          .addComponent(jButtonCancelar))
                      .addComponent(jLabelTexto)
                      .addComponent(jTextFieldTexto, javax.swing.GroupLayout.PREFERRED_SIZE, 363, javax.swing.GroupLayout.PREFERRED_SIZE))
                  .addContainerGap(25, Short.MAX_VALUE))
          );
          layout.setVerticalGroup(
              layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                  .addContainerGap()
                  .addComponent(jLabelTexto)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                  .addComponent(jTextFieldTexto, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 23, Short.MAX_VALUE)
                  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                      .addComponent(jButtonConfirmar)
                      .addComponent(jButtonCancelar))
                  .addGap(26, 26, 26))
          );
          pack();
      }// </editor-fold>//GEN-END:initComponents
   
      private void jButtonCancelarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonCancelarActionPerformed
  // TODO adicione seu código de manipulação aqui:
      }//GEN-LAST:event_jButtonCancelarActionPerformed
   
      private void processaTexto() {
         String texto = jTextFieldTexto.getText();
         StringBuilder textoInverso = new StringBuilder();
         
         for(int i = texto.length()-1; i >= 0; i --){
               textoInverso.append(texto.charAt(i));
         }
         
         JOptionPane.showMessageDialog(this, "O texto que você digitou na forma inversa: \n "+
         textoInverso.toString().toUpperCase());
      }
      /**
       * @param args the command line arguments
       */
      public static void main(String args[]) {
          java.awt.EventQueue.invokeLater(new Runnable() {
              public void run() {
                  new FAppListener().setVisible(true);
              }
          });
      }
      
      // Declaração de variáveis - não modifique//GEN-BEGIN:variables
      private javax.swing.JButton jButtonCancelar;
      private javax.swing.JButton jButtonConfirmar;
      private javax.swing.JLabel jLabelTexto;
      private javax.swing.JTextField jTextFieldTexto;
      // Fim da declaração de variáveis//GEN-END:variables
      
  }

Aprendemos neste artigo o princípio básico de funcionamento dos listeners, usando como base dois listeners que são indispensáveis para quase todos os projetos: ActionListener e KeyListener. O ActionListener é utilizado para ouvir cliques do botão e tomar alguma decisão lógica baseado nesta ação, enquanto que o KeyListener ouve o pressionamento de alguma tecla do teclado, tomando também alguma decisão lógica a partir disto.

Logo no início falamos da importância de usar a lógica fora dos listeners, com um método, classe ou camada separada. Isso foi demonstrado com clareza no artigo, onde usamos a mesma lógica para dois listeners diferentes, ou seja, evitamos a duplicidade de código, centralizando toda a lógica em apenas um local.

Confira alguns cursos Java da DevMedia

Curso de Java Reflection

Curso de Java orientado a objetos

Curso de JBoss

Curso de Apache Struts