问题的出现基本上是因为您在处理该列表的更改时最终更改了可观察列表(选择模型的selectedItems)。这是API中的一个缺陷,imo。一种解决方法(hack)是使用过滤列表,并在Platform.runLater(...) 中更新列表上的谓词,以便在再次更改之前让第一次更改完成:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class ComboBoxNoSelectedItem extends Application {
@Override
public void start(Stage primaryStage) {
ObservableList<String> allItems = FXCollections.observableArrayList("One", "Two", "Three", "Four", "Five");
ComboBox<String> combo = new ComboBox<>();
combo.setValue(allItems.get(0));
FilteredList<String> items = allItems.filtered(item -> item != combo.getValue());
combo.setItems(items);
combo.valueProperty().addListener((obs, oldValue, newValue) -> Platform.runLater(() -> items.setPredicate(item -> item != newValue)));
BorderPane root = new BorderPane();
root.setTop(combo);
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
也许更简洁的方法是禁用显示当前所选项目的单元格,这与 UX 很接近,但并不完全相同:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class ComboBoxNoSelectedItem extends Application {
@Override
public void start(Stage primaryStage) {
ObservableList<String> allItems = FXCollections.observableArrayList("One", "Two", "Three", "Four", "Five");
ComboBox<String> combo = new ComboBox<>(allItems);
combo.setCellFactory(lv -> new ListCell<String>() {
{
disableProperty().bind(combo.valueProperty().isEqualTo(itemProperty()));
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(item);
}
});
BorderPane root = new BorderPane();
root.setTop(combo);
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}