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");
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.
This example https://gist.github.com/floralvikings/10290131 only allowed Strings.
I edited it to allow any object and just use that object's toString
method. This allows you to use the object selected to do other things rather than just populate the TextField. Also fixed a bug where an exception would occur if you are deleting a String
in the TextField
that is not a part of the entries.
Example usage:
SortedSet<Address> entries = new TreeSet<>((Address o1, Address o2) -> o1.toString().compareTo(o2.toString()));
entries.add(new Address("50", "Main Street", "Oakville", "Ontario", "T6P4K9"));
entries.add(new Address("3", "Fuller Road", "Toronto", "Ontario", "B6S4T9"));
AutoCompleteTextField<Address> text = new AutoCompleteTextField(entries);
text.getEntryMenu().setOnAction(e ->
{
((MenuItem) e.getTarget()).addEventHandler(Event.ANY, event ->
{
if (text.getLastSelectedObject() != null)
{
text.setText(text.getLastSelectedObject().toString());
System.out.println(text.getLastSelectedObject().getProvince());
}
});
});
AutoCompleteTextField.java
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.geometry.Side;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.TextField;
import java.util.LinkedList;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
/**
* This class is a TextField which implements an "autocomplete" functionality,
* based on a supplied list of entries.<p>
*
* If the entered text matches a part of any of the supplied entries these are
* going to be displayed in a popup. Further the matching part of the entry is
* going to be displayed in a special style, defined by
* {@link #textOccurenceStyle textOccurenceStyle}. The maximum number of
* displayed entries in the popup is defined by
* {@link #maxEntries maxEntries}.<br>
* By default the pattern matching is not case-sensitive. This behaviour is
* defined by the {@link #caseSensitive caseSensitive}
* .<p>
*
* The AutoCompleteTextField also has a List of
* {@link #filteredEntries filteredEntries} that is equal to the search results
* if search results are not empty, or {@link #filteredEntries filteredEntries}
* is equal to {@link #entries entries} otherwise. If
* {@link #popupHidden popupHidden} is set to true no popup is going to be
* shown. This list can be used to bind all entries to another node (a ListView
* for example) in the following way:
* <pre>
* <code>
* AutoCompleteTextField auto = new AutoCompleteTextField(entries);
* auto.setPopupHidden(true);
* SimpleListProperty filteredEntries = new SimpleListProperty(auto.getFilteredEntries());
* listView.itemsProperty().bind(filteredEntries);
* </code>
* </pre>
*
* @author Caleb Brinkman
* @author Fabian Ochmann
* @param <S>
*/
public class AutoCompleteTextField<S> extends TextField
{
private final ObjectProperty<S> lastSelectedItem = new SimpleObjectProperty<>();
/**
* The existing autocomplete entries.
*/
private final SortedSet<S> entries;
/**
* The set of filtered entries:<br>
* Equal to the search results if search results are not empty, equal to
* {@link #entries entries} otherwise.
*/
private ObservableList<S> filteredEntries
= FXCollections.observableArrayList();
/**
* The popup used to select an entry.
*/
private ContextMenu entriesPopup;
/**
* Indicates whether the search is case sensitive or not. <br>
* Default: false
*/
private boolean caseSensitive = false;
/**
* Indicates whether the Popup should be hidden or displayed. Use this if
* you want to filter an existing list/set (for example values of a
* {@link javafx.scene.control.ListView ListView}). Do this by binding
* {@link #getFilteredEntries() getFilteredEntries()} to the list/set.
*/
private boolean popupHidden = false;
/**
* The CSS style that should be applied on the parts in the popup that match
* the entered text. <br>
* Default: "-fx-font-weight: bold; -fx-fill: red;"
* <p>
* Note: This style is going to be applied on an
* {@link javafx.scene.text.Text Text} instance. See the <i>JavaFX CSS
* Reference Guide</i> for available CSS Propeties.
*/
private String textOccurenceStyle = "-fx-font-weight: bold; "
+ "-fx-fill: red;";
/**
* The maximum Number of entries displayed in the popup.<br>
* Default: 10
*/
private int maxEntries = 10;
/**
* Construct a new AutoCompleteTextField.
*
* @param entrySet
*/
public AutoCompleteTextField(SortedSet<S> entrySet)
{
super();
this.entries = (entrySet == null ? new TreeSet<>() : entrySet);
this.filteredEntries.addAll(entries);
entriesPopup = new ContextMenu();
textProperty().addListener((ObservableValue<? extends String> observableValue, String s, String s2) ->
{
if (getText() == null || getText().length() == 0)
{
filteredEntries.clear();
filteredEntries.addAll(entries);
entriesPopup.hide();
} else
{
LinkedList<S> searchResult = new LinkedList<>();
//Check if the entered Text is part of some entry
String text1 = getText();
Pattern pattern;
if (isCaseSensitive())
{
pattern = Pattern.compile(".*" + text1 + ".*");
} else
{
pattern = Pattern.compile(".*" + text1 + ".*", Pattern.CASE_INSENSITIVE);
}
for (S entry : entries)
{
Matcher matcher = pattern.matcher(entry.toString());
if (matcher.matches())
{
searchResult.add(entry);
}
}
if (!entries.isEmpty())
{
filteredEntries.clear();
filteredEntries.addAll(searchResult);
//Only show popup if not in filter mode
if (!isPopupHidden())
{
populatePopup(searchResult, text1);
if (!entriesPopup.isShowing())
{
entriesPopup.show(AutoCompleteTextField.this, Side.BOTTOM, 0, 0);
}
}
} else
{
entriesPopup.hide();
}
}
});
focusedProperty().addListener((ObservableValue<? extends Boolean> observableValue, Boolean aBoolean, Boolean aBoolean2) ->
{
entriesPopup.hide();
});
}
/**
* Get the existing set of autocomplete entries.
*
* @return The existing autocomplete entries.
*/
public SortedSet<S> getEntries()
{
return entries;
}
/**
* 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<S> searchResult, String text)
{
List<CustomMenuItem> menuItems = new LinkedList<>();
int count = Math.min(searchResult.size(), getMaxEntries());
for (int i = 0; i < count; i++)
{
final String result = searchResult.get(i).toString();
final S itemObject = searchResult.get(i);
int occurence;
if (isCaseSensitive())
{
occurence = result.indexOf(text);
} else
{
occurence = result.toLowerCase().indexOf(text.toLowerCase());
}
if (occurence < 0)
{
continue;
}
//Part before occurence (might be empty)
Text pre = new Text(result.substring(0, occurence));
//Part of (first) occurence
Text in = new Text(result.substring(occurence, occurence + text.length()));
in.setStyle(getTextOccurenceStyle());
//Part after occurence
Text post = new Text(result.substring(occurence + text.length(), result.length()));
TextFlow entryFlow = new TextFlow(pre, in, post);
CustomMenuItem item = new CustomMenuItem(entryFlow, true);
item.setOnAction((ActionEvent actionEvent) ->
{
lastSelectedItem.set(itemObject);
entriesPopup.hide();
});
menuItems.add(item);
}
entriesPopup.getItems().clear();
entriesPopup.getItems().addAll(menuItems);
}
public S getLastSelectedObject()
{
return lastSelectedItem.get();
}
public ContextMenu getEntryMenu()
{
return entriesPopup;
}
public boolean isCaseSensitive()
{
return caseSensitive;
}
public String getTextOccurenceStyle()
{
return textOccurenceStyle;
}
public void setCaseSensitive(boolean caseSensitive)
{
this.caseSensitive = caseSensitive;
}
public void setTextOccurenceStyle(String textOccurenceStyle)
{
this.textOccurenceStyle = textOccurenceStyle;
}
public boolean isPopupHidden()
{
return popupHidden;
}
public void setPopupHidden(boolean popupHidden)
{
this.popupHidden = popupHidden;
}
public ObservableList<S> getFilteredEntries()
{
return filteredEntries;
}
public int getMaxEntries()
{
return maxEntries;
}
public void setMaxEntries(int maxEntries)
{
this.maxEntries = maxEntries;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With