2

I w'd like make a JComboBox that contents could be searchable. I tried AutoCompleteDecorator, GlazedLists, SwingLabs, JIDE, Laf-Widget, but all of the cannot search by second keyword. For example, in this code possible search by 1st letter and this content includes just one word.

this.comboBox = new JComboBox(new Object[] { "Ester", "Jordi", "Jordina", "Jorge", "Sergi" });
AutoCompleteDecorator.decorate(this.comboBox);

If JComboBox content consists 2 or 3 words, for example: "Ester Jordi" or "Jorge Sergi", in this case if I enter "Sergi", JComboBoxdon't show nothing, because it can recognize by 1st word. I w'd like to ask is there have any way to solve this problem?

  • I found this FilterComboBox: http://stackoverflow.com/questions/10368856/jcombobox-filter-in-java-look-and-feel-independent . My JFrame includes many ComboBoxes with long line data, for quick search I need a SuggestField as Google. Problem is I don't know how to adopt this code to my JFrame. –  Jan 06 '15 at 10:14
  • @Marius Žilėnas: I am sorry for long delay. I followed to your suggestion, but still I couldn't succeed with ComboBox search part. It is still not clear for me **rewrite that part to search in that index** . If possible could you provide with some code. Your help is greatly appreciated. –  Feb 16 '15 at 17:29
  • @Marius Žilėnas: 1) index of words: `String[] data = new String[] {"English", "French", "Spanish", "Japanese", "Chinese"}; for(int i=0; i < data.length; i++){ jComboBox1.addItem(data[i]); jComboBox1.setEditable(true);` 2) `{if data.get(i).toLowerCase().contains(enteredText.toLowerCase())) {filterArray.add(data.get(i));` I don't know I think I am in wrong way. I totally lost in search part. –  Feb 17 '15 at 05:42

2 Answers2

4

I refactored the given code. It can recognize by the fragment. So "American English" and "English" are recognized when you put "English".

You could use the FilterComboBox class in your program.

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

/**
 * A class for filtered combo box.
 */
public class FilterComboBox
    extends JComboBox
{
    /**
     * Entries to the combobox list.
     */
    private List<String> entries;

    public List<String> getEntries()
    {
        return entries;
    }

    public FilterComboBox(List<String> entries)
    {
        super(entries.toArray());
        this.entries = entries  ;
        this.setEditable(true);

        final JTextField textfield =
            (JTextField) this.getEditor().getEditorComponent();

        /**
         * Listen for key presses.
         */
        textfield.addKeyListener(new KeyAdapter()
        {
            public void keyReleased(KeyEvent ke)
            {
                SwingUtilities.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        /**
                         * On key press filter the list.
                         * If there is "text" entered in text field of this combo use that "text" for filtering.
                         */
                        comboFilter(textfield.getText());
                    }
                });
            }
        });

    }

    /**
     * Build a list of entries that match the given filter.
     */
    public void comboFilter(String enteredText)
    {
        List<String> entriesFiltered = new ArrayList<String>();

        for (String entry : getEntries())
        {
            if (entry.toLowerCase().contains(enteredText.toLowerCase()))
            {
                entriesFiltered.add(entry);
            }
        }

        if (entriesFiltered.size() > 0)
        {
            this.setModel(
                    new DefaultComboBoxModel(
                        entriesFiltered.toArray()));
            this.setSelectedItem(enteredText);
            this.showPopup();
        }
        else
        {
            this.hidePopup();
        }
    }
}

See how FilterComboBox works in Demo program.

import javax.swing.JFrame;
import java.util.Arrays;

public class Demo
{
    public static void makeUI()
    {
        JFrame frame = new JFrame("Your frame");
        /**
         * Put data to your filtered combobox.
         */
        FilterComboBox fcb = new FilterComboBox(
                Arrays.asList(
                    "",
                    "English", 
                    "French", 
                    "Spanish", 
                    "Japanese",
                    "Chinese",
                    "American English",
                    "Canada French"
                    ));
        /**
         * Set up the frame.
         */
        frame.getContentPane().add(fcb);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public static void main(String[] args) throws Exception
    {
        makeUI();
    }
}

Another approach, where we make existing comboboxes to filtered comboboxes. It is somewhat "decoration".

"Decorator" class.

import java.util.List;
import java.util.ArrayList;

import javax.swing.JComboBox;
import javax.swing.JTextField;
import javax.swing.DefaultComboBoxModel;

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

/**
 * Makes given combobox editable and filterable.
 */ 
public class JComboBoxDecorator
{
    public static void decorate(final JComboBox jcb, boolean editable)
    {
        List<String> entries = new ArrayList<>();
        for (int i = 0; i < jcb.getItemCount(); i++)
        {
            entries.add((String)jcb.getItemAt(i));
        }

        decorate(jcb, editable, entries);
    }

