AutoComplete JComboBox
|
|
There are ready-made classes of JComboBox that implement an autocomplete feature and I chose to use Orbital Computer because of its ease of use and simplicity of the code. I added an extra feature in this JComboBox wherein if you press the ESC key, it will automatically select and show the first item in the ComboBoxModel.
To use this class, simply call it like this.
1 2 | [... cb ...] new AutoCompleteJComboBoxer(cb); |
That’s it! No need to actually create a variable for this class since you just pass the JComboBox object as the parameter and the class handles the rest adding the autocomplete feature. Pretty simple right? This is a handy class. Get the code below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | public class AutoCompleteJComboBoxer extends PlainDocument { private final JComboBox comboBox; private ComboBoxModel model; private JTextComponent editor; private boolean hidePopupOnFocusLoss; public AutoCompleteJComboBoxer(JComboBox comboBox) { this.comboBox = comboBox; comboBox.setEditable(true); model = comboBox.getModel(); editor = (JTextComponent) comboBox.getEditor().getEditorComponent(); editor.setDocument(this); // Bug 5100422 on Java 1.5: Editable JComboBox won't hide popup when tabbing out hidePopupOnFocusLoss = System.getProperty("java.version").startsWith("1.5"); // Highlight whole text when focus gets lost editor.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { highlightCompletedText(0); // Workaround for Bug 5100422 - Hide Popup on focus loss if (hidePopupOnFocusLoss) AutoCompleteJComboBoxer.this.comboBox.setPopupVisible(false); } }); // Highlight whole text when user hits enter editor.addKeyListener(new KeyAdapter() { @Override public void keyPressed(java.awt.event.KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { highlightCompletedText(0); } else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { AutoCompleteJComboBoxer.this.comboBox.setSelectedIndex(0); AutoCompleteJComboBoxer.this.editor.setText(AutoCompleteJComboBoxer.this.comboBox.getSelectedItem().toString()); highlightCompletedText(0); } } }); // Handle initially selected object Object selected = comboBox.getSelectedItem(); if (selected != null) editor.setText(selected.toString()); } @Override public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { // construct the resulting string String currentText = getText(0, getLength()); String beforeOffset = currentText.substring(0, offs); String afterOffset = currentText.substring(offs, currentText.length()); String futureText = beforeOffset + str + afterOffset; // lookup and select a matching item Object item = lookupItem(futureText); if (item != null) { comboBox.setSelectedItem(item); } else { // keep old item selected if there is no match item = comboBox.getSelectedItem(); // imitate no insert (later on offs will be incremented by str.length(): selection won't move forward) offs = offs-str.length(); // provide feedback to the user that his input has been received but can not be accepted comboBox.getToolkit().beep(); // when available use: UIManager.getLookAndFeel().provideErrorFeedback(comboBox); } // remove all text and insert the completed string super.remove(0, getLength()); super.insertString(0, item.toString(), a); // if the user selects an item via mouse the the whole string will be inserted. // highlight the entire text if this happens. if (item.toString().equals(str) && offs==0) { highlightCompletedText(0); } else { highlightCompletedText(offs+str.length()); // show popup when the user types comboBox.setPopupVisible(true); } } private void highlightCompletedText(int start) { editor.setCaretPosition(getLength()); editor.moveCaretPosition(start); } private Object lookupItem(String pattern) { Object selectedItem = model.getSelectedItem(); // only search for a different item if the currently selected does not match if (selectedItem != null && startsWithIgnoreCase(selectedItem.toString(), pattern)) { return selectedItem; } else { // iterate over all items for (int i=0, n=model.getSize(); i < n; i++) { Object currentItem = model.getElementAt(i); // current item starts with the pattern? if (startsWithIgnoreCase(currentItem.toString(), pattern)) { return currentItem; } } } // no item starts with the pattern => return null return null; } // checks if str1 starts with str2 - ignores case private boolean startsWithIgnoreCase(String str1, String str2) { return str1.toUpperCase().startsWith(str2.toUpperCase()); } } |









August 17th, 2010 at 6:48 pm
Your code works great… I’ve made a couple changes so that you can change the ComboBoxModel at runtime with and still have the functionallity:
package net.perroazul.nalib.desktop;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.ComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.PlainDocument;
/*
* Tomado de http://tech.chitgoks.com/2009/11/06/autocomplete-jcombobox/
*/
public class JComboBoxAutoCompletador extends PlainDocument implements FocusListener, KeyListener, PropertyChangeListener {
//
private JComboBox comboBox;
private ComboBoxModel model;
private JTextComponent editor;
private boolean hidePopupOnFocusLoss;
//
//
public JComboBoxAutoCompletador() {
// Bug 5100422 on Java 1.5: Editable JComboBox won’t hide popup when tabbing out
hidePopupOnFocusLoss = System.getProperty(“java.version”).startsWith(“1.5″);
}
public JComboBoxAutoCompletador(JComboBox jcb) {
this();
registraComboBox(jcb);
}
//
//
public void registraComboBox(JComboBox jcb) {
desregistraComboBox();
this.comboBox = jcb;
comboBox.setEditable(true);
model = comboBox.getModel();
editor = (JTextComponent) comboBox.getEditor().getEditorComponent();
editor.setDocument(this);
// Highlight whole text when focus gets lost
editor.addFocusListener(this);
// Highlight whole text when user hits enter
editor.addKeyListener(this);
comboBox.addPropertyChangeListener(this);
// Handle initially selected object
Object selected = comboBox.getSelectedItem();
if(selected != null)
editor.setText(selected.toString());
else
editor.setText(“”);
}
public void desregistraComboBox() {
if(comboBox != null) {
comboBox.getEditor().getEditorComponent().removeFocusListener(this);
comboBox.getEditor().getEditorComponent().removeKeyListener(this);
comboBox.removePropertyChangeListener(this);
comboBox.setSelectedItem(null);
comboBox = null;
}
}
//
//
@Override
public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
// construct the resulting string
String currentText = getText(0, getLength());
String beforeOffset = currentText.substring(0, offs);
String afterOffset = currentText.substring(offs, currentText.length());
String futureText = beforeOffset + str + afterOffset;
// lookup and select a matching item
Object item = lookupItem(futureText);
if (item != null)
comboBox.setSelectedItem(item);
else {
// keep old item selected if there is no match
item = comboBox.getSelectedItem();
// imitate no insert (later on offs will be incremented by str.length(): selection won’t move forward)
offs = offs – str.length();
// provide feedback to the user that his input has been received but can not be accepted
comboBox.getToolkit().beep(); // when available use: UIManager.getLookAndFeel().provideErrorFeedback(comboBox);
}
// remove all text and insert the completed string
super.remove(0, getLength());
super.insertString(0, item.toString(), a);
// if the user selects an item via mouse the the whole string will be inserted.
// highlight the entire text if this happens.
if (item.toString().equals(str) && offs == 0)
highlightCompletedText(0);
else {
highlightCompletedText(offs + str.length());
// show popup when the user types
if(comboBox.isShowing() && comboBox.isFocusOwner())
comboBox.setPopupVisible(true);
}
}
private void highlightCompletedText(int start) {
editor.setCaretPosition(getLength());
editor.moveCaretPosition(start);
}
private Object lookupItem(String pattern) {
Object selectedItem = model.getSelectedItem();
// only search for a different item if the currently selected does not match
if (selectedItem != null && startsWithIgnoreCase(selectedItem.toString(), pattern))
return selectedItem;
else {
// iterate over all items
for (int i = 0, n = model.getSize(); i < n; i++) {
Object currentItem = model.getElementAt(i);
// current item starts with the pattern?
if(startsWithIgnoreCase(currentItem.toString(), pattern))
return currentItem;
}
}
// no item starts with the pattern => return null
return null;
}
private boolean startsWithIgnoreCase(String str1, String str2) {
return str1.toUpperCase().startsWith(str2.toUpperCase());
}
//
//
@Override
public void focusGained(FocusEvent e) { }
@Override
public void focusLost(FocusEvent e) {
highlightCompletedText(0);
// Workaround for Bug 5100422 – Hide Popup on focus loss
if(hidePopupOnFocusLoss)
comboBox.setPopupVisible(false);
}
//
//
@Override
public void keyTyped(KeyEvent e) { }
@Override
public void keyReleased(KeyEvent e) { }
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER)
highlightCompletedText(0);
else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
comboBox.setSelectedIndex(0);
editor.setText(comboBox.getSelectedItem().toString());
highlightCompletedText(0);
}
}
//
//
@Override
public void propertyChange(PropertyChangeEvent evt) {
if(evt.getPropertyName().equals(“model”))
registraComboBox(comboBox);
}
//
}
August 17th, 2010 at 6:52 pm
@oso: thanks for sharing. this can come in handy