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

JavaFX

单琛
2023-03-14

我有一个JavaFX应用程序,它可以与服务器通信并从服务器获取数据。接收到的数据被放入ObservableList并显示在TableView中。

与服务器的通信在它自己的线程中运行,当调用观测ist.add它会触发一个IllegalStateException(抱怨不是事件线程/不是JavaFX线程)

我已经找到了一个类似问题的以下解决方案,但我不确定如何采用该解决方案,因为在我的情况下,与服务器的通信需要不断中断,因此任务/线程一直在运行,直到通信终止。

我这里有一个最低限度的工作示例,它触发上述异常并大致模拟应用程序的工作方式。

主要:

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(root, 300, 275));
        primaryStage.show();
    }


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

控制器:

package sample;

import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;

public class Controller {
    public TableView<Integer> timeTable;
    public TableColumn<Integer, String> positionColumn;

    private ObservableList<Integer> testList;

    @FXML
    private void initialize() {
        testList = FXCollections.synchronizedObservableList(FXCollections.observableArrayList());
        positionColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().toString()));
        timeTable.setItems(testList);
        Task<Integer> integerTask = new Test(testList);
        Thread testThread = new Thread(integerTask);

        testThread.start();
    }
}

沟通任务:

package sample;

import javafx.collections.ObservableList;
import javafx.concurrent.Task;

public class Test extends Task<Integer> {
    private ObservableList<Integer> testlist;

    Test(ObservableList<Integer> list) {
        testlist = list;
    }

    @Override
    protected Integer call() throws Exception {
        // Emulates the server communication thread. Instead of an endless loop, I used a fixed number of iterations.
        // The real application has an endless while loop for server communication so a Task cannot be used to
        // get the data

        // getDataFromServer()
        // parseData()
        // putDataInList()
        // loop
        Thread.sleep(2000);
        for (int i = 0; i < 500; ++i) {
            testlist.add(i);
        }

        return 0;
    }
}

FXML:

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.GridPane?>
<GridPane fx:controller="sample.Controller"
          xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
    <TableView fx:id="timeTable" editable="true" prefHeight="498.0"
               prefWidth="254.0">
        <columns>
            <TableColumn fx:id="positionColumn" prefWidth="73.0" text="Position"/>
        </columns>
        <columnResizePolicy>
            <TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
        </columnResizePolicy>
    </TableView>
</GridPane>

共有2个答案

傅自明
2023-03-14

而不是更新可观察列表

申屠飞
2023-03-14

即使列表已同步,列表的修改仍会触发执行修改的线程上的事件。在您的情况下,这将导致在非应用程序线程上触发TableView更新。

此外,您不能简单地使用Task.updateValue,请参阅javadoc的以下部分(强调我的):

updateValue的调用被合并并稍后在FX应用程序线程[...]上运行,并且可以合并中间值以保存事件通知。

您需要自己同步它。以下类结合更新以补偿快速更新,这些更新可能会通过发布许多可运行文件来减慢应用程序线程的速度:

public abstract class JavaFXWorker<T> implements Runnable {

    private final List<T> results = new LinkedList<>();
    private final Object lock = new Object();
    private boolean updateWaiting = false;

    protected void publish(T... values) {
        synchronized (lock) {
            for (T v : values) {
                results.add(v);
            }

            // don't trigger additional updates, if last update didn't fetch the results yet
            // this reduces the number of Runables posted on the application thread
            if (!updateWaiting) {
                updateWaiting = true;
                Platform.runLater(this::update);
            }
        }
    }

    private void update() {
        List<T> chunks;
        synchronized(lock) {
            // copy results to new list and clear results
            chunks = new ArrayList(results);
            results.clear();
            updateWaiting = false;
        }
        // run ui updates
        process(chunks);
    }

    protected abstract void process(List<T> chunks);

}

您的代码可以使用上述类重写如下。

public class Test extends JavaFXWorker<Integer> {
    private final ObservableList<Integer> testlist;

    public Test(ObservableList<Integer> list) {
        testlist = list;
    }

