Share the post "How To Create AutoComplete ComboBox Or TextField In Java FX 2"
Well, since there is no built-in class that does this functionality, the only solution was to create my own. Here is my version of an auto-complete class that can be used for a TextField or editable ComboBox.
If you want to use this for a TextField node, just change parts of the code to cater to it. It should not be hard.
|
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 109 110 111 112 113 114 115 |
public class AutoCompleteComboBoxListener implements EventHandler<KeyEvent> { private ComboBox comboBox; private StringBuilder sb; private int lastLength; public AutoCompleteComboBoxListener(ComboBox comboBox) { this.comboBox = comboBox; sb = new StringBuilder(); this.comboBox.setEditable(true); this.comboBox.setOnKeyReleased(AutoCompleteComboBoxListener.this); // add a focus listener such that if not in focus, reset the filtered typed keys this.comboBox.getEditor().focusedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { if (newValue) { // in focus } else { lastLength = 0; sb.delete(0, sb.length()); selectClosestResultBasedOnTextFieldValue(false, false); } } }); this.comboBox.setOnMouseClicked(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { selectClosestResultBasedOnTextFieldValue(true, true); } }); } @Override public void handle(KeyEvent event) { // this variable is used to bypass the auto complete process if the length is the same. // this occurs if user types fast, the length of textfield will record after the user // has typed after a certain delay. if (lastLength != (comboBox.getEditor().getLength() - comboBox.getEditor().getSelectedText().length())) lastLength = comboBox.getEditor().getLength() - comboBox.getEditor().getSelectedText().length(); if (event.isControlDown() || event.getCode() == KeyCode.BACK_SPACE || event.getCode() == KeyCode.RIGHT || event.getCode() == KeyCode.LEFT || event.getCode() == KeyCode.DELETE || event.getCode() == KeyCode.HOME || event.getCode() == KeyCode.END || event.getCode() == KeyCode.TAB ) return; IndexRange ir = comboBox.getEditor().getSelection(); sb.delete(0, sb.length()); sb.append(comboBox.getEditor().getText()); // remove selected string index until end so only unselected text will be recorded try { sb.delete(ir.getStart(), sb.length()); } catch (Exception e) { } ObservableList items = comboBox.getItems(); for (int i=0; i<items.size(); i++) { if (items.get(i).toString().toLowerCase().startsWith(comboBox.getEditor().getText().toLowerCase()) ) { try { comboBox.getEditor().setText(sb.toString() + items.get(i).toString().substring(sb.toString().length())); } catch (Exception e) { comboBox.getEditor().setText(sb.toString()); } comboBox.getEditor().positionCaret(sb.toString().length()); comboBox.getEditor().selectEnd(); break; } } } /* * selectClosestResultBasedOnTextFieldValue() - selects the item and scrolls to it when * the popup is shown. * * parameters: * affect - true if combobox is clicked to show popup so text and caret position will be readjusted. * inFocus - true if combobox has focus. If not, programmatically press enter key to add new entry to list. * */ private void selectClosestResultBasedOnTextFieldValue(boolean affect, boolean inFocus) { ObservableList items = AutoCompleteComboBoxListener.this.comboBox.getItems(); boolean found = false; for (int i=0; i<items.size(); i++) { if (AutoCompleteComboBoxListener.this.comboBox.getEditor().getText().toLowerCase().equals(items.get(i).toString().toLowerCase())) { try { ListView lv = ((ComboBoxListViewSkin) AutoCompleteComboBoxListener.this.comboBox.getSkin()).getListView(); lv.getSelectionModel().clearAndSelect(i); lv.scrollTo(lv.getSelectionModel().getSelectedIndex()); found = true; break; } catch (Exception e) { } } } String s = comboBox.getEditor().getText(); if (!found && affect) { comboBox.getSelectionModel().clearSelection(); comboBox.getEditor().setText(s); comboBox.getEditor().end(); } if (!inFocus && comboBox.getEditor().getText() != null && comboBox.getEditor().getText().trim().length() > 0) { // press enter key programmatically to have this entry added KeyEvent ke = KeyEvent.impl_keyEvent(comboBox, KeyCode.ENTER.toString(), KeyCode.ENTER.getName(), KeyCode.ENTER.impl_getCode(), false, false, false, false, KeyEvent.KEY_RELEASED); comboBox.fireEvent(ke); } } } |
To use this class, just do
|
1 |
new AutoCompleteComboBoxListener(myComboBox); |
Easy, right?
This class also includes an added feature wherein after you type something in the editable ComboBox and when you press the ENTER key, an action will take place: say, you want to save it to the database or something.
Note: the focus listener is now added to the ComboBox’s TextField component because in the development environment, it works fine. However, when deployed as an Applet, it does not work. Only when I shifted the focus listener to the ComboBox’s editor did it work correctly.
This worked. Of all things I have found, this works. The only issue(I am certain it is my code) is that when I try to comboBox.getValue(), it does not seem to load the value.
But everything else, filter/autocomplete, amazing!
Thankyou, thankyou, thankyou.
your combobox probably has not been populated with data?.
make sure you add list first then use this class
I add a filled observablelist to the combobox, then apply your java file.
The list populates fine. But when I try to load the combobox’s value into a method as the T object of the observablelist, then use a method on that object, (i.e. getName()), it declares a null pointer.
Is it poosible that the ComboBox’s text is being set, but the value is not being set. I’m not as familiar with Java, especially comboboxes, as you are. Is that possible?
This is my main class:
http://pastebin.com/P1EuJhbm
This is my “info pane”:
http://pastebin.com/6z3tiF42
The infopane is meant to load in data from the Oil selected in the comboBox. The null ptr is on line 43.
@nicholas: lol, im very lazy checking out someone else’s code. but your problem may be that just because you type something in the editable combobox does not mean that your custom object is linked to it.
the editable combobox will always return a String. what you can do is when you call getValue() check if it is an instanceof String then do a lookup in your list and return that Object.
if the combobox is clicked and the list is shown in the combobox popup, once you select an item, it will return that custom object.
Thankyou for the help you’ve provided thus far.
Your code works great non-the-less.
class for use =)
package ejemplo;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.stage.Stage;
public class JavaFx extends Application {
ComboBox combobox;
AutoCompleteComboBoxListener aux;
ObservableList data = FXCollections.observableArrayList(
“Todos”,
“alegria”,
“chilevision”,
“america”,
“ana”,
“chileno”,
“BCX”
);
public static void main(String[] args) {
//Esto se utiliza para ejecutar la aplicación
//es como el new Contructor();
launch(args);
}
//Este metodo es obligatorio
@Override
public void start( Stage primaryStage ){
Group root = new Group();
//El grupo que se desea agregar, y el tamaño ancho y alto
Scene scene = new Scene( root, 300, 300 );
//Titulo de la ventana
primaryStage.setTitle(“JavaFx”);
//Se agrega la scena
primaryStage.setScene( scene );
//Creacion del boton
combobox = new ComboBox();
combobox.setLayoutX(105);
combobox.setLayoutY(110);
combobox.setItems(data);
aux = new AutoCompleteComboBoxListener(combobox);
combobox.setOnAction(actionButton);
root.getChildren().add(combobox);
//Para mostrar la visible, semejante al setVisible(true)
primaryStage.show();
}
private final EventHandler actionButton = new EventHandler() {
@Override
public void handle(final ActionEvent t) {
System.out.println(“Accion del combobox ” +combobox.getSelectionModel().selectedItemProperty().getValue());
// Stage stage = (Stage) boton.getScene().getWindow();
// stage.close();
}
};
}
Hi, not working in JavaFX 8. Error here:
KeyEvent ke = KeyEvent.impl_keyEvent(comboBox, KeyCode.ENTER.toString(), KeyCode.ENTER.getName(), KeyCode.ENTER.impl_getCode(), false, false, false, false, KeyEvent.KEY_RELEASED);
if (!inFocus && comboBox.getEditor().getText() != null && comboBox.getEditor().getText().trim().length() > 0) {
// press enter key programmatically to have this entry added
Robot robot = com.sun.glass.ui.Application.GetApplication().createRobot();
robot.keyPress(java.awt.event.KeyEvent.VK_ENTER);
KeyCode.ENTER.getName(), KeyCode.ENTER.impl_getCode(), false, false, false, false, KeyEvent.KEY_RELEASED);
}
Edit:
if (!inFocus && comboBox.getEditor().getText() != null && comboBox.getEditor().getText().trim().length() > 0) {
// press enter key programmatically to have this entry added
Robot robot = com.sun.glass.ui.Application.GetApplication().createRobot();
robot.keyPress(java.awt.event.KeyEvent.VK_ENTER);
}
hello sorry cant help you with just that. is there an error? or it wont compile
i have not tried java fx 8 though so cant help you
KeyEvent.impl_keyEvent is not longer part of the API in Javafx8
i see. but my post is about java fx2 not fx8
Hi, how i can fill the combobox from database, show only a column (for example ‘Name of Peoples’) and get other column (for example ‘Id of peoples’)?
Thanks.
@padre.cedano:
1) you do not need Java FX to load data from database to ComboBox. You need Java for that.
2) what do you mean by show only a column? You probably need to use a Table.
3) To fill the ComboBox, combobox.setItems(ObservableList);
@blogmeister
Thanks for your response.
I need populate combobox with object as:
private final ObservableList data =
FXCollections.observableArrayList(
new Couple(1,”Name1″),
new Couple(2,”Name2″),
new Couple(3,”Name3″));
When i says “show only a column” i says that i only need show in combobox the second column: Name1, Name2…
After i need to add an event to combobox for get the first column of selected item Couple: 1,2,3
I can get this value if the user choose one value into the combobox but, in editable combobox i have error NPE if the user write a new value. I read in some forums that the solution is to apply a string converter to editable combo box, but this not works for me.
@padre if you can wait tomorrow ill post my code so you wont get that NPE
to show the name of the Couple in the combobox just override toString() in your Couple class.
e.g.
class Couple { … public String toString() { return name; } }
hi @padre, im not really sure what you mean without seeing a running sample code.
i also dont get why you get an NPE. make sure from the editable combobox you do not use the combobox item because the value returned is always a String. you only get a combobox value if you click on the dropdown arrow and swlect an item.
What i did was to check if the value returned is an instance of String or not. if it is, i search the list of Couple to see if there is a matching name, then return it. If there is no match, return null. make sure to add an If statement because maybe the combobox did not return anything.
If you happen to enter the ComboBox by pressing Shift+Tab, the first item in the array is auto-completed. To avoid this add the following code after the line “ObservableList items = comboBox.getItems();”
if ((comboBox.getEditor().getText().length())>0){
…(and close the brace after “comboBox.getEditor().selectEnd();
break;”
Thank you,
This is a great simple and efficient autocomplete combobox.
I am looking for an autocomplete combobox which does more than that and surprise !
I found it in the link above : how-to-go-to-item-in-combobox-on-keypress-in-java-fx-2
this works great also !
I would like to mix your two listener:
AutoCompleteComboBoxListener and SelectKeyComboBoxListener.
to have both behaviours.
So when you type the first letter of a word, it finds and displays complement in button cell but also select the first match in the list.
What happens now is that the first matching word is selected in the listView, the first matching word is displayed in button cell but unfortunately, the caret position is reset at the beginning of the word and no characters are selected.
Example, i type “J”, and he will Display “|Jack” with cursor before the “J”.
It means that if I click quickly “Ja” he will display “|aJack”.
But again thank you, I think it is a good base to find a solution.
added a function for it to be compatible text with accents. Just added a new feature:
public static String unAccent(String s) {
String temp = Normalizer.normalize(s, Normalizer.Form.NFD);
Pattern pattern = Pattern.compile(“\\p{InCombiningDiacriticalMarks}+”);
return pattern.matcher(temp).replaceAll(“”);
}
and used it in handle Override, the segunte way:
…
for (Object data1 : this.data) {
// if (!data1.toString().toLowerCase().contains(comboBox.getEditor().getText().toLowerCase())) continue;
// list.add(data1);
if(unAccent(data1.toString().toLowerCase()).contains(unAccent(comboBox.getEditor().getText().toLowerCase())))
list.add(data1);
}
…
I hope to have contributed to the AutoCompleteComboBox. xD
@Rogério: thank you for this!