2 回答
TA贡献1883条经验 获得超3个赞
我的建议是FilteredList从您的原始列表中创建一个。然后,使用 aPredicate过滤掉不匹配的结果。如果您将ComboBox项目设置为该过滤列表,它将始终显示所有项目或与您的搜索词匹配的项目。
将ValueProperty只会更新,当用户“提交”按变化[进入]。
我在这里有一个简短的 MCVE 应用程序来演示整个注释:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
// Create a list of items
private final ObservableList<String> items = FXCollections.observableArrayList();
// Create the ComboBox
private final ComboBox<String> comboBox = new ComboBox<>();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
// Simple Interface
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
// Allow manual entry into ComboBox
comboBox.setEditable(true);
// Add sample items to our list
items.addAll("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten");
createListener();
root.getChildren().add(comboBox);
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Filtered ComboBox");
primaryStage.show();
}
private void createListener() {
// Create the listener to filter the list as user enters search terms
FilteredList<String> filteredList = new FilteredList<>(items);
// Add listener to our ComboBox textfield to filter the list
comboBox.getEditor().textProperty().addListener((observable, oldValue, newValue) ->
filteredList.setPredicate(item -> {
// If the TextField is empty, return all items in the original list
if (newValue == null || newValue.isEmpty()) {
return true;
}
// Check if the search term is contained anywhere in our list
if (item.toLowerCase().contains(newValue.toLowerCase().trim())) {
return true;
}
// No matches found
return false;
}));
// Finally, let's add the filtered list to our ComboBox
comboBox.setItems(filteredList);
}
}
您将拥有一个简单的、可编辑的 ComboBox,它可以从列表中过滤掉不匹配的值。
使用这种方法,您不需要监听每个按键,但可以在其Predicate自身内提供任何过滤指令,如上所示。
结果:
编辑:
ComboBox但是,可编辑项存在一些问题需要解决,因为从列表中选择一个项目会引发IndexOutOfBoundsException.
这可以通过使用单独TextField的过滤器来缓解,但保持与上述基本相同的代码。而不是将侦听器添加到comboBox.getEditor(),只需将其更改为textField。这将毫无问题地过滤列表。
这是使用该方法的完整 MCVE:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
// Create a list of items
private final ObservableList<String> items = FXCollections.observableArrayList();
// Create the search field
TextField textField = new TextField("Filter ...");
// Create the ComboBox
private final ComboBox<String> comboBox = new ComboBox<>();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
// Simple Interface
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
// Add sample items to our list
items.addAll("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten");
createListener();
root.getChildren().addAll(textField, comboBox);
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Filtered ComboBox");
primaryStage.show();
}
private void createListener() {
// Create the listener to filter the list as user enters search terms
FilteredList<String> filteredList = new FilteredList<>(items);
// Add listener to our ComboBox textfield to filter the list
textField.textProperty().addListener((observable, oldValue, newValue) ->
filteredList.setPredicate(item -> {
// If the TextField is empty, return all items in the original list
if (newValue == null || newValue.isEmpty()) {
return true;
}
// Check if the search term is contained anywhere in our list
if (item.toLowerCase().contains(newValue.toLowerCase().trim())) {
return true;
}
// No matches found
return false;
}));
// Finally, let's add the filtered list to our ComboBox
comboBox.setItems(filteredList);
// Allow the ComboBox to extend in size
comboBox.setMaxWidth(Double.MAX_VALUE);
}
}
TA贡献1810条经验 获得超5个赞
我发现的最佳解决方案是稍微修改的版本:JavaFX searchable combobox (like js select2)
我修改的内容是使 InputFilter 类通用,并且组合框在关闭下拉列表后失去焦点。这里的代码:
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.transformation.FilteredList;
import javafx.scene.control.ComboBox;
public class InputFilter<T> implements ChangeListener<String> {
private ComboBox<T> box;
private FilteredList<T> items;
private boolean upperCase;
private int maxLength;
private String restriction;
private int count = 0;
/**
* @param box
* The combo box to whose textProperty this listener is
* added.
* @param items
* The {@link FilteredList} containing the items in the list.
*/
public InputFilter(ComboBox<T> box, FilteredList<T> items, boolean upperCase, int maxLength,
String restriction) {
this.box = box;
this.items = items;
this.upperCase = upperCase;
this.maxLength = maxLength;
this.restriction = restriction;
this.box.setItems(items);
this.box.showingProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue == false) {
items.setPredicate(null);
box.getParent().requestFocus();
}
}
});
}
public InputFilter(ComboBox<T> box, FilteredList<T> items, boolean upperCase, int maxLength) {
this(box, items, upperCase, maxLength, null);
}
public InputFilter(ComboBox<T> box, FilteredList<T> items, boolean upperCase) {
this(box, items, upperCase, -1, null);
}
public InputFilter(ComboBox<T> box, FilteredList<T> items) {
this(box, items, false);
}
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
StringProperty value = new SimpleStringProperty(newValue);
this.count++;
System.out.println(this.count);
System.out.println(oldValue);
System.out.println(newValue);
// If any item is selected we save that reference.
T selected = box.getSelectionModel().getSelectedItem() != null
? box.getSelectionModel().getSelectedItem() : null;
String selectedString = null;
// We save the String of the selected item.
if (selected != null) {
selectedString = this.box.getConverter().toString(selected);
}
if (upperCase) {
value.set(value.get().toUpperCase());
}
if (maxLength >= 0 && value.get().length() > maxLength) {
value.set(oldValue);
}
if (restriction != null) {
if (!value.get().matches(restriction + "*")) {
value.set(oldValue);
}
}
// If an item is selected and the value in the editor is the same
// as the selected item we don't filter the list.
if (selected != null && value.get().equals(selectedString)) {
// This will place the caret at the end of the string when
// something is selected.
System.out.println(value.get());
System.out.println(selectedString);
Platform.runLater(() -> box.getEditor().end());
} else {
items.setPredicate(item -> {
System.out.println("setPredicate");
System.out.println(value.get());
T itemString = item;
if (this.box.getConverter().toString(itemString).toUpperCase().contains(value.get().toUpperCase())) {
return true;
} else {
return false;
}
});
}
// If the popup isn't already showing we show it.
if (!box.isShowing()) {
// If the new value is empty we don't want to show the popup,
// since
// this will happen when the combo box gets manually reset.
if (!newValue.isEmpty() && box.isFocused()) {
box.show();
}
}
// If it is showing and there's only one item in the popup, which is
// an
// exact match to the text, we hide the dropdown.
else {
if (items.size() == 1) {
// We need to get the String differently depending on the
// nature
// of the object.
T item = items.get(0);
// To get the value we want to compare with the written
// value, we need to crop the value according to the current
// selectionCrop.
T comparableItem = item;
if (value.get().equals(comparableItem)) {
Platform.runLater(() -> box.hide());
}
}
}
box.getEditor().setText(value.get());
}
}
然后将 InputFilter 添加为 Combobox 的 textField 的更改侦听器:
comboBox.getEditor().textProperty().addListener(new InputFilter<YourCustomClass>(comboBox, new FilteredList<YourCustomClass>(comboBox.getItems())));
目前Combobox.setEditable(true)必须在外部手动完成,但我计划将其移入 InputFilter 本身。您还需要为 Combobox 设置一个字符串转换器。到目前为止,这个解决方案对我来说很糟糕,唯一缺少的是在输入搜索键时支持空格键。
添加回答
举报