Autocomplete Drop Down in a Java JComboBox

Articles —> Autocomplete Drop Down in a Java JComboBox

autocomplete JComboBox

A JComboBox is a java swing component that provides a drop down menu of items. A JComboBox can also be set to editable, allowing users to enter custom data not provided within the drop down menu. For longer lists, it can be quite useful to allow users to start typing what they wish, and let the JComboBox perform an auto-completion, in which the drop down menu is populated with the appropriate items that match a search for the user entered text.

To achieve this behavior, one can first create an interface defining a searchable data structure:


/**

 * Interface to search an underlying inventory of items and return a collection of found items. 

 * @author G. Cope

 *

 * @param <E> The type of items to be found.

 * @param <V> The type of items to be searched

 */

public interface Searchable<E, V>{

	

	/**

	 * Searches an underlying inventory of items consisting of type E

	 * @param value A searchable value of type V

	 * @return A Collection of items of type E.

	 */

	public Collection<E> search(V value);

	

}

The above interface provides an abstract way to decouple the auto-complete JComboBox from the searchable inventory, allowing this inventory to be just about anything - from hardcoded values to a file to even a database.

For a basic demonstration, one can search for String objects as demonstrated below:


/**

 * Implementation of the Searchable interface that searches a List of String objects. 

 * This implementation searches only the beginning of the words, and is not be optimized

 * for very large Lists. 

 * @author G. Cope

 *

 */

public class StringSearchable implements Searchable<String,String>{



	private List<String> terms = new ArrayList<String>();

	

	/**

	 * Constructs a new object based upon the parameter terms. 

	 * @param terms The inventory of terms to search.

	 */

	public StringSearchable(List<String> terms){

		this.terms.addAll(terms);

	}

	

	@Override

	public Collection<String> search(String value) {

		List<String> founds = new ArrayList<String>();

		

		for ( String s : terms ){

			if ( s.indexOf(value) == 0 ){

				founds.add(s);

			}

		}

		return founds;

	}

	

}

The above implementation allows us to search a List of terms based upon whether each item within the List of terms begins with the search term (one could easily modify this to search anywhere within each term). The class performs a linear search - not the most optimal for large lists but is sufficient for smaller collections of search terms.

Now for the JComboBox auto-completion. The class below extends JComboBox to implement this behavior:




/**

 * JComboBox with an autocomplete drop down menu. This class is hard-coded for String objects, but can be

 * altered into a generic form to allow for any searchable item. 

 * @author G. Cope

 *

 */

public class AutocompleteJComboBox extends JComboBox{

	

	static final long serialVersionUID = 4321421L;

	

	private final Searchable<String,String> searchable;

	

	/**

	 * Constructs a new object based upon the parameter searchable

	 * @param s

	 */

	public AutocompleteJComboBox(Searchable<String,String> s){

		super();

		this.searchable = s;

		setEditable(true);

		Component c = getEditor().getEditorComponent();

		if ( c instanceof JTextComponent ){

			final JTextComponent tc = (JTextComponent)c;

			tc.getDocument().addDocumentListener(new DocumentListener(){



				@Override

				public void changedUpdate(DocumentEvent arg0) {}



				@Override

				public void insertUpdate(DocumentEvent arg0) {

					update();

				}



				@Override

				public void removeUpdate(DocumentEvent arg0) {

					update();

				}

				

				public void update(){

					//perform separately, as listener conflicts between the editing component

					//and JComboBox will result in an IllegalStateException due to editing 

					//the component when it is locked. 

					SwingUtilities.invokeLater(new Runnable(){



						@Override

						public void run() {

							List<String> founds = new ArrayList<String>(searchable.search(tc.getText()));

							Set<String> foundSet = new HashSet<String>();

							for ( String s : founds ){

								foundSet.add(s.toLowerCase());

							}

							Collections.sort(founds);//sort alphabetically

							

							

							setEditable(false);

							removeAllItems();

							//if founds contains the search text, then only add once.

							if ( !foundSet.contains( tc.getText().toLowerCase()) ){

								addItem( tc.getText() );

							}

							

							for (String s : founds) {

								addItem(s);

							}

							setEditable(true);

							setPopupVisible(true);

						}

						

					});

					

				}

				

			});

			//When the text component changes, focus is gained 

			//and the menu disappears. To account for this, whenever the focus

			//is gained by the JTextComponent and it has searchable values, we show the popup.

			tc.addFocusListener(new FocusListener(){



				@Override

				public void focusGained(FocusEvent arg0) {

					if ( tc.getText().length() > 0 ){

						setPopupVisible(true);

					}

				}



				@Override

				public void focusLost(FocusEvent arg0) {						

				}

				

			});

		}else{

			throw new IllegalStateException("Editing component is not a JTextComponent!");

		}

	}

}