    public static void decorate(final JComboBox jcb, boolean editable, final List<String> entries)
    {
        jcb.setEditable(editable);
        jcb.setModel(
                    new DefaultComboBoxModel(
                        entries.toArray()));

        final JTextField textField = (JTextField) jcb.getEditor().getEditorComponent();

        textField.addKeyListener(
            new KeyAdapter()
            {
                public void keyReleased(KeyEvent e)
                {
                    SwingUtilities.invokeLater(
                        new Runnable()
                        {
                            public void run()
                            { 
                                comboFilter(textField.getText(), jcb, entries);
                            }
                        }
                    );
                } 
            }
        );
    }

    /**
     * Build a list of entries that match the given filter.
     */
    private static void comboFilter(String enteredText, JComboBox jcb, List<String> entries)
    {
        List<String> entriesFiltered = new ArrayList<String>();

        for (String entry : entries)
        {
            if (entry.toLowerCase().contains(enteredText.toLowerCase()))
            {
                entriesFiltered.add(entry);
            }
        }

        if (entriesFiltered.size() > 0)
        {
            jcb.setModel(
                    new DefaultComboBoxModel(
                        entriesFiltered.toArray()));
            jcb.setSelectedItem(enteredText);
            jcb.showPopup();
        }
        else
        {
            jcb.hidePopup();
        }
    }

}

Demonstration class.

import javax.swing.JFrame;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;

import javax.swing.JComboBox;
import javax.swing.JPanel;

public class Demo
{
    public static void makeUI()
    {
        JFrame frame = new JFrame("Demonstration");

        /**
         * List of values for comboboxes.
         */
        List<String> list = Arrays.asList( 
            "",
            "English", 
            "French", 
            "Spanish", 
            "Japanese",
            "Chinese",
            "American English",
            "Canada French"
        );

        List<String> list2 = Arrays.asList( 
            "",
            "Anglais", 
            "Francais", 
            "Espagnol", 
            "Japonais",
            "Chinois",
            "Anglais americain",
            "Canada francais"
        );

        /**
         * Set up the frame.
         */
        JPanel panel = new JPanel();
        frame.add(panel);

        /**
         * Create ordinary comboboxes.
         * These comboboxes represent premade combobox'es. Later in the run-time we make some of them filtered.
         */
        JComboBox<String> jcb1 = new JComboBox<>(list.toArray(new String[0]));
        panel.add(jcb1);

        JComboBox<String> jcb2 = new JComboBox<>();
        panel.add(jcb2);

        JComboBox<String> jcb3 = new JComboBox<>(list2.toArray(new String[0]));
        panel.add(jcb3);

        /**
         * On the run-time we convert second and third combobox'es to filtered combobox'es.
         * The jcb1 combobox is left as it was.
         * For the first decorated combobox supply our entries.
         * For the other put entries from list2.
         */
        JComboBoxDecorator.decorate(jcb2, true, list); 
        JComboBoxDecorator.decorate(jcb3, true); 

        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public static void main(String[] args) throws Exception
    {
        makeUI();
    }
}
  • I think the answer of @Marius Žilėnas is helpful for the question of in this link: [http://stackoverflow.com/questions/10368856/jcombobox-filter-in-java-look-and-feel-independent] –  Feb 18 '15 at 03:15
  • I w'd like to ask can I use FilterComboBox class for my `JComboBoxes` as `jComboBox1.add(fcb);` `jComboBox2.add(fcb);` ? –  Feb 18 '15 at 12:16
  • With `JComboBoxDecorator.decorate(jComboBox2, true)` call the `jComboBox2` becomes filtered. :) – Developer Marius Žilėnas Feb 18 '15 at 12:36
  • Thank you very much for "Decorator" class. I truly appreciate your time and effort. Sorry if I bothered you. I could apply your code to my generated `JComboBoxes`. But another problem is `JComboBox` at first it shows empty list, then when I enter the text that moment appers items of `JComboBox`. I w'd like to ask how to update the items of `JComboBox` ? –  Feb 18 '15 at 16:11
  • I accepted your answer and give you +1 for your kindness and professionalism. (^~^) –  Feb 18 '15 at 16:30
  • Thank you :)! Good catch, if the combo is empty it should get the entries that where passed along with decorate method. I've updated the code in decorator `public static void decorate(final JComboBox jcb, boolean editable, final List entries) { jcb.setEditable(editable); jcb.setModel( new DefaultComboBoxModel( entries.toArray()));` If decorate(jcb, true, entries) is applied on jcombobox jcb, then this jcb is filled with entries. – Developer Marius Žilėnas Feb 19 '15 at 06:46
  • You are **GREAT** person that I have ever known. THANK YOU VERY MUCH. If you need any help (of course I think not in Java :)) I will do my best. (^~^) –  Feb 19 '15 at 07:22
  • Thank you!:) The helping you is enough of a reward. :) – Developer Marius Žilėnas Feb 19 '15 at 07:44
