Customizando a pesquisa dos itens no JComboBox

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
}

Comments are closed.