I want to make this TextField have suggestions feature just like in Lucene. I've searched all the web and I just find it for ComboBox.
TextField instNameTxtFld = instNameTxtFld();
private TextField instNameTxtFld() {
TextField txtFld = new TextField();
txtFld.setPrefSize(600, 75);
return txtFld;
}
The reason that I can't use the method for ComboBox is because I can't input the value to database below if I use ComboBox.
private void goNext() {
if (nameTxtFld.getText() == null || nameTxtFld.getText().trim().isEmpty()
|| instNameTxtFld.getText()== null || instNameTxtFld.getText().trim().isEmpty()
|| addTxtArea.getText() == null || addTxtArea.getText().trim().isEmpty()) {
alertDialog.showAndWait();
} else {
String satu = idNumTxtFld.getText();
String dua = nameTxtFld.getText();
String tiga = addTxtArea.getText();
String empat = instNameTxtFld.getText();
int delapan = idType.getSelectionModel().getSelectedIndex();
String sembilan = timeStamp.getText();
try {
KonekDB.createConnection();
Statement st = KonekDB.conn.createStatement();
String sql = "INSERT INTO privateguest"
+ "(idNumber, name, address, institution, idType, startTime) "
+ "VALUES "
+ "('" + satu + "','" + dua + "','" + tiga + "','" + empat + "','" + delapan + "','" + sembilan + "')";
System.out.println(sql);
st.executeUpdate(sql);
} catch (SQLException ex) {
System.out.println(satu + " " + dua + " " + tiga + " " + empat + " " + delapan + " " + sembilan);
System.out.println("SQL Exception (next)");
ex.printStackTrace();
}
Frame3Private frame3 = new Frame3Private(english);
this.getScene().setRoot(frame3);
}
}
Please help me to make the most simple code for doing TextField suggestions/ auto-complete.
Here is my solution based on This.
public class AutocompletionlTextField extends TextFieldWithLengthLimit {
//Local variables
//entries to autocomplete
private final SortedSet<String> entries;
//popup GUI
private ContextMenu entriesPopup;
public AutocompletionlTextField() {
super();
this.entries = new TreeSet<>();
this.entriesPopup = new ContextMenu();
setListner();
}
/**
* wrapper for default constructor with setting of "TextFieldWithLengthLimit" LengthLimit
*
* @param lengthLimit
*/
public AutocompletionlTextField(int lengthLimit) {
this();
super.setLengthLimit(lengthLimit);
}
/**
* "Suggestion" specific listners
*/
private void setListner() {
//Add "suggestions" by changing text
textProperty().addListener((observable, oldValue, newValue) -> {
String enteredText = getText();
//always hide suggestion if nothing has been entered (only "spacebars" are dissalowed in TextFieldWithLengthLimit)
if (enteredText == null || enteredText.isEmpty()) {
entriesPopup.hide();
} else {
//filter all possible suggestions depends on "Text", case insensitive
List<String> filteredEntries = entries.stream()
.filter(e -> e.toLowerCase().contains(enteredText.toLowerCase()))
.collect(Collectors.toList());
//some suggestions are found
if (!filteredEntries.isEmpty()) {
//build popup - list of "CustomMenuItem"
populatePopup(filteredEntries, enteredText);
if (!entriesPopup.isShowing()) { //optional
entriesPopup.show(AutocompletionlTextField.this, Side.BOTTOM, 0, 0); //position of popup
}
//no suggestions -> hide
} else {
entriesPopup.hide();
}
}
});
//Hide always by focus-in (optional) and out
focusedProperty().addListener((observableValue, oldValue, newValue) -> {
entriesPopup.hide();
});
}
/**
* Populate the entry set with the given search results. Display is limited to 10 entries, for performance.
*
* @param searchResult The set of matching strings.
*/
private void populatePopup(List<String> searchResult, String searchReauest) {
//List of "suggestions"
List<CustomMenuItem> menuItems = new LinkedList<>();
//List size - 10 or founded suggestions count
int maxEntries = 10;
int count = Math.min(searchResult.size(), maxEntries);
//Build list as set of labels
for (int i = 0; i < count; i++) {
final String result = searchResult.get(i);
//label with graphic (text flow) to highlight founded subtext in suggestions
Label entryLabel = new Label();
entryLabel.setGraphic(Styles.buildTextFlow(result, searchReauest));
entryLabel.setPrefHeight(10); //don't sure why it's changed with "graphic"
CustomMenuItem item = new CustomMenuItem(entryLabel, true);
menuItems.add(item);
//if any suggestion is select set it into text and close popup
item.setOnAction(actionEvent -> {
setText(result);
positionCaret(result.length());
entriesPopup.hide();
});
}
//"Refresh" context menu
entriesPopup.getItems().clear();
entriesPopup.getItems().addAll(menuItems);
}
/**
* Get the existing set of autocomplete entries.
*
* @return The existing autocomplete entries.
*/
public SortedSet<String> getEntries() { return entries; }
}
You must extends from "TextField" instead of "TextFieldWithLengthLimit" and delete constructor with "Length limit".
I use static methods to work with Styles. It's used here to "highlight" entered text inside suggestion results. Here is the code of methos from this class:
/**
* Build TextFlow with selected text. Return "case" dependent.
*
* @param text - string with text
* @param filter - string to select in text
* @return - TextFlow
*/
public static TextFlow buildTextFlow(String text, String filter) {
int filterIndex = text.toLowerCase().indexOf(filter.toLowerCase());
Text textBefore = new Text(text.substring(0, filterIndex));
Text textAfter = new Text(text.substring(filterIndex + filter.length()));
Text textFilter = new Text(text.substring(filterIndex, filterIndex + filter.length())); //instead of "filter" to keep all "case sensitive"
textFilter.setFill(Color.ORANGE);
textFilter.setFont(Font.font("Helvetica", FontWeight.BOLD, 12));
return new TextFlow(textBefore, textFilter, textAfter);
}
You may add this "AutocompletionlTextField" in FXML (dont forget about "imports") or inside constructor. To set "suggestions" list on use "entries" getter:
AutocompletionlTextField field = new AutocompletionlTextField();
field.getEntries().addAll(YOUR_ARRAY_OF_STRINGS);
It seems like that in my application:
Hope it helps.
You can use ControlsFX --> maven
Solution:
TextFields.bindAutoCompletion(textfield,"text to suggest", "another text to suggest");
Here is my solution - a complete method only with a ComboBox parameter:
/**
* My own autocomplete combobox
*
* @param categoryComboBox
*/
public static void bindAutoCompleteToComboBox(ComboBox<String> categoryComboBox) {
/**
* backup the original list
*/
List<String> categoryComboBoxItemsList = new ArrayList<String>(categoryComboBox.getItems());
/**
* if mouse pressed: select all of the text field
*/
categoryComboBox.getEditor().setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
Platform.runLater(new Runnable() {
@Override
public void run() {
if (categoryComboBox.getEditor().isFocused() && !categoryComboBox.getEditor().getText().isEmpty()) {
categoryComboBox.getEditor().selectAll();
}
}
});
}
});
/**
* events on text input
*/
categoryComboBox.setOnKeyReleased(new EventHandler<KeyEvent>() {
private List<String> reducedList = new ArrayList<String>();
@Override
public void handle(KeyEvent event) {
if (event.getCode().isLetterKey() || event.getCode().isDigitKey() || event.getCode().equals(KeyCode.BACK_SPACE)) {
/**
* Open comboBox if letter, number or backspace
*/
categoryComboBox.show();
String temp = categoryComboBox.getEditor().getText();
reducedList = new ArrayList<String>();
/**
* If backspace pressed, selection refers to the basic list again
*/
if (event.getCode().equals(KeyCode.BACK_SPACE)) {
categoryComboBox.getItems().clear();
categoryComboBox.getItems().addAll(categoryComboBoxItemsList);
// java fx workaround to restore the default list height of 10
categoryComboBox.hide();
categoryComboBox.setVisibleRowCount(10);
categoryComboBox.show();
}
/**
* loop through all entrys and look whether input contains this text.
*
* after that, entry will be added to the reduced list
*/
for (String element : categoryComboBox.getItems()) {
if (StringUtils.containsIgnoreCase(element, temp)) {
reducedList.add(element);
}
}
/**
* all elements are cleared, the reduced list will be added. First element is selected
*/
categoryComboBox.getItems().clear();
categoryComboBox.getItems().addAll(reducedList);
categoryComboBox.getSelectionModel().select(0);
categoryComboBox.getEditor().setText(temp);
} else if (event.getCode().equals(KeyCode.ENTER)) {
/**
* if enter, the element which is selected will be applied to the text field and the dropdown will be closed
*/
if (categoryComboBox.getSelectionModel().getSelectedIndex() != -1) {
categoryComboBox.getEditor().setText(categoryComboBox.getItems().get((categoryComboBox.getSelectionModel().getSelectedIndex())));
} else {
categoryComboBox.getEditor().setText(categoryComboBox.getItems().get(0));
}
} else if (event.getCode().equals(KeyCode.DOWN)) {
/**
* arrow down shows the dropdown
*/
categoryComboBox.show();
}
/**
* Tab marks everything (when tabbing into the field
*/
if (event.getCode().equals(KeyCode.TAB)) {
Platform.runLater(new Runnable() {
@Override
public void run() {
if (categoryComboBox.getEditor().isFocused() && !categoryComboBox.getEditor().getText().isEmpty()) {
categoryComboBox.getEditor().selectAll();
}
}
});
} else {
/**
* all entries except for tab put the caret on the last character
*/
Platform.runLater(new Runnable() {
@Override
public void run() {
categoryComboBox.getEditor().positionCaret(categoryComboBox.getEditor().getText().length());
}
});
}
}
});
/**
* focus lost
*/
categoryComboBox.focusedProperty().addListener(new ChangeListener<Boolean>() {
/**
* if focus lost: refill the category combo box with the original items
*/
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (oldValue) {
/**
* saves whether textfield was empty before reset the comboBox
*/
boolean emptyTextField = categoryComboBox.getEditor().getText().isEmpty();
if (categoryComboBox.getSelectionModel().getSelectedIndex() != -1) {
categoryComboBox.getEditor().setText(categoryComboBox.getItems().get(categoryComboBox.getSelectionModel().getSelectedIndex()));
}
String temp = categoryComboBox.getEditor().getText();
categoryComboBox.getItems().clear();
categoryComboBox.getItems().addAll(categoryComboBoxItemsList);
if (!emptyTextField) {
categoryComboBox.getSelectionModel().select(temp);
} else {
categoryComboBox.getEditor().setText("");
}
}
}
});
}
There is another solution with JFoenix. Since February 2018 they added autocompletion class. This is implementation of it.
// when initializing the window or in some other method
void initialize() {
JFXAutoCompletePopup<String> autoCompletePopup = new JFXAutoCompletePopup<>();
autoCompletePopup.getSuggestions().addAll("option1", "option2", "...");
autoCompletePopup.setSelectionHandler(event -> {
textField.setText(event.getObject());
// you can do other actions here when text completed
});
// filtering options
textField.textProperty().addListener(observable -> {
autoCompletePopup.filter(string -> string.toLowerCase().contains(textField.getText().toLowerCase()));
if (autoCompletePopup.getFilteredSuggestions().isEmpty() || textField.getText().isEmpty()) {
autoCompletePopup.hide();
// if you remove textField.getText.isEmpty() when text field is empty it suggests all options
// so you can choose
} else {
autoCompletePopup.show(textField);
}
});
}
This is a bit new approach and worked fine with me. Hope it will help and thanks to JFoenix developers.