Then, is it easy for Swing’s textbox to realize this? Examples are also available on the Net, but either too simple-functioned or too wordy-coded. In addition, some commercial Swing components are not free-charged. This article involves the application of the free ERP software development in 2BizBox and manages to achieve the effects with a very easy and effective way.
At first, let’s see it closely: it is a textbox, not a ComboBox, from appearance and nature. Therefore, we would not like to make it in to a ComboBox, that is, we don’t feel like inheriting from JComboBox. Next, ComboBox is dynamic and completely nonsynchronous with the input field because it is just a suggestion, unable to interfere with the texts input. Finally, the appearance and the act of the ComboBox is undoubtedly that of the ComboBox of JComboBox. So, “the JTextField which is able to complete automatically” should be a combination of the ComboBox of both JTextField and JComboBox.
After the analysis above, we can make sure that it is actually a JTextField, but also combined with a JComboBox’s ComboBox. Then in this case it on earth inherits from which one? From JTextField?
In fact, inheriting from others is not the best way. Why? It means that only by inheriting from your class can others use this function. If there are already more than 10 thousand interfaces written in your project and you would like to add the intelligent suggesting functions into some of the textboxes, will you change all of these codes in order to let them inherit your class? There is no doubt that that is an awful idea. For this reason, it’s not good for those people who just learned OO to inherit at every move. Is it not a better idea that we just offer a method Util to make a little change on the already existing JTextField instances, thus having the intelligent suggesting functions?
In order to combine the two components JTextField and JComboBox, here we use a very “special”method which is definitely out of your imagination, that is, put a JComboBox into JTextField and make that invisible. Let’s see the code:
JTextField txtInput = new JTextField();
JComboBox cbInput = new JComboBox();
txtInput.setLayout(new BorderLayout());
txtInput.add(cbInput, BorderLayout.SOUTH);
What? Set JTextField as a layout and add a JComboBox to put it in south? I believe that you have never heard of that. It is so peculiar. But it looks perfect once you set the height of JComboBox as 0.
JComboBox cbInput = new JComboBox(model) {
public Dimension getPreferredSize() {
return new Dimension(super.getPreferredSize().width, 0);
}
};
Although you can’t see Combo, it does exist in the textbox and is at the south of it. Our idea is: when texts are typed into the textbox, it will judge whether there are something conform to the requirements in the ComboBox. If there is, then the ComboBox will pop out immediately or it will disappear.
It is not difficult to keep an eye on the textbox: add listener to its document. Here we have used the rules as “case-insensitive” and “the items having the same opening as the string input”to filter. All the alternative strings are put in a single array, and those which conform to the conditions will be filtered out dynamically. Then they will be added into JComboBox and pop up the ComboBox.
public void insertUpdate(DocumentEvent e) {
updateList();
}
public void removeUpdate(DocumentEvent e) {
updateList();
}
public void changedUpdate(DocumentEvent e) {
updateList();
}
private void updateList() {
setAdjusting(cbInput, true);
model.removeAllElements();
String input = txtInput.getText();
if (!input.isEmpty()) {
for (String item : items) {
if (item.toLowerCase().startsWith(input.toLowerCase())) {
model.addElement(item);
}
}
}
cbInput.setPopupVisible(model.getSize() > 0);
setAdjusting(cbInput, false);
}
});
In addition, for much more convenience, we have added several shortcut keys: when ESC is input, the ComboBox will be closed automatically; when inputting a carriage return or a space, the first string which meets the requirement will be input in the textbox directly:
@Override
public void keyPressed(KeyEvent e) {
setAdjusting(cbInput, true);
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
if (cbInput.isPopupVisible()) {
e.setKeyCode(KeyEvent.VK_ENTER);
}
}
if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN) {
e.setSource(cbInput);
cbInput.dispatchEvent(e);
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
txtInput.setText(cbInput.getSelectedItem().toString());
cbInput.setPopupVisible(false);
}
}
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
cbInput.setPopupVisible(false);
}
setAdjusting(cbInput, false);
}
});
There is another important method needed to be explained: when popping up the popup list, we want that the arrow on keyboard can select the items in the list both upward and downward and that at the same time the current cursor and the focus still stay in the textbox. This seems to be very difficult. Please look at how we make it: when we keep an eye on the textbox and find out that the items in the list are to be input by the up and down arrows, we change source of the current keyboard events to JComboBox and then distribute it to JComboBox. That is to say, originally, the source of the event is the textbox, but now we have intercepted the postman, changed the receiver and then continued to give it back to him. In this way, we have grafted one twig on another.
if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN) {
e.setSource(cbInput);
cbInput.dispatchEvent(e);
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
txtInput.setText(cbInput.getSelectedItem().toString());
cbInput.setPopupVisible(false);
}
}
At last, to see the effects, we need to put some data in the ComboBox. Then what to put? We choose “all countries” in Java to avoid troubleness.
Locale[] locales = Locale.getAvailableLocales();
for (int i = 0; i < locales.length; i++) {
String item = locales[i].getDisplayName();
items.add(item);
}
Now let’s see the effect. Good. It well lives up to our expectations.
The complete code is listed as follow:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import twaver.*;
public class Test {
public static void main(String[] args) throws Exception {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
JFrame frame = new JFrame();
frame.setTitle("Auto Completion Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(200, 200, 500, 400);
ArrayList<String> items = new ArrayList<String>();
Locale[] locales = Locale.getAvailableLocales();
for (int i = 0; i < locales.length; i++) {
String item = locales[i].getDisplayName();
items.add(item);
}
JTextField txtInput = new JTextField();
setupAutoComplete(txtInput, items);
txtInput.setColumns(30);
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(txtInput, BorderLayout.NORTH);
frame.setVisible(true);
}
private static boolean isAdjusting(JComboBox cbInput) {
if (cbInput.getClientProperty("is_adjusting") instanceof Boolean) {
return (Boolean) cbInput.getClientProperty("is_adjusting");
}
return false;
}
private static void setAdjusting(JComboBox cbInput, boolean adjusting) {
cbInput.putClientProperty("is_adjusting", adjusting);
}
public static void setupAutoComplete(final JTextField txtInput, final ArrayList<String> items) {
final DefaultComboBoxModel model = new DefaultComboBoxModel();
final JComboBox cbInput = new JComboBox(model) {
public Dimension getPreferredSize() {
return new Dimension(super.getPreferredSize().width, 0);
}
};
setAdjusting(cbInput, false);
for (String item : items) {
model.addElement(item);
}
cbInput.setSelectedItem(null);
cbInput.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (!isAdjusting(cbInput)) {
if (cbInput.getSelectedItem() != null) {
txtInput.setText(cbInput.getSelectedItem().toString());
}
}
}
});
txtInput.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
setAdjusting(cbInput, true);
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
if (cbInput.isPopupVisible()) {
e.setKeyCode(KeyEvent.VK_ENTER);
}
}
if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN) {
e.setSource(cbInput);
cbInput.dispatchEvent(e);
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
txtInput.setText(cbInput.getSelectedItem().toString());
cbInput.setPopupVisible(false);
}
}
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
cbInput.setPopupVisible(false);
}
setAdjusting(cbInput, false);
}
});
txtInput.getDocument().addDocumentListener(new DocumentListener() {
public void insertUpdate(DocumentEvent e) {
updateList();
}
public void removeUpdate(DocumentEvent e) {
updateList();
}
public void changedUpdate(DocumentEvent e) {
updateList();
}
private void updateList() {
setAdjusting(cbInput, true);
model.removeAllElements();
String input = txtInput.getText();
if (!input.isEmpty()) {
for (String item : items) {
if (item.toLowerCase().startsWith(input.toLowerCase())) {
model.addElement(item);
}
}
}
cbInput.setPopupVisible(model.getSize() > 0);
setAdjusting(cbInput, false);
}
});
txtInput.setLayout(new BorderLayout());
txtInput.add(cbInput, BorderLayout.SOUTH);
}
}
There is a problem when backsapce button is pressed. Popup is showing but combobox is cut (1 item is visible). Is there any solution for this bug?
ReplyDeleteyou can check for best way to make autocomplete textfield in Swing :
Deletehttp://geekonjava.blogspot.com/2015/07/auto-complete-text-field-swing.html
I've figured out how to fix the bug. In the updateList method set popup false and then set it to model size > 0. Basically, this resets the popup display. I'd personally use a boolean array for this. Pretty basic, simple fix.
Deleteplease change:
ReplyDeletecbInput.setPopupVisible(model.getSize() > 0);
TO
cbInput.hidePopup();
cbInput.setPopupVisible(model.getSize() > 0);
It doesn't works for me. Only you have to do this?
DeleteWhat happen?
cbInput.updateUI();
DeletecbInput.setPopupVisible(model.getSize() > 0);
Works fine
Can you help me, make a search like in facebook?
ReplyDeleteevery search there are pictures and text
This comment has been removed by the author.
ReplyDeletePlease see:
Deletehttp://twaver.blogspot.com/2013/04/add-function-autocomplete-in-jtextfield2.html
Very god article, you made my day, thank you very much.
ReplyDeleteGreat work, thank you very much!
ReplyDeleteSuper article. Thank you.
ReplyDeleteIs there a possibility that the next field gets directly to the Tab Focus?
why i can't get key from ArrayList in method autoComplete
ReplyDeletei need this auto suggestion with jCombobox, please help me
ReplyDeleteHi, I have one problem with this auticomplete, when I delete all caracters and I retype them again it dosn't show the popup menu again, can you help me to solve this probleml?
ReplyDeleteafter hitting enter key, the pop up is still visible,
ReplyDeleteHow to hide this pop after hitting enter button?
1000 Excelent! Thanks Friend
ReplyDeletei have a problem when i uses this AutocompleteJTextField with Jcombobox. when i select country in Jcombobox and after that i select another item in Jcombobox but in Jtextfield autocomplete list show item according to country only,this show according to first item selected in jcombobox,please reply me if any solution for this problem
ReplyDeleteGreat work!
ReplyDeleteThere is an autocomplete for very large data set using Trie and Machine Learning for suggestions.
ReplyDeletehttp://wp.me/p976D1-cH
how can you change the locale which shows the data of all the countries into the data from your database?? Any help would be much appreciated..
ReplyDelete