Adding Auto-Completion Support to Swing Comboboxes Blog


    Providing auto-completion support on application text components and comboboxes is quickly becoming a standard UI feature. You can find it in the Firefox search bar, the address field in Google Maps, and many other places. In this article we compare four alternative implementations for providing auto-completion support on Swing comboboxes: GlazedLists, SwingX, JIDE, and Laf-Widget.

    Basic Overview

    The basic functionality is similar across the available implementations. An editable combobox uses either the model entries or an external list to dynamically select entries based on the text currently typed by the user. This is especially useful on comboboxes with large models where a specific entry (searched for by the user) is not located near the top of the list. By typing the few first letters that match that entry, the user is able to navigate to the "region of interest" in a much more efficient and quick fashion. Compare this to moving the mouse to the scroll bar of the pop-up and scrolling down the list; this tends to be inefficient on large lists.

    At this time, there are a considerable number of mature and stable implementations that provide this functionality on different Swing text components. Here, we will compare four open-source implementations on comboboxes. The libraries are:

    • GlazedLists, led by James Lemieux and Jesse Wilson, provides an extensive set of enhancements for working with lists, as well as auto-completionsupport on JComboBoxes.
    • SwingLabs, which has adopted the original code written by Thomas Bierhance (see his article "Inside JComboBox: Adding Automatic Completion") in the SwingX subproject. The current implementation provides auto-completion support forJComboBoxes as well as for genericJTextComponents.
    • JIDE Common Layerhas recently been open-sourced and is led by David Qiao. Based on the original implementation by Thomas Bierhance (mentioned above), it provides auto-completionsupport for JComboBoxes andJTextComponents.
    • The Laf-Widgetproject introduced in the article " Enhancing Swing Applications" provides the auto-completion support forJComboBoxes on the look-and-feel level. It is based on the original implementation by Thomas Bierhance as well, and is maintained by the author of this article.

    In the following sections, we will look at the basic usage of each of the implementations under two scenarios. The first scenario is a simple one--the combobox in question is backed by aString model. The second scenario is more complex (and closer to real-life applications). In this scenario, the combobox is backed by a model with custom objects. In such a case, the auto-completion layer has to provide hooks for translating between user input (characters) and the model elements (custom objects that do not necessarily implement a useful toString()method).

    Simple Case: A Model Backed by Strings

    The simplest combobox in Swing is backed by aString model. Here is how you can create such a combobox:

    Object[] elements = new Object[] { "Ester", "Jordi", "Jordina", "Jorge", "Sergi" }; JComboBox comboBox = new JComboBox(elements); 

    Simple Scenario with GlazedLists

    In order to install the auto-completion support on such a combobox with GlazedLists, you can use the following code (whereAutoCompleteSupport comes from theca.odell.glazedlists.swing package):

    Object[] elements = new Object[] { "Ester", "Jordi", "Jordina", "Jorge", "Sergi" }; this.comboBox = new JComboBox(); AutoCompleteSupport support = AutoCompleteSupport.install( this.comboBox, GlazedLists.eventListOf(elements)); System.out.println("Is editable - " + this.comboBox.isEditable() + ". Surprise!"); 

    Note that unlike other implementations, GlazedLists uses an "external" list of available items. In our case, we create an empty combobox and provide an external list (the second parameter to theAutoCompleteSupport.install method) that will be used as the combobox model by GlazedLists. Another interesting point to note is that GlazedLists makes the combobox editable as a side-effect of AutoCompleteSupport.install, displaying the "convention over configuration" approach (you don't explicitly need to make the combobox editable in your code). At runtime, the last line will print

    Is editable - true. Surprise! 

    A nice thing about this implementation is that the pop-up will only show entries that match the currently typed prefix (instead of showing the whole list and scrolling to the first matching item as in other implementations), as shown in Figure 1.

    The pop-up only shows the matching items
    Figure 1. The pop-up only shows the matching items

    By default, the auto-completion in GlazedLists allows entering elements that are not in the list (freetyping). To enablestrict completion, use the following code:

    AutoCompleteSupport support = AutoCompleteSupport.install( this.comboBox, GlazedLists.eventListOf(elements)); support.setStrict(true); 

    Simple Scenario with SwingX

    In order to install auto-completion support on our combobox with SwingX, you can use the following code (whereAutoCompleteDecorator comes from theorg.jdesktop.swingx.autocomplete package):

    this.comboBox = new JComboBox(new Object[] { "Ester", "Jordi", "Jordina", "Jorge", "Sergi" }); AutoCompleteDecorator.decorate(this.comboBox); System.out.println("Is editable - " + this.comboBox.isEditable() + ". Surprise!"); 

    As with GlazedLists, the combobox has been made editable after installing the auto-completion support. The current implementation doesn't allow for the proper setting of strict selection mode. Instead, it uses the original editable status: if the combobox was editable before the call to decorate, the auto-completion will be freetyping. If the combobox was not editable, the auto-completion will be strict. This behavior is specified in the Javadoc comments of the method.

    Simple Scenario with JIDE

    In order to install the auto-completion support on our combobox with JIDE, you can use the following code (whereAutoCompletion comes from thecom.jidesoft.swing package):

    Object[] elements = new Object[] { "Ester", "Jordi", "Jordina", "Jorge", "Sergi" }; this.comboBox = new JComboBox(elements); this.comboBox.setEditable(true); AutoCompletion ac = new AutoCompletion(this.comboBox); ac.setStrict(false); 

    With JIDE, the combobox has to be editable in order to have auto-completion installed on it with theAutoCompletion class. In order to install auto-completion on non-editable comboboxes, you can use theSearchable feature. In addition, by default JIDE provides strict selection mode. If you want to have the freetyping functionality, call the setStrict method on theAutoCompletion object that you have created.

    An even simpler way to provide auto-completion on a combobox with JIDE is to use AutoCompletionComboBox:

    Object[] elements = new Object[] { "Ester", "Jordi", "Jordina", "Jorge", "Sergi" }; this.comboBox = new AutoCompletionComboBox(elements); this.comboBox.setStrict(false); 

    Simple Scenario with Laf-Widget

    In order to install the auto-completion support on our combobox with Laf-Wigdet, you will need to run under a look and feel that has been "widgetized" to provide this functionality. You can read about the "widgetizing" process in the blog entry " Spring Effects and Widgets: Now in Windows Look and Feel," which also points to the Laf-Widget-Windowsproject. This project provides extensions to the Windows look and feel, and one of these extensions is auto-completion support for editable comboboxes.

    First, you need to set the widgetized look and feel using the following code:

    UIManager.setLookAndFeel( new; 

    Now, you create a combobox and set it to editable:

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

    The Laf-Widget approach takes the "convention over configuration" a step further and automatically installs the auto-completion support on all editable comboboxes. If you want to uninstall this functionality, use the LafWidget.COMBO_BOX_NO_AUTOCOMPLETIONclient property. By default, the auto-completion is freetyping. If you want to install the strict mode, call the following code:

    this.comboBox = new JComboBox(new Object[] { "Ester", "Jordi", "Jordina", "Jorge", "Sergi" }); this.comboBox.setEditable(true); this.comboBox.putClientProperty( LafWidget.COMBO_BOX_USE_MODEL_ONLY, Boolean.TRUE); 

    Note that under the strict mode, Laf-Widget implementation will install a "lock" border that provides a visual indication or "read-only" mode of the selection (as shown in Figure 2):

    Lock border on strict auto-completion under Laf-Widget implementation
    Figure 2. Lock border on strict auto-completion under Laf-Widget implementation

    Simple Scenario: Summary

    As can be seen, the libraries are very similar in the basic support for installing auto-completion on comboboxes. The main differences are in the "convention over configuration" assumptions. GlazedLists and SwingX make the combobox editable; Laf-Widget doesn't even require the user to explicitly install the auto-completion. GlazedLists provides a nice option to install an "external" choice list, which may be very useful in certain situations, while JIDE provides a custom component that has the auto-completion functionality out of the box.

    Complex Case: A Model Backed by Custom Objects

    While the previous section showed a simple model backed byStrings, most real-life applications will have custom objects backing up the model. In this section, we will show how the libraries in question deal with auto-completion on such comboboxes.

    First, we'll start with a simple custom bean that contains information on a single model object:

    public class UserInfo { private String firstName; private String lastName; public UserInfo(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } } 

    In addition, we'll simulate the user database with aUserRepository class (see the Resources section below). The implementation is quite naive in that it assumes uniqueness of the first name, but it will suffice for our purposes. In order to create a combobox with all available users, we use the following code:

    UserInfo[] allUsers = UserRepository.getInstance().getAllUsers(); this.comboBox = new JComboBox(allUsers); 

    The next step is to provide a custom cell renderer for the combobox pop-up. One option is to provide a customtoString() implementation on the model class (UserInfo), and another option is to provide a custom implementation of a ListCellRenderer and install it on the combobox. A sample implementation of such a renderer that uses the first name of the specific user is:

    public class UserInfoRenderer extends DefaultListCellRenderer { @Override public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { JLabel result = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); UserInfo userInfo = (UserInfo) value; result.setText(userInfo.getFirstName()); return result; } } 

    And to install this renderer on our combobox, use:

    this.comboBox.setRenderer(new UserInfoRenderer()); 

    The interesting part comes with installing the editor. While the core Swing layer (ComboBoxEditor.getEditorComponent) doesn't mandate it, in most cases you would have a text field as the actual editor of the combobox. In our case (a model backed up by custom objects), we will have to provide translation between the model objects and the text field contents (string). In order to be in sync with the renderer implementation (which shows the user's first name), here is a possible implementation of such a "translation" editor:

    public class UserInfoEditor extends BasicComboBoxEditor { public UserInfoEditor(ComboBoxEditor origEditor) { super(); editor.setBorder(((JComponent) origEditor.getEditorComponent()) .getBorder()); } @Override public void setItem(Object anObject) { if (anObject instanceof UserInfo) { super.setItem(((UserInfo) anObject).getFirstName()); } else { super.setItem(anObject); } } @Override public Object getItem() { Object superRes = super.getItem(); if (superRes instanceof String) { UserInfo result = UserRepository.getInstance().getUserInfo( (String) superRes); return result; } return superRes; } } 

    The implementation is quite straightforward. ThesetItem converts a model object into a string representation, and the getItem converts a string (typed by the user into the editable combobox) into the matching model object.

    Note that up until now, none of code samples in this section have mentioned auto-completion. However, the basic steps of installing auto-completion support with all four libraries are the same--you need to provide some sort of a translation "implementation" that the specific library uses to select the best-matching item based on the currently typed prefix. While the specific interfaces that you need to implement are different, the implementation itself is very similar.

    Complex Scenario with GlazedLists

    Here is what you need to do for GlazedLists. First, you provide a custom java.text.Format implementation for model-string conversions:

    private static class UserInfoFormat extends Format { @Override public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { if (obj != null) toAppendTo.append(((UserInfo) obj).getFirstName()); return toAppendTo; } @Override public Object parseObject(String source, ParsePosition pos) { return UserRepository.getInstance().getUserInfo( source.substring(pos.getIndex())); } } 

    And now, you provide a custom TextFilteratorimplementation to use the firstName property of your bean:

    UserInfo[] allUsers = UserRepository.getInstance().getAllUsers(); this.comboBox = new JComboBox(); TextFilterator<UserInfo> textFilterator = GlazedLists.textFilterator( UserInfo.class, "firstName"); AutoCompleteSupport support = AutoCompleteSupport.install( this.comboBox, GlazedLists.eventListOf(allUsers), textFilterator, new UserInfoFormat()); support.setStrict(true); 

    Note that for GlazedLists, there is no need to install the custom editor, the custom renderer, or the model. The implementation uses the formatter, the filterator, and the event list to wrap the original (default) editor, renderer, and model with its own implementations that convert between model objects and the string editor contents.

    Complex Scenario with SwingX

    The code for SwingX follows the same lines. As with GlazedLists, you do not need to install a custom editor (but you do need the custom renderer and the model):

    UserInfo[] allUsers = UserRepository.getInstance().getAllUsers(); this.comboBox = new JComboBox(allUsers); this.comboBox.setRenderer(new UserInfoRenderer()); AutoCompleteDecorator.decorate(comboBox, new ObjectToStringConverter() { @Override public String getPreferredStringForItem(Object item) { if (item == null) return null; return ((UserInfo) item).getFirstName(); } }); 

    Complex Scenario with JIDE

    Unlike with GlazedLists and SwingX, the implementations of JIDE and Laf-Widget require you to install a custom editor on your combobox. In some cases this might seem like coding overhead, but at least your code will not be "surprised" when the original editor is "yanked" and replaced by a custom one installed by the auto-completion code. Here is how you install the auto-completion support on a combobox with JIDE:

    UserInfo[] allUsers = UserRepository.getInstance().getAllUsers(); this.comboBox = new JComboBox(allUsers); this.comboBox.setEditor(new UserInfoEditor(this.comboBox .getEditor())); this.comboBox.setRenderer(new UserInfoRenderer()); this.comboBox.setEditable(true); AutoCompletion ac = new AutoCompletion(this.comboBox, new ComboBoxSearchable(this.comboBox) { @Override protected String convertElementToString(Object object) { return ((UserInfo) object).getFirstName(); } }); 

    As you can see, you have to install a custom editor and explicitly set the combobox to be editable. Other than that, the implementation is pretty much the same as with other libraries. You can also use the AutoCompletionComboBox to make the code a little bit simpler:

    UserInfo[] allUsers = UserRepository.getInstance().getAllUsers(); this.comboBox = new AutoCompletionComboBox(allUsers) { @Override protected AutoCompletion createAutoCompletion() { return new AutoCompletion(this, new ComboBoxSearchable(this) { @Override protected String convertElementToString(Object object) { return ((UserInfo) object).getFirstName(); } }); } }; this.comboBox.setEditor(new UserInfoEditor(this.comboBox.getEditor())); this.comboBox.setRenderer(new UserInfoRenderer()); 

    Complex Scenario with Laf-Widget

    Here is how you install the auto-completion with Laf-Widget:

    UserInfo[] allUsers = UserRepository.getInstance().getAllUsers(); this.comboBox = new JComboBox(allUsers); this.comboBox.setEditor(new UserInfoEditor(comboBox .getEditor())); this.comboBox.setRenderer(new UserInfoRenderer()); this.comboBox.setEditable(true); this.comboBox.putClientProperty(LafWidget.COMBO_BOX_USE_MODEL_ONLY, Boolean.TRUE); this.comboBox.putClientProperty( LafWidget.COMBO_BOX_AUTOCOMPLETION_MATCHER, new AutoCompletionMatcher() { public Object getFirstMatching(ComboBoxModel model, String prefix) { for (int i = 0; i < model.getSize(); i++) { UserInfo userInfo = (UserInfo) model .getElementAt(i); if (userInfo.getFirstName().startsWith(prefix)) return userInfo; } return null; } }); 

    Note that you need to explicitly set a custom editor and mark the combobox as editable, as well.


    As you have seen from the code examples, the surveyed solutions for installing the auto-completion support on Swing comboboxes provide very similar capabilities with comparable implementation efforts (some of this is due to the fact that three out of four have branched from the same base implementation, and the fourth has drawn a few ideas from it). Some use a "convention over configuration" approach, while others require explicit settings. If you are already using one of these libraries in your code, it would be a wise choice to use auto-completion from the same library. If you're starting a new project, look at the other APIs provided by the relevant libraries and compare the support for different operating systems, the look and feels, and the licensing requirements.

    You can compare the four libraries in action by running theSimpleAll class from the bundled sample code (see theResources section). In this example, you will see the subtle differences in the highlighting and pop-up handling.

    Thanks to James Lemieux of GlazedLists and David Qiao of JIDE for reviewing this article.


    Kirill Grouchnikov has been writing software for the last 15 years, the last seven doing it for a living.