当前位置: 首页 > 知识库问答 >
问题:

需要澄清在javafx应用程序线程中更改数据

廉实
2023-03-14

我一直在将我的一个项目迁移到JavaFX,并开始遇到线程问题。我将附上一个简短的示例。经过多次搜索,我设法解决了问题。我无法在 fx 应用程序线程之外更改 tableView 数据。我将我的代码从使用摇摆工人切换到任务。

起初,这一直有效,直到我向表的可观察列表添加了一个更改侦听器。然后我收到错误“不在FX应用程序线程上;”

当我尝试更新标签的值时,在onChanged方法中发生了错误。我通过将它包装在Platform.runLater()中来解决这个问题。

我只是很困惑,为什么更改标签会说它不在应用程序线程上。这是在哪个线程上运行的?另外,我是否通过使用任务正确地向表中添加了行?在我的实际应用程序中,我可能会添加50k行,因此为什么要使用单独的线程,以免锁定用户界面。

public class Temp extends Application{  
    private ObservableList<String> libraryList = FXCollections.observableArrayList();

    public void start(Stage stage) {

        Label statusLabel = new Label("stuff goes here");

        TableView<String> table = new TableView<String>(libraryList);
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);

        TableColumn<String, String> col = new TableColumn<String, String>("Stuff");
        col.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue()));
        table.getColumns().add(col);

        libraryList.addListener(new ListChangeListener<String>() {
            public void onChanged(Change change) {
                // Problem was caused by setting the label's text (prior to adding the runLater)
                Platform.runLater(()->{
                    statusLabel.setText(libraryList.size()+" entries");
                });                 
            }               
        });

        // dummy stuff
        libraryList.add("foo");
        libraryList.add("bar");

        Button b = new Button("Press Me");
        b.setOnAction(new EventHandler<ActionEvent>() {
            public void handle(ActionEvent e) {
                FileTask task = new FileTask();
                new Thread(task).start();
            }
        });

        BorderPane mainBody = new BorderPane();

        mainBody.setTop(statusLabel);
        mainBody.setCenter(table);
        mainBody.setBottom(b);
        Scene scene = new Scene(mainBody);
        stage.setScene(scene);
        stage.show();       
    }


    class FileTask extends Task<Boolean>{

        public FileTask(){

        }

        protected Boolean call() throws Exception{

            Random rand = new Random();
            for(int i = 0; i < 5; i++) {
                String s = ""+rand.nextInt(Integer.MAX_VALUE);
                libraryList.add(s);
            }

            return true;
        }   
    }     

    public static void main(String[] args) {
        Application.launch(args);

    }
}

共有2个答案

杨豪
2023-03-14

考虑将视图所需的信息封装在一个单独的类中(通常称为模型)。视图应该通过监听器或绑定来响应模型中的变化。< br >您可以使用一个或多个线程来更新模型:

import java.util.Random;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class Temp extends Application{

    @Override
    public void start(Stage stage) {

        Model model = new Model();

        Label statusLabel = new Label("stuff goes here");

        TableView<String> table = new TableView<>(model.getLibraryList());
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);

        TableColumn<String, String> col = new TableColumn<>("Stuff");
        col.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue()));
        table.getColumns().add(col);
        statusLabel.textProperty().bind(Bindings.concat(model.sizeProperty.asString(), " entries"));

        // dummy stuff
        model.add("foo");  model.add("bar");

        Button b = new Button("Press Me");
        b.setOnAction(e -> {
            FileTask task = new FileTask(model);
            new Thread(task).start();
        });

        BorderPane mainBody = new BorderPane();

        mainBody.setTop(statusLabel);
        mainBody.setCenter(table);
        mainBody.setBottom(b);
        Scene scene = new Scene(mainBody);
        stage.setScene(scene);
        stage.show();
    }

    class Model {

        private final ObservableList<String> libraryList;
        private final IntegerProperty sizeProperty;

        Model(){
            libraryList = FXCollections.observableArrayList();
            sizeProperty = new SimpleIntegerProperty(0);
            libraryList.addListener((ListChangeListener<String>) change -> {
                Platform.runLater(()->sizeProperty.set(libraryList.size()));
            });
        }

        //synchronize if you want to use multithread
        void add(String string) {
            Platform.runLater(()->sizeProperty.set(libraryList.add(string)));
        }

        ObservableList<String> getLibraryList() {
            return libraryList;
        }

        IntegerProperty getSizeProperty() {
            return sizeProperty;
        }
    }

    class FileTask implements Runnable{

        private final  Model model;

        public FileTask(Model model){
            this.model = model;
        }

        @Override
        public void run() {
            Random rand = new Random();
            for(int i = 0; i < 5; i++) {
                String s = ""+rand.nextInt(Integer.MAX_VALUE);
                model.add(s);
            }
        }
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}
萧光华
2023-03-14