The above class performs a type of auto-completion, in which the drop down menu shows all items which match the user input string. The code has to jump through a few hoops to accomplish this. First, it adds a document listener to the editing component of the JComboBox. Every time the component is edited, this listener is fired and the JComboBox updated. However, editing the items in the JComboBox also affects the editing component, and to avoid conflicts it does so at a later time by dispatching the appropriate code to the SwingUtilities class.


public class Demonstration{

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

		SwingUtilities.invokeAndWait(new Runnable(){



			@Override

			public void run() {

				List<String> myWords = new ArrayList<String>();

				myWords.add("bike");

				myWords.add("car");

				myWords.add("cap");

				myWords.add("cape");

				myWords.add("canadian");

				myWords.add("caprecious");

				myWords.add("catepult");



				StringSearchable searchable = new StringSearchable(myWords);

				AutocompleteJComboBox combo = new AutocompleteJComboBox(searchable);

				

				JFrame frame = new JFrame();

				frame.add(combo);

				frame.pack();

				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

				frame.setVisible(true);

			}

			

		});

        }

}

And the result from running the code above and typing 'ca':

autocomplete JComboBox

Just another example of how extendable and customizable java and Swing can be.




Comments

  • Dmitriy Ryabin   -   March, 6, 2014

    Thanks for this.

    However, I can't get it properly running.

    I start typing, but after every letter entered I have to move the mouse into the editable field and re-activate it,

  • Greg Cope   -   March, 6, 2014

    This was an initial issue I found and the reason a FocusListener was added to AutocompleteJComboBox - the focus change due to the TextComponent model change should be accounted for by the FocusListener.

  • Liz Tejada   -   April, 10, 2014

    Hi, I love this article i was a long time ago since i saw this type of component i wish i could use it.

    Thank it was really usefully for me, i just make some changes and make it a java bean and works perfect for me.

    Thanks once again.

    Att: Ing.Liz.Tejada

  • Liz Tejada   -   April, 10, 2014

    Dmitriy Ryabin ,

    I fix this issue by adding

    tc.requestFocus();

    Just after

    setPopupVisible(true);

    setEditable(true);

    I hope it works for you

  • William Lajeanne   -   May, 17, 2014

    @Dmitriy Ryabin

    My fix is to add

    tc.setCaretPosition(tc.getText().length());

    on line 180 (method focusGained, after setPopupVisible(true);

  • Ashley Uy   -   January, 19, 2015

    This was great, thanks! Moreso to the followup comments here at the bottom. This tutorial gave me an idea on how to solve the problem I'm currently working on. Thanks for sharing!

  • Jack Zhang   -   March, 19, 2015

    I have encountered a problem writing this program. When I type the first character into the text field, the first item of the list would be put into the text field automatically,and it would fire the listener function again.How can I avoid this?

  • Greg Cope   -   March, 19, 2015

    @Jack, that should not be the default behavior of this example - the JComboBox should be populated and not the JTextField (until an item is chosen). You may need to play with the listeners you have in your code to correct whatever behavior you see. If I misunderstood your question please elaborate (if your code differs from that above, you might try your luck posting at StackOverflow.com or javaprogrammingforums.com as this comment section is not the greatest format for passing code specifics back and forth)

  • Gerald Mills   -   April, 23, 2015

    Please how can I make it search from anywhere in a string and also how to implement it into my combo box thanks

  • Akary Moe   -   April, 24, 2015

    Thanks in advance. But I had one problem. When I place the combo box into Jpanel, it doesn't work properly and does not show the very first combo items.

  • Charlotte Lolotte   -   September, 27, 2015

    Hello, I think I have the same problem as stated by Jack.

    The content of the input field appears as the first item in the JComboBox, as "ca" above "canadian" in the image of the example. This make doubloon with the input field. And in addition in the case of "ca", it is not correct since "ca" is not a word in the list.

    I think the problem is the following lines:

    //if founds contains the search text, then only add once.

    if ( !foundSet.contains( tc.getText().toLowerCase()) ){

    addItem( tc.getText() );

    }

    I tried to remove them, but then as soon as I enter the first letter in the input field, a word of my list is selected and displayed in the input field without the comboBox will be opened, and then I have at all the possibility of writing in the field.

    thank you in advance for your assistance

    Charlotte

  • Alone Pmk   -   November, 18, 2015

    Thanks!!!!

  • prithvi raj   -   April, 5, 2016

    Liz Tejada ... it worked :)

  • Muzammil Jamal   -   May, 17, 2016

    Hi there,

    This idea looks pretty cool. But what we are doing here is we are kinda hard coding the values that need to listed in the dropdown. Is there a way to list the values automatically without giving the values in the script. say if i enter "sa" in textbox..It should list me with "Sam", "Sample"..etc...??

  • Greg Cope   -   November, 30, -0001

    @Muzammil, the hard coding is just an example - it all depends upon how Searchable is implemented, which fully depends upon the context. For example, this could depend upon hard coded values, a local or remote database, an internet search, etc...

  • benoit granier   -   June, 12, 2016

    At the beginning when nothing is typed yet, if I press the down arrow I would like to see the whole list in the popup. How can I achieve this ?

  • Semynar Vicencion   -   October, 24, 2016

    Thanks in this... it's what I need on my App

  • venu venu   -   January, 2, 2017

    Hi Charlotte Lolotte ,

    Do you got solution for your query.

    Is there any way to remove the extra line that appears in the list.

  • Guillaume Tamburini   -   November, 2, 2016

    Sorry for previous comment, copy/paste went wrong ...

    Bonjour/Hi Benoit, I don't know if you are still looking for an answer to your question or not any more, but I faced the same issue and I solved it with this (probably not the best, but working) piece of code. I added an init method to the class AutocompleteJComboBox:

    public void init() {

    final Component c = getEditor().getEditorComponent();

    if (c instanceof JTextComponent) {

    final JTextComponent tc = (JTextComponent) c;

    SwingUtilities.invokeLater(() -> {

    final List founds = new ArrayList(searchable.search(tc.getText()));

    final Set foundSet = new HashSet();

    for (final String s1 : founds) {

    foundSet.add(s1.toLowerCase());

    }

    Collections.sort(founds);// sort alphabetically

    setEditable(false);

    removeAllItems();

    if (!foundSet.contains(tc.getText().toLowerCase())) {

    addItem(tc.getText());

    }

    for (final String s2 : founds) {

    addItem(s2);

    }

    setEditable(true);

    setPopupVisible(false);

    tc.requestFocus();

    });

    }

    }

    Then, you can call this method once your control has been instantiated, and your drop down will be populated before you start typing anything.

    I am sure we could refactor some of this code, since it is pretty much the same as update().

  • Dewin Burvis   -   January, 28, 2017

    Nice code, I've been searching for this. Can you add support for two or more words?

  • 2014 068652   -   January, 29, 2017

    hi thanks for the code, can you add functionality where i can choose with the down key

Back to Articles


© 2008-2017 Greg Cope