Javafx:如何将ComboBox项目动态绑定到可观察列表,并从数据库更新自身

时间:2019-03-27 13:02:31

标签: java javafx

我正在尝试将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());

}

2 个答案:

答案 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为空。每次您单击“刷新数据”按钮时,基础的ObservableListitems都会使用新的随机项目列表进行更新; 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 会有所帮助的原因