我有一個簡單的應用程序,顯示已從表格中的組合框中選擇的項目。但是,如果在組合框中選擇了項目,則會過濾其餘項目以包括名稱包含在所選項目中的項目。例如,在下面的MCVE中,如果要從組合框中選擇「Apple」,控制列表將被過濾爲包含「Apple」和「Pineapple」。JavaFX組合框選定的項目在過濾組合框列表後消失
偶爾,組合框將被重置爲在應用過濾器後不再顯示所選項目。 當您選擇在結果篩選列表中沒有任何其他項目的項目時,會發生此問題。例如,如果您從組合框中選擇「香蕉」或「菠蘿」,而不是顯示所選項目,則組合框將顯示提示文本。
請看下面MCVE
Main.java
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("ComboBox Issues");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller.java
package sample;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.util.StringConverter;
import java.util.function.Predicate;
public class Controller {
@FXML
private TableView<Fruit> fruityTable;
@FXML
private ComboBox<Fruit> fruitSelector;
private ObservableList<Fruit> selectedFruits;
private ObservableList<Predicate<Fruit>> filterCriteria;
private Predicate<Fruit> fruitFilter;
@FXML
private TableColumn<Fruit, String> fruitNameColumn;
@FXML
private TableColumn<Fruit, String> fruitColorColumn;
@FXML
void addSelectedFruit(ActionEvent event) {
if (fruitSelector.getValue() != null) {
Fruit selectedFruit = getSelectedFruitFromComboBox();
final String name = selectedFruit.getName().toLowerCase();
fruitFilter = selectableFruits -> selectableFruits.getName().toLowerCase().contains(name);
Platform.runLater(() -> filterCriteria.add(fruitFilter));
this.selectedFruits.add(selectedFruit);
event.consume();
}
}
private Fruit getSelectedFruitFromComboBox() {
return fruitSelector.getValue();
}
@FXML
void initialize() {
Fruit apple = new Fruit("Apple", "Red");
Fruit pineapple = new Fruit("Pineapple", "Brown");
Fruit banana = new Fruit("Banana", "Yellow");
ObservableList<Fruit> fruitSelectorItems = FXCollections.observableArrayList();
fruitSelectorItems.addAll(apple, pineapple, banana);
initializeFruitSelector(fruitSelectorItems);
initializeFruitTable();
}
private void initializeFruitSelector(ObservableList<Fruit> fruitSelectorItems) {
FilteredList<Fruit> filteredFruit = new FilteredList<>(fruitSelectorItems, x -> true);
fruitSelector.setItems(filteredFruit);
filterCriteria = FXCollections.observableArrayList();
filteredFruit.predicateProperty().bind(Bindings.createObjectBinding(() ->
filterCriteria.stream().reduce(x-> true, Predicate::and), filterCriteria));
fruitSelector.setConverter(createFruitChooserConverter());
}
private StringConverter<Fruit> createFruitChooserConverter() {
return new StringConverter<Fruit>() {
@Override
public String toString(Fruit item) {
if (item == null) {
return null;
} else {
return item.getName();
}
}
@Override
public Fruit fromString(String string) {
return null;
}
};
}
private void initializeFruitTable() {
selectedFruits = FXCollections.observableArrayList();
fruitNameColumn.setCellValueFactory(cellData -> formatFruitNameColumnText(cellData.getValue()));
fruitColorColumn.setCellValueFactory(cellData -> formatFruitColorColumnText(cellData.getValue()));
fruityTable.setItems(selectedFruits);
}
private ObservableValue<String> formatFruitColorColumnText(Fruit fruit) {
ReadOnlyStringWrapper color;
if (fruit == null) {
color = null;
} else {
color = new ReadOnlyStringWrapper(fruit.getColor());
}
return color;
}
private ObservableValue<String> formatFruitNameColumnText(Fruit fruit) {
ReadOnlyStringWrapper name;
if (fruit == null) {
name = null;
} else {
name = new ReadOnlyStringWrapper(fruit.getName());
}
return name;
}
}
Fruit.java
package sample;
public class Fruit {
private String name;
private String color;
Fruit(String name, String color){
this.name = name;
this.color = color;
}
public String getColor() {
return color;
}
public String getName() {
return name;
}
}
sample.fxm升
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane alignment="center" hgap="10" vgap="10" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<children>
<TableView fx:id="fruityTable" prefHeight="200.0" prefWidth="201.0" GridPane.columnIndex="1">
<columns>
<TableColumn fx:id="fruitNameColumn" prefWidth="75.0" text="Name" />
<TableColumn fx:id="fruitColorColumn" prefWidth="75.0" text="Color" />
</columns>
</TableView>
<ComboBox fx:id="fruitSelector" onAction="#addSelectedFruit" prefWidth="150.0" promptText="Choose a fruit" />
</children>
<columnConstraints>
<ColumnConstraints minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints />
</columnConstraints>
<rowConstraints>
<RowConstraints />
</rowConstraints>
</GridPane>
這似乎是與JavaFX的組合框的錯誤,但我還沒有看到任何一個有類似的問題(也許是因爲它是一個選擇後過濾相同的組合框?一種罕見的要求)還是我做錯事情?
編輯
由於James_D在評論中指出,這個問題是不存在的Java的更新版本(Java的8u131至少)。然而,我現在被迫使用Java 8u25。我關心這個問題的主要原因是因爲它允許用戶兩次選擇相同的項目。因此,防止用戶複製表中項目的解決方案對我來說是可以接受的。
您似乎在每次選擇某個東西時將新過濾器與現有過濾器組合在一起。 (爲什麼?這不是你描述的要求。)所以,如果你選擇「蘋果」,然後選擇「香蕉」,該列表將被過濾,只包含那些包含文本「蘋果」*和*包含文本「香蕉「,導致一個空的列表。 –
對,我在寫這個mcve時忽略了。實際應用程序應用一些額外的邏輯來確定何時過濾列表。它只會應用一次過濾器。我用來過濾列表的方法對我而言有點矯枉過正。在嘗試使用您建議的前兩種方法嘗試解決此問題後,我實際上是從您的答案中偷了它。 –
我只是試着運行它,它似乎按預期工作。 (因爲在選擇「Apple」之後,「Banana」被過濾了,所以你不能真正選擇「Apple」,然後選擇「Banana」。)我無法重現你描述的問題。 –