Java 在不更改ValueProperty的情况下更改ComboBox的项
编辑: 我正在尝试构建一个带有搜索功能的组合框,这就是我想到的:Java 在不更改ValueProperty的情况下更改ComboBox的项,java,javafx,combobox,listener,Java,Javafx,Combobox,Listener,编辑: 我正在尝试构建一个带有搜索功能的组合框,这就是我想到的: public class SearchableComboBox<T> extends ComboBox<T> { private ObservableList<T> filteredItems; private ObservableList<T> originalItems; private T selectedItem; private StringProperty filter
public class SearchableComboBox<T> extends ComboBox<T> {
private ObservableList<T> filteredItems;
private ObservableList<T> originalItems;
private T selectedItem;
private StringProperty filter = new SimpleStringProperty("");
public SearchableComboBox () {
this.setTooltip(new Tooltip());
this.setOnKeyPressed(this::handleOnKeyPressed);
this.getTooltip().textProperty().bind(filter);
this.showingProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
// If user "closes" the ComboBox dropdown list: reset filter, reset list to the full original list, hide tooltip
if (newValue == false) {
filter.setValue("");;
setItems(originalItems);
getTooltip().hide();
// If user opens the combobox dropdown list: get a copy of the items and show tooltip
} else {
originalItems = getItems();
Window stage = getScene().getWindow();
getTooltip().show(stage);
}
}
});
}
public void handleOnKeyPressed(KeyEvent e) {
//Only execute if the dropdown list of the combobox is opened
if (this.showingProperty().getValue() == true) {
// Get key and add it to the filter string
String c = e.getText();
filter.setValue(filter.getValue() + c);
//Filter out objects that dont contain the filter
this.filteredItems = this.originalItems.filtered(a -> this.getConverter().toString(a).toLowerCase().contains(filter.getValue().toLowerCase()));
//Set the items of the combox to the filtered list
this.setItems(filteredItems);
}
}
公共类SearchableComboBox扩展了ComboBox{
私人可观察过滤器;
私人观察主义原创;
私人T-selectedItem;
私有StringProperty筛选器=新的SimpleStringProperty(“”);
公共可搜索组合框(){
this.setTooltip(新工具提示());
this.setOnKeyPressed(this::handleOnKeyPressed);
this.getTooltip().textProperty().bind(过滤器);
this.showingProperty().addListener(新的ChangeListener()){
@凌驾
public void changed(observevalue我建议从原始列表创建一个FilteredList
。然后,使用谓词
过滤掉不匹配的结果。如果您将组合框
项目设置为该过滤列表,它将始终显示所有项目或与搜索条件匹配的项目
只有当用户按[enter]键“提交”更改时,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);
}
}
我的建议是从原始列表中创建一个FilteredList
。然后,使用谓词
过滤掉不匹配的结果。如果您将组合框
项目设置为该过滤列表,它将始终显示所有项目或与搜索条件匹配的项目
只有当用户按[enter]键“提交”更改时,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);
}
}
我找到的最佳解决方案是对以下版本稍加修改:
我修改的内容是使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());
}
导入javafx.application.Platform;
导入javafx.beans.property.SimpleStringProperty;
导入javafx.beans.property.StringProperty;
导入javafx.beans.value.ChangeListener;
导入javafx.beans.value.observeValue;
导入javafx.collections.transformation.FilteredList;
导入javafx.scene.control.ComboBox;
公共类InputFilter实现ChangeListener{
私人组合框;
私人筛选列表项目;
私有布尔大写;
私有整数最大长度;
私有字符串限制;
私有整数计数=0;
/**
*@param-box
*此侦听器指向其textProperty的组合框
*补充说。
*@param项目
*包含列表中项目的{@link FilteredList}。
*/
公共InputFilter(组合框、FilteredList项、布尔大写、int maxLength、,
字符串限制){
this.box=box;
这个项目=项目;
this.upperCase=大写;
this.maxLength=maxLength;
这个限制=限制;
此.box.setItems(项目);
this.box.showingProperty().addListener(新的ChangeListener()){
@凌驾
public void changed(observevalue我发现的最佳解决方案是对以下内容稍加修改:
我修改的内容是使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());
}
导入javafx.application.Platform;
导入javafx.beans.property.SimpleStringProperty;
导入javafx.beans.property.StringProperty;
导入javafx.beans.value.ChangeListener;
导入javafx.beans.value.observeValue;
导入javafx.collections.transformation.FilteredList;
导入javafx.scene.control.ComboBox;
公共类InputFilter实现ChangeListener{
私人组合框;
私人筛选列表项目;
私有布尔大写;
私有整数最大长度;
私有字符串限制;
私有整数计数=0;
/**
*@param-box
*此侦听器指向其textProperty的组合框
*补充说。
*@param项目
*包含列表中项目的{@link FilteredList}。
*/
公共InputFilter(组合框、FilteredList项、布尔大写、int maxLength、,
字符串限制){
this.box=box;
这个项目=项目;
this.upperCase=大写;
this.maxLength=maxLength;
这个限制=限制;
此.box.setItems(项目);
this.box.showingProperty().addListener(新的ChangeListener()){
@凌驾
public void已更改(observevable值我在家时一定会这样做。但到目前为止,可编辑性对我来说是一个相当大的问题。每次我设置可编辑性(true)我的StringConverter中出现了一个nullpointerexception。我的解决方案和您的解决方案之间唯一的真正区别是您使用的是editable而不是Im,那么为什么在您的案例中以及在我的代码中,该值都没有改变?(请注意,即使对setValue部分进行了注释,该值在我的案例中仍然会改变)老实说,我不完全了解你的代码在做什么,如果没有完整的代码供我自己测试,我就无法真正复制你的问题。但请尝试一下我的解决方案,看看它是否满足你的需要;否则,我恐怕我不完全确定你的目标是什么。你真的不需要完整的代码,你可以粘贴我的SearchableCombo因为这是一个独立的类(虽然由于缺少导入而需要进行一些单击,并且addListener一开始可能无法被ide识别)。我尝试了your代码,但有一件事我注意到,当选择高于“1”的任何内容时,通常会出现IndexOutOfBoundsException。此外,在选择一个项目后,您必须手动从文本框中删除该项目,因为它是用于筛选的新谓词。正如我所说,到目前为止,我无法获得setEditable(true)工作,但在我的应用程序中,我不是使用简单的字符串,而是使用自定义类,仍然不知道为什么它不工作。我会编辑这个问题,希望我能用这种方式说得更清楚一点。谢谢你的努力,但现在请你这么说
comboBox.getEditor().textProperty().addListener(new InputFilter<YourCustomClass>(comboBox, new FilteredList<YourCustomClass>(comboBox.getItems())));