我正在尝试将javafx 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());
}
答案 0 :(得分:1)
注意:由于您没有提供Minimal, Complete, and Verifiable Example代码,因此该解决方案对您的实现进行了一些假设。
您不需要为此使用绑定。 ComboBox
使用ObservableList
填充其项目。此处的关键字是可观察的。这意味着当基础List
发生更改时,ComboBox
将“看到”更改并自动更新其显示的项目。
您可以使用ComboBox
方法初始化setItems()
,并向其传递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
为空。每次您单击“刷新数据”按钮时,基础的ObservableList
,items
都会使用新的随机项目列表进行更新; ComboBox
的更改会立即反映出这些更改。
不在JavaFX应用程序线程上:
现在,该错误消息如何处理?关于StackOverflow的许多问题和答案已经解决了该问题,但这是一个简短的解释:
我假设您正在后台线程中从数据库加载数据,并且 这很好! 。但是,您不能从该线程对UI进行任何更改或更新。 。所有UI更新都需要在JavaFX Application Thread上完成。
这很容易完成。在调用数据库方法时更新List
时,请将该更新包装在Platform.runLater()
调用中:
Platform.runLater(() -> programList.setAll(yourNewList));
这将计划对列表的更新安排在JFX Application Thread上进行。问题解决了!
答案 1 :(得分:0)
由于您尚未发布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-> Listener处理更新
VS
CheckForDatabaseChanges-> UpdateList(无需侦听器就可以推送更新)
除非您的数据库在这种情况下更新时通知您,否则您的方式还是有意义的,我对此表示怀疑,因为您说“定期进行一些代码更新programList”,这就是为什么 MCVE 会有所帮助的原因>