Searchable JComboBox
The following code can be used to produce a searchable JComboBox. As the user types her selection, the control will search through the list of options and find the closest match, filling in the remaining characters in a user-friendly way. It’s much better control than the default JComboBox, IMO.
This control is highly efficient, and can be used for models with huge numbers of elements. It uses a TernarySearchTree to implement the search forward behavior, which is incredibly fast and memory efficient. All of the classes used by JSearchableComboBox are part of the standard Java distribution except TernarySearchTree, which is provided in the article resources section of the TST link above.
What’s new?
-
2002.10.3 – Added getItem override in custom editor to improve support for
putting non-string objects into the combo box.
-
2002.10.8 – Improved ActionEvent handling to avoid deselecting text at incorrect
times
JSearchableComboBox.java:
import javax.swing.JComboBox;
import javax.swing.ComboBoxModel;
import java.awt.event.KeyEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.plaf.basic.BasicComboBoxEditor;
/**
* Enables efficient searching through a
JComboBox.
* Based on code by Ron
(rmlchan@yahoo.com)
*
* @author Eric Lindauer
* @date 2002.9.24
*/
public class JSearchableComboBox extends JComboBox {
public
JSearchableComboBox () {
super();
init();
}
public
JSearchableComboBox (Object[] elements) {
super(elements);
init();
}
public
void setModel (ComboBoxModel model)
{
super.setModel
(model);
init
();
}
private
void init () {
setEditable(true);
setEditor(new
SearchEditor (this));
}
private
static class SearchEditor extends BasicComboBoxEditor {
private
TernarySearchTree _data = new TernarySearchTree ();
public
Object getItem () {
return
_data.get (super.getItem ().toString ());
}
public
SearchEditor (final JSearchableComboBox cb) {
//
populate the search tree with the items in the list
ComboBoxModel
model = cb.getModel ();
for
(int i = 0; i < model.getSize (); i++)
{
Object
data = model.getElementAt (i);
_data.put
(data.toString (), data);
}
//
when the user types, search the data and guess what they want
KeyAdapter
listener = new KeyAdapter () {
public
void keyReleased (KeyEvent ev) {
if
((ev.getKeyChar () >= 'a' && ev.getKeyChar () <= 'z') ||
(ev.getKeyChar
() >= '0' && ev.getKeyChar () <= '9') ||
(ev.getKeyChar
() >= 'A' && ev.getKeyChar () <= 'Z') ||
(ev.getKeyChar
() == KeyEvent.VK_SPACE))
{
String
startText = editor.getText ();
//
cb.showPopup (); ß uncomment for smaller
data sets
String
finalText = _data.matchPrefixString (startText, 1);
if
(finalText.equals (""))
finalText
= startText;
if
(! finalText.equals (startText))
{
editor.setText
(finalText);
editor.setSelectionStart
(startText.length ());
editor.setSelectionEnd
(finalText.length ());
}
cb.setSelectedItem
(_data.get (finalText));
}
}
};
editor.addKeyListener
(listener);
//
register an action listener to keep the text area always up-to-date
ActionListener
actionListener = new ActionListener () {
public
void actionPerformed (ActionEvent e)
{
if
(cb.getSelectedItem () != null &&
!
editor.getText ().equals (cb.getSelectedItem ().toString ()))
{
editor.setText
(cb.getSelectedItem ().toString ());
}
}
};
cb.addActionListener
(actionListener);
}
}
public
static void main(String[] args) {
java.awt.Frame
f = new java.awt.Frame();
f.addWindowListener(new
java.awt.event.WindowAdapter() {
public
void windowClosing(java.awt.event.WindowEvent ev) {
System.exit(0);
}
}
);
String[]
stuff = new String[] {"first", "second", "second and
some",
"third",
"three", "three+", "thrice", "four",
"four-plus"};
JSearchableComboBox
cb = new JSearchableComboBox (stuff);
f.add(cb);
f.pack();
f.show();
}
}
Send questions, comments, complaints or compliments to eric@achess.com.
Yours,
Eric Lindauer