Archive for setembro, 2010

Customizando a pesquisa dos itens no JComboBox

sexta-feira, setembro 10th, 2010

Assim como o último post, este também é baseado em uma pergunta feita na lista do genesis há alguns meses atrás.

Um usuário percebeu que num JComboBox com um ComboModel de Strings, ao digitar os primeiros caracteres do item, ele é selecionado automaticamente, ao passo que isso não acontecia com o combo populado pelo genesis. Logo que comecei a investigar o issue, percebi que o combo usa o valor retornado pelo toString() dos objetos contidos no model para implementar essa funcionalidade, o que claramente não é adequado para a maioria das implementações de model que não usam Strings.

Deparei-me com a interface JComboBox.KeySelectionManager, que permitiu resolver meu problema, usando o valor de display do bean para que a pesquisa seja executada.

Uma ideia interessante que tive foi de buscar os itens que contém o termo, como ocorre em alguns componentes do NetBeans. Isso é bastante útil quando muitos itens possuem o mesmo começo, como no exemplo abaixo. Fica dada a dica ;-)

package br.com.michaelnascimento.test;

import java.awt.EventQueue;
import java.awt.FlowLayout;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JComboBox.KeySelectionManager;
import javax.swing.JFrame;
import javax.swing.WindowConstants;

public class KeySelectionManagerDemo extends JFrame {
   public KeySelectionManagerDemo() {
      initComponents();
      jComboBox1.setKeySelectionManager(new KeySelectionManager() {
         private final long delay = 500;
         private long lastTime = -1;
         private String searchTerm;

         @Override
         public int selectionForKey(char key, ComboBoxModel model) {
            final long currentTime = System.currentTimeMillis();
            final int size = model.getSize();
            final String[] formatted = new String[size];
            final Object selectedItem = model.getSelectedItem();
            int selectedIndex = -1;

            for (int i = 0; i < size; i++) {
               Object element = model.getElementAt(i);

               if (selectedItem == element && selectedIndex == -1) {
                  selectedIndex = i;
               }

               formatted[i] = element.toString();
            }

            key = Character.toLowerCase(key);
            int start = selectedIndex;

            if (lastTime == -1 || currentTime - lastTime > delay) {
               searchTerm = String.valueOf(key);
            } else {
               searchTerm += key;
            }

            lastTime = currentTime;

            for (int i = Math.max(start, 0); i < size; i++) {
               if (matches(formatted[i])) {
                  return i;
               }
            }

            for (int i = 0; i <= start; i++) {
               if (matches(formatted[i])) {
                  return i;
               }
            }

            return -1;
         }

         private boolean matches(final String s) {
            return s.toLowerCase().contains(searchTerm);
         }
      });
   }

   // <editor-fold defaultstate="collapsed" desc="Generated Code">
   private void initComponents() {

      jComboBox1 = new JComboBox();

      setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      getContentPane().setLayout(new FlowLayout());

      jComboBox1.setModel(new DefaultComboBoxModel(new String[] {
            "São Bento do Sapucaí", "São Bernardo do Campo",
            "São Caetano do Sul", "São Carlos", "São Francisco",
            "São João da Boa Vista", "São João das Duas Pontes",
            "São João de Iracema", "São João do Pau Dalho",
            "São Joaquim da Barra", "São José da Bela Vista",
            "São José do Barreiro", "São José do Rio Pardo",
            "São José do Rio Preto", "São José dos Campos",
            "São Lourenço da Serra", "São Luiz do Paraitinga",
            "São Manuel", "São Miguel Arcanjo", "São Paulo", "São Pedro",
            "São Pedro do Turvo", "São Roque", "São Sebastião",
            "São Sebastião da Grama", "São Simão", "São Vicente" }));
      jComboBox1.setName("jComboBox1"); // NOI18N
      getContentPane().add(jComboBox1);

      pack();
   }// </editor-fold>

   public static void main(String args[]) {
      EventQueue.invokeLater(new Runnable() {
         public void run() {
            new KeySelectionManagerDemo().setVisible(true);
         }
      });
   }
   // Variables declaration - do not modify
   private JComboBox jComboBox1;
   // End of variables declaration
}

Determinando a ordem dos componentes em Swing

quarta-feira, setembro 8th, 2010

Há alguns meses atrás foi feita uma pergunta na lista do genesis cuja resposta envolvia saber como determinar a ordem dos componentes numa interface Swing.