它按预期工作,您有应用程序线程和任务线程,它们看起来像这样:

App ------\ ----------------------
Task       \-label.setText() Exception

除了应用程序线程之外,您不能在任何其他东西上执行任何UI工作,因此添加RunLater可以做到以下几点:

App ----\ -------------/ RunLater(label.setText()) ----------
Task     \-add to list/

这很好。有几种方法可以根据您要执行的操作来管理它:

  • 如果要更新任务中的表列表,可以将 RunLater 调用移动到任务内部,而不是处理程序内部,这样它仍会将你返回到应用线程。这样,如果你实际上在应用线程上,则无需在处理程序中调用 RunLater。
App ---\ -----------------------/ label.setText() ----------
Task    \-RunLater(add to list)/
  • 另一种选择是只使用Task
App -----\ ------------------------------/ label.setText() ---/ add to table list-------
Task      \-build list, update progress /- return final list /

希望格式保持不变。

 类似资料:
  • 我们有申报单 其中backlog被标识为传入连接的最大队列长度。如果连接指示在队列已满时到达,则会拒绝连接。 假设积压设置为10。 这是否意味着服务器套接字将接受不超过10个客户端? 然后: 只有当时,积压数才会下降。这是正确的吗? 有没有办法知道一个套接字当前正在处理多少个打开的连接(换句话说,它离放弃并开始拒绝新连接有多近)

  • 当我创建一个简单的非多线程JavaFX应用程序并启动它时,该应用程序会创建一些线程(JavaFXApplicationThread、JavaFXLauncher等)。这些线程中的大多数都已命名,但在我的所有JavaFX应用程序中都有一个未命名的线程(“线程-1”或“线程-2”)。我绝对不会创建自己的线程,因为我尝试启动Hello World JavaFX应用程序(由IDEA生成),其中也包含“线程

  • 在过去的几天里,我一直在尝试JavaFX、FXML、任务和属性。我偶然发现了一种奇怪的行为,希望您能帮助我更好地理解正在发生的事情。 我有一个极简的GUI,看起来像这样:GUI 如果我单击按钮,就会创建并启动一个新任务。此任务会增加一个双属性,并且新值会写入标签并在ProgressBar中设置。任务代码可以在这里看到: FXML控制器的代码如下: 如果我运行此代码,尝试更新标签会导致以下异常:ja

  • 我认为用A*算法应该是SAEFG,但答案是SBEFG。现在我的教授是一个无所事事的人。有人能解释为什么是SBEFG吗?

  • 来自 https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-blob#poison 当 Blob 触发器函数失败时... “失败”是什么意思?编译失败?引发未处理的异常? 如果有一些不起作用的示例代码,那就太好了。所有示例代码都显示了工作状态,因此无法演示 Azure 函数中的故障模式。

  • 问题内容: 我正在使用FXML中描述的核心组件制作JavaFX桌面应用程序,我想为用户提供更改语言的选项。但是,一旦从FXML加载了组件,我还没有找到任何直接的方法来更改语言。 问题是,有什么标准方法可以处理JavaFX中的语言切换。 问题答案: 你可以做这样的事情。正如您在回答中一样,您可能希望将其实现为单例,或者使用DI框架在需要的地方注入单个实例: 现在,您可以执行以下操作: 而且任何时候您