首页 > 解决方案 > Javafx:如何将 ComboBox 项动态绑定到从数据库更新自身的 observablelist

问题描述

我正在尝试将我的 javafx 组合框项目绑定到可观察列表;当列表更新时,组合框项目也会更新(添加、删除或修改)。我尝试将侦听器添加到组合框项目,但仍然得到“不在 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());

}

标签: javajavafx

解决方案


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


您不需要为此使用绑定。使用ComboBoxanObservableList来填充其项目。这里的关键字是observable。这意味着当底层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 应用程序线程上完成。

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

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

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


推荐阅读