    @Override
    public void run() {
        // Emulates the server communication thread. Instead of an endless loop, I used a fixed number of iterations.
        // The real application has an endless while loop for server communication so a Task cannot be used to
        // get the data

        // getDataFromServer()
        // parseData()
        // putDataInList()
        // loop
        Thread.sleep(2000);
        for (int i = 0; i < 500; ++i) {
            publish(i);
        }
    }

    @Override
    protected process(List<Integer> chunks) {
        testlist.addAll(chunks);
    }
}
testList = FXCollections.observableArrayList();

...

Thread testThread = new Thread(new Test(testList));

testThread.start();
 类似资料:
  • 问题内容: 是否可以在pom.xml中将JavaFX 2.0引用为Maven中的依赖项,以便一切顺利进行? 我在这个问题中看到可以在本地安装jfxrt.jar,但是理想情况下,我希望使用一种更简单的方法,在该方法中可以正确解决依赖关系并下载它,而无需任何本地黑客。 问题答案: 不,目前还没有这样的解决方案。我怀疑它会永远存在,因为从Java 1.7更新2开始,JavaFX与JVM一起安装了。从Ja

  • 问题内容: 我想在关闭JavaFX应用程序之前保存文件。 这就是我在中设置处理程序的方式: 当按下按钮时,控制器将调用: 如果关闭窗口,请单击窗口边框上的红色X(正常方式),然后会收到输出消息“ ”,这是所需的行为。 但是,在调用应用程序时,它会在不调用处理程序的情况下关闭(没有输出)。 如何使控制器使用添加到的处理程序? 问题答案: 如果你看一下在生命周期中的类: 每当启动应用程序时,JavaF

  • 问题内容: 假设我在TreeTableView中有2列,现在我想在第一列中添加一个字符串/标签,在另一列中添加一个ProgressBar。我将如何完成这样的事情? 非常感谢任何帮助! 问题答案: 正如James_D正确指出的,您可以将其用于带有ProgressBars的列。有内部支撑为如某些其他用户界面控件,等 对于其他UI控件,您可以创建一个Custom ,如下所示: 然后将a分配给第二列 在其

  • 问题内容: 我使用Java 8启动了一个Maven项目,并希望将其迁移到Java9。与此同时,我也从Eclipse Neon EE迁移到Eclipse Oxygen EE。(重新安装了JFX SDK)。尽管Eclipse工作区没有显示任何错误;一旦尝试编译并运行与Java 8中运行完全相同的程序,我将得到以下异常: 异常正在运行的应用程序application.Main 我现在已经多次重建了该项目

  • 问题内容: 在c ++或c编程语言中,我们知道要使用gotoxy(x,y)来更改坐标,并且可以使用循环和睡眠来更改坐标并制作动画。像这样; 但是我的问题是在JAVAFX 2.0编程中如何?我正在使用netbeans 7.2。感谢您的帮助。 问题答案: 看一下使用时间轴动画。它是JavaFX中动画的关键组成部分, 用于确定动画的关键部分何时以及以什么顺序出现。 这是一个例子

  • 问题内容: 我有一个具有简单场景(表单)的Java FX基本应用程序。我有一个Mysql Db,我正在使用Spring JPA(spring数据jpa,即存储库/实体)与之交互。 现在,由于我们知道javaFx有一些生命周期挂钩,即:init()start()和stop()。 假设我想使用JPA save() 方法在数据库中插入数据。通常,如果是我的控制器,则进行正常的数据库注入,例如: 本来可以

  • 问题内容: 因此,第一个和最后一个输入应该是字母,并且它们之间应该只有数字。这是我的代码: 问题答案: 您可以使用和 。 如果中的文本不符合这三个条件之一,则显示旧文本。 完整的应用 **更新:更改为。@kleopatra表示这是实现此目标的正确方法。

  • 问题内容: 我正在使用javafx构建桌面应用程序,我正在使用ftp下载约500 MB的文件。下载进行时,我需要在进度条上显示%。我还需要提供一个取消正在进行的下载过程的选项。 这是我下载文件的代码。 问题答案: 您应该使自己熟悉JavaFX中的并发性。 您可以在网上找到一些有关所需内容的示例,例如ProgressBar和Background Processes 。