Javafx:如何将组合框项动态绑定到从数据库更新自身的可观察列表



>我正在尝试将我的javafx comboBox项目绑定到可观察列表;当列表更新时,combobox项目也会更新(添加,删除或修改)。我尝试将侦听器添加到组合框项目,但仍然收到"不在 FX 应用程序线程上"异常。这是我的代码:

{
…
private ObservableList<String> programList = FXCollections.observableArrayList();
…
some code initialize programList from database
…
private ListProperty<String> programListProperty = new SimpleListProperty<>(programList);
…
some code update programList periodically
…
}

控制器

{
@FXML ComboBox programComboBox;

model.programListProperty().addListener((v, oldValue, newValue) -> 
Platform.runLater(() -> {
programComboBox.getItems().clear();
programComboBo.getItems().add(every item in model.programList);
}));
}

我也尝试过这种方式,但都不起作用

{
@FXML ComboBox programComboBox;
programComboBox.itemsproperty().bind(model.programListProperty());
}

注意:此解决方案对您的实现做出了一些假设,因为您没有提供代码的最小、完整和可验证示例。


您不需要为此使用绑定。ComboBox使用ObservableList来填充其项目。这里的关键字是可观察的。这意味着当基础List发生变化时,ComboBox将"看到"更改并自动更新其显示的项目。

使用setItems()方法初始化ComboBox,并向其传递ObservableList作为参数:

comboBox.setItems(programList);

从那里,每当programList更新(添加、删除项目等)时,ComboBox都会反映更改,而无需您提供任何进一步的代码。


查看以下完整示例:

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class ComboBoxListenerExample extends Application {
// This is our ObservableList that will hold our ComboBox items
private ObservableList<String> items = FXCollections.observableArrayList();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// Simple ComboBox
ComboBox<String> comboBox = new ComboBox<>();
// Let's "permanently" set our ComboBox items to the "items" ObservableList. This causes the
// ComboBox to "observe" the list for changes
comboBox.setItems(items);
// Create a button that will reload data from our "database"
Button button = new Button("Refresh Data");
// The items.setAll()` method replaces all items in the list with our new list of items
button.setOnAction(event -> items.setAll(reloadDatabase()));
root.getChildren().addAll(comboBox, button);
// Show the Stage
primaryStage.setWidth(300);
primaryStage.setHeight(300);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
// Sample method to simulate reloading the database information
private List<String> reloadDatabase() {
List<String> items = new ArrayList<>();
for (int i = 0; i < 5; i++) {
items.add(getRandomWord());
}
return items;
}
// Just a helper method specific for this example; it simply returns a random word. 
// This is used to simulate loading new data from the database
private String getRandomWord() {
List<String> words = Arrays.asList("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Red", "Blue", "Green", "Yellow", "Left", "Right", "Top", "Bottom");
Random random = new Random();
return words.get(random.nextInt(words.size()));
}
}

运行示例时,ComboBox为空。每次单击"刷新数据"按钮时,基础ObservableListitems都会使用新的随机项目列表进行更新;ComboBox会立即更改以反映这些更改。


不在 JavaFX 应用程序线程上:

现在,该错误消息呢?StackOverflow上有很多问题和答案已经解决了这个问题,但这里有一个简短的解释:

我假设您在后台线程上从数据库加载数据,这很好!但是,您无法从该线程对 UI 进行任何更改或更新。所有 UI 更新都需要在 JavaFX 应用程序线程上完成。

这很容易实现。在调用数据库方法时更新List时,请将该更新包装在Platform.runLater()调用中:

Platform.runLater(() -> programList.setAll(yourNewList));

这会安排在 JFX 应用程序线程上对列表的更新。问题解决了!

由于您尚未发布MCVE,因此我们只能为您提供很多帮助 这是您要做的一个例子 我尽力模仿您已经拥有的东西,您必须查看哪些内容对您不起作用并纠正它,但这是我所做的测试和为我工作的示例

public class Main extends Application {
@Override
public void start(Stage primaryStage) {
Model model = new Model();
ComboBox<String> comboBox = new ComboBox<>();
comboBox.itemsProperty().bind(model.getProgramListProperty());
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
vBox.getChildren().add(comboBox);
primaryStage.setScene(new Scene(vBox));
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
}

模型类

public class Model{
private ObservableList<String> programList = FXCollections.observableArrayList();
private ListProperty<String> programListProperty = new SimpleListProperty<>(programList);
Model(){
updateListFromDatabase();
continuousDatabaseUpdate();
}
ListProperty<String> getProgramListProperty() {
return programListProperty;
}
private void continuousDatabaseUpdate() {
new Timer().schedule( //Schedule a timer to fire a task at an interval
new TimerTask() {//Create a new TimerTask that runs a list update
@Override
public void run() {//What its running
//Important my update is wrapped in Platform.runLater so I DON'T block the main thread
Platform.runLater(() -> updateListFromDatabase());
}
},
0,//Delay 0
2/*Number of seconds*/ * 1000/*turn milliseconds into seconds*/);
}
private void updateListFromDatabase(){//Create a list of varying size and changing numbers
programList.clear();//Clear current list 
double size = Math.random() * 10 + 1;
for (int i = 0; i < size; i++) {
programList.add(String.valueOf(Math.random() * 20 + 1));//Add random values
}
}
}

这可能不是最佳解决方案,因为我们看不到您在MCVE中尝试做什么,您刚刚发布了一些代码,并期望其他人填写其余代码

此外,我不建议将侦听器添加到列表中,我会检查列表是否需要在您与数据库交谈时更新。以下是我所引用的内容

model.programListProperty().addListener((v, oldValue, newValue) -> 
Platform.runLater(() -> {
programComboBox.getItems().clear();
programComboBo.getItems().add(every item in model.programList);
}));

否则这就是数据流

检查数据库更改>更新列表>侦听器处理更新

CheckForDatabaseChanges->UpdateList(无需侦听器即可推送更新)

除非您的数据库在这种情况下更新时通知您,否则我对此表示怀疑,因为您说"某些代码更新程序定期列出"这就是为什么MCVE会有所帮助的原因

最新更新