Javafx: как я могу динамически привязывать элементы ComboBox к списку наблюдаемых, обновляемому из базы данных - PullRequest
0 голосов
/ 27 марта 2019

Я пытаюсь привязать элементы comboBox 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());

}

Ответы [ 2 ]

1 голос
/ 28 марта 2019

Примечание: Это решение делает некоторые предположения относительно вашей реализации, поскольку вы не предоставили Минимальный, Полный и Проверяемый пример вашего кода.


Вам не нужно использовать привязку для этого. 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 есть много вопросов и ответов, которые уже решают эту проблему, но вот краткое объяснение:

Я предполагаю, что вы загружаете данные из базы данных в фоновом потоке и , что хорошо! Однако вы не можете вносить какие-либо изменения или обновления в пользовательский интерфейс из этого потока. Все обновления пользовательского интерфейса должны выполняться в потоке приложений JavaFX.

Это очень просто сделать. Когда вы обновляете List при вызове метода базы данных, оберните это обновление в Platform.runLater() вызов:

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

Запланирует обновление списка в потоке приложений JFX. Проблема решена!

0 голосов
/ 27 марта 2019

Поскольку вы еще не опубликовали 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-> Слушатель обрабатывает обновление

VS

CheckForDatabaseChanges-> UpdateList (Попросите это обновить обновление без необходимости слушателя)

Если ваша база данных не уведомляет вас, когда она обновляется, в таком случае ваш смысл имеет смысл, я сомневаюсь в этом, потому что вы сказали "некоторый код обновления программы периодически", поэтому MCVE был бы полезен

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...