Quando falamos de ordem do ponto de vista do usuário, não necessariamente falamos da ordem em profundidade, que refletiria a “árvore” dos componentes em determinado container, mas sim a ordem de foco. Existe uma interface que foi adicionada no Java 1.4 chamada FocusTraversalPolicy, que serve justamente para isso. Abaixo um exemplo de como determinar a ordem usando os nomes de componentes dados por quem fez a pergunta:

package br.com.michaelnascimento.test;

import java.awt.Component;
import java.awt.EventQueue;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
import org.jdesktop.layout.GroupLayout;
import org.jdesktop.layout.LayoutStyle;

public class ComponentOrder extends JFrame {
   public ComponentOrder() {
      initComponents();
   }

   private void findOrder() {
      List<Component> components = Arrays.asList(new Component[] {login, senha,
            endereco, empresa});
      Collections.shuffle(components);

      for (Component component : components) {
         System.out.println(component.getName());
      }

      Map<Component, Integer> componentPerPosition = new HashMap<Component,
            Integer>();
      Component c = getFocusTraversalPolicy().getFirstComponent(this);
      int position = 0;

      while (componentPerPosition.size() < components.size()) {
         if (c == null) {
            break;
         }

         if (components.contains(c)) {
            componentPerPosition.put(c, position);
         }

         position++;
         c = getFocusTraversalPolicy().getComponentAfter(this, c);
      }

      for (Component component : components) {
         System.out.println(component.getName() + ": " + componentPerPosition.
               get(component));
      }
   }

   // <editor-fold defaultstate="collapsed" desc="Generated Code">
   private void initComponents() {

      jPanel1 = new JPanel();
      login = new JTextField();
      senha = new JTextField();
      endereco = new JTextField();
      empresa = new JTextField();

      setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

      jPanel1.setName("jPanel1"); // NOI18N

      login.setText("jTextField1");
      login.setName("login"); // NOI18N

      senha.setText("jTextField1");
      senha.setName("senha"); // NOI18N

      GroupLayout jPanel1Layout = new GroupLayout(jPanel1);
      jPanel1.setLayout(jPanel1Layout);
      jPanel1Layout.setHorizontalGroup(
         jPanel1Layout.createParallelGroup(GroupLayout.LEADING)
         .add(jPanel1Layout.createSequentialGroup()
            .addContainerGap()
            .add(jPanel1Layout.createParallelGroup(GroupLayout.LEADING)
               .add(login, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
               .add(senha, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
            .addContainerGap(31, Short.MAX_VALUE))
      );
      jPanel1Layout.setVerticalGroup(
         jPanel1Layout.createParallelGroup(GroupLayout.LEADING)
         .add(jPanel1Layout.createSequentialGroup()
            .addContainerGap()
            .add(login, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(LayoutStyle.RELATED)
            .add(senha, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
            .addContainerGap(43, Short.MAX_VALUE))
      );

      endereco.setText("jTextField1");
      endereco.setName("endereco"); // NOI18N

      empresa.setText("jTextField1");
      empresa.setName("empresa"); // NOI18N

      GroupLayout layout = new GroupLayout(getContentPane());
      getContentPane().setLayout(layout);
      layout.setHorizontalGroup(
         layout.createParallelGroup(GroupLayout.LEADING)
         .add(layout.createSequentialGroup()
            .addContainerGap()
            .add(layout.createParallelGroup(GroupLayout.LEADING)
               .add(jPanel1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
               .add(endereco, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
               .add(empresa, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
            .addContainerGap(290, Short.MAX_VALUE))
      );
      layout.setVerticalGroup(
         layout.createParallelGroup(GroupLayout.LEADING)
         .add(layout.createSequentialGroup()
            .addContainerGap()
            .add(jPanel1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(LayoutStyle.RELATED)
            .add(endereco, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(LayoutStyle.RELATED)
            .add(empresa, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
            .addContainerGap(137, Short.MAX_VALUE))
      );

      pack();
   }// </editor-fold>

   public static void main(String args[]) {
      EventQueue.invokeLater(new Runnable() {
         @Override
         public void run() {
            final ComponentOrder co = new ComponentOrder();
            co.setVisible(true);
            co.findOrder();
         }
      });
   }
   // Variables declaration - do not modify
   private JTextField empresa;
   private JTextField endereco;
   private JPanel jPanel1;
   private JTextField login;
   private JTextField senha;
   // End of variables declaration
}