0

When trying to execute, I had encountered two problems:

  • When pressing ( up, down, enter ) buttons on keyboard. I solved it by preventing them using return statement.

  • The popupMenuWillBecomeInvisble() is fired every time you write in textfield. I solved it by adding getKeyStat() add this code in main frame inside popupMenuWillBecomeInvisible

Hope this helps!

   private voidjCombobox1popupMenuWillBecomeInvisble(javax.swing.event.PopupMenuEvent evt){
         if(JComboBoxDecorator.getKeyStat().equals("enter")){
           //write yor code here
        }
        }

import java.util.List;
import java.util.ArrayList;
import javax.swing.JComboBox;
import javax.swing.JTextField;
import javax.swing.DefaultComboBoxModel;

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.SwingUtilities;

/**
 * Makes given combo box editable and filterable.
 */
public class JComboBoxDecorator {
    static String checkenter="";
    
    public static String getKeyStat(){
       return checkenter;
    }
    
    public static void decorate(final JComboBox jcb, boolean editable) {
        List<String> entries = new ArrayList<>();
        for (int i = 0; i < jcb.getItemCount(); i++) {
            entries.add((String) jcb.getItemAt(i));
        }

        decorate(jcb, editable, entries);
    }
    
    public static void decorate(final JComboBox jcb, boolean editable, final List<String> entries) {
        jcb.setEditable(editable);
        jcb.setModel(new DefaultComboBoxModel(entries.toArray()));

        final JTextField textField = (JTextField) jcb.getEditor().getEditorComponent();

        textField.addKeyListener(
                new KeyAdapter() {
                    @Override
                    public void keyPressed(KeyEvent e) {
                     if(e.getKeyCode() == KeyEvent.VK_ENTER){
                         checkenter = "enter";
                     }
                    }
            @Override
            public void keyReleased(KeyEvent e) {
                SwingUtilities.invokeLater(() -> {
                   if(e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode()==KeyEvent.VK_ENTER){
                      // e.consume();
                       return ;                       
                   }  
                   else{ 
                       comboFilter(textField.getText(), jcb, entries);
                   }
                    
                });
                checkenter = "type";
            }
        }
        );
    }

    /**
     * Build a list of entries that match the given filter.
     */
    private static void comboFilter(String enteredText, JComboBox jcb, List<String> entries) {
        List<String> entriesFiltered = new ArrayList<>();

        for (String entry : entries) {
            if (entry.toLowerCase().contains(enteredText.toLowerCase())) {
                entriesFiltered.add(entry);
            }
        }

        if (entriesFiltered.size() > 0) {
            jcb.setModel(
                    new DefaultComboBoxModel(
                            entriesFiltered.toArray()));
            jcb.setSelectedItem(enteredText);
            jcb.showPopup();
            
        } else {
            jcb.hidePopup();
        }
    }

}
Liju Thomas
  • 1,054
  • 5
  • 18
  • 25