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

单击按钮时JavaFX在新线程上运行任务

柴衡
2023-03-14

我试图在JavaFX中单击按钮时检索XLS文件并将其加载到TableView中。我使用Task类和ExecutorService来启动新线程。我需要reader类是可重用的,但是FileChooser没有出现。

这是我尝试编写一些并发代码。我想知道我做错了什么,我将如何改进我的代码,因为一切都是事件驱动的?

控制器类代码

public void retrieveCustomersFromXLS() {
        try {
            loader.setOnSucceeded(workerStateEvent -> {
                File file = null;
                try {
                    file = loader.get();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                if (file != null && file.exists()) {
                    reader.setWorkingFile(file);
                    executor.submit(reader);
                }
            });
            reader.setOnSucceeded(workerStateEvent1 -> {
                Object[][] XLSFile = new Object[0][];
                try {
                    XLSFile = reader.get();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                if (XLSFile != null) {
                    tableInterface.setEntries(XLSFile);
                    tableInterface.setEntryType("customers");
                    executor.submit(tableInterface);
                }
            });
            tableInterface.setOnSucceeded(workerStateEvent2 -> {
                customerList = FXCollections.observableArrayList(tableInterface.getCustomerEntries());
                column_customerReference.setCellValueFactory(new PropertyValueFactory<customers, Integer>("customerReference"));
                column_customerName.setCellValueFactory(new PropertyValueFactory<customers, String>("customerName"));
                column_customerAddress.setCellValueFactory(new PropertyValueFactory<customers, String>("customerAddress"));
                column_customerPost.setCellValueFactory(new PropertyValueFactory<customers, Integer>("customerPost"));
                column_customerRegion.setCellValueFactory(new PropertyValueFactory<customers, String>("customerRegion"));
                column_customerID_DDV.setCellValueFactory(new PropertyValueFactory<customers, String>("customerDDV"));
                table_customerImports.setItems(customerList);
            });
            executor.submit(loader);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

阅读器类文件

@Override
    protected File call() throws Exception {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("Choose Excel file");
        fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Excel", "*.xlsx", "*.xls", "*.csv"));
        File selectedFile = fileChooser.showOpenDialog(new Stage());
        if (selectedFile != null) {
            path = selectedFile.getAbsolutePath();
            file = new File(path);
        }
        return file;
    }

共有1个答案

翟奕
2023-03-14

您必须在 JavaFX Application Thread 上调用 FileChooser#showXXXDialog 方法。如果您观察到任务失败,您将看到一个 IllegalStateException,其中包含一条消息,指出您尝试在错误的线程上执行操作。此外,你不需要后台任务来首先提示用户输入文件。

下面是一个提示用户输入文本文件的示例,在任务中读取文本文件,并将结果放入列表视图

应用程序.java

package com.example;

import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class App extends Application {

  @Override
  public void start(Stage primaryStage) throws IOException {
    Scene scene = new Scene(FXMLLoader.load(getClass().getResource("/App.fxml")));
    primaryStage.setScene(scene);
    primaryStage.setTitle("FileChooser Example");
    primaryStage.show();
  }

}

App.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.layout.VBox?>

<VBox xmlns="http://javafx.com/javafx/13" xmlns:fx="http://javafx.com/fxml/1" spacing="5" prefWidth="600"
      prefHeight="400" alignment="CENTER" fx:controller="com.example.Controller">
    <padding>
        <Insets topRightBottomLeft="5"/>
    </padding>
    <Button text="Open File..." onAction="#handleOpenFile"/>
    <Separator/>
    <ListView fx:id="listView" VBox.vgrow="ALWAYS"/>
</VBox>

Controller.java

package com.example;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javafx.collections.FXCollections;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.ListView;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;

public class Controller {

  private final Executor executor = Executors.newSingleThreadExecutor(r -> {
    Thread t = new Thread(r, "controller-thread");
    t.setDaemon(true);
    return t;
  });

  @FXML private ListView<String> listView;

  @FXML
  private void handleOpenFile(ActionEvent event) {
    event.consume();

    FileChooser chooser = new FileChooser();
    chooser.getExtensionFilters()
        .add(new ExtensionFilter("Text Files", "*.txt", "*.json", "*.xml", "*.html", "*.java"));

    File file = chooser.showOpenDialog(listView.getScene().getWindow());
    if (file != null) {
      ReadFileTask task = new ReadFileTask(file.toPath());
      task.setOnSucceeded(wse -> listView.setItems(FXCollections.observableList(task.getValue())));
      task.setOnFailed(wse -> task.getException().printStackTrace());
      executor.execute(task);
    }
  }

  private static class ReadFileTask extends Task<List<String>> {

    private final Path file;

    private ReadFileTask(Path file) {
      this.file = Objects.requireNonNull(file);
    }

    @Override
    protected List<String> call() throws Exception {
      return Files.readAllLines(file);
    }

  }

}
 类似资料:
  • 问题内容: 标题可能有点含糊,所以让我对其定义更好一些。我有一段有效的代码(在下面):我正在开发的游戏的简单主菜单。除“开始”按钮外,其他所有内容均正常运行。 我想要做的是单击“开始”按钮,并在同一阶段(窗口)中出现一个新场景。我不想看到一个新窗口打开。我已经与Java经验丰富的人进行了交谈,他们告诉我为MenuFX和GameFX创建单独的类。如果是这种情况,我需要从MenuFX类中调用GameF

  • 问题内容: 关于此事,我几乎遍历了每篇文章,但大多数都没有解释如何正确执行。问题: 我创建了一个javaFX应用程序,一个骰子游戏,人类玩家与计算机,但是在玩游戏的任何时候,人类玩家都应该能够单击“新游戏”按钮,并且应该从头开始重新启动游戏。 我尝试再次启动该阶段,但是在javafx中,我们不能两次调用launch方法。 1)有没有一种方法可以实现此功能而无需重新启动整个应用程序? 2)如果没有,

  • 问题内容: 我是Swing的新手。 我要在单击按钮(完成按钮)后更新表格。我认为数据正确,但屏幕无法正常工作。 以下是我程序的说明 选中复选框,然后单击完成按钮 最底层应更改。 没有主 这是我的代码: 问题答案: 而不是这样做… 只需更新现有模型 或简单地 假设您要继续向表中添加新行。如果您要这样做,还可以先清除表,然后再向其中添加新行。 Swing的工作原理是MVC(模型-视图- 控制器 ),该

  • 问题内容: 我在执行后台任务时使JavaFX UI保持活动状态时遇到问题。我已经设置了这个非常简单的代码- 我希望发生的事情是让进度条每1秒钟更新一次,直到任务完成。而是,UI完全冻结10秒钟,之后进度条显示为完成。明确地说- 问题不仅在于所有更新最终一次出现,而且UI直到那时都完全没有响应。 我已经阅读了有关此主题的其他任何问题,但找不到答案。我究竟做错了什么? 谢谢。 问题答案: 使用 sta

  • 我对javafx和场景构建器非常陌生。当单击按钮时,我试图创建一个新标签。以下是按钮创建标签的方法: 但它不起作用:(是否因为我没有在fxml中创建lbl1?是否可以在java代码中创建标签而不在fxml中创建?请帮助我!

  • 问题内容: 我正在使用此处找到的自定义表格模型。我已经使用该帖子中提供的建议更新了我的代码,并遇到了一个新问题。我对我的代码所做的改变是一个注入到我,以避免与线程的问题。完成此操作后,通过按按钮更新我的表的代码已停止工作。 用于初始化的代码如下: 我用来更新的代码如下: 我也尝试过使用,但这也不起作用。截至目前,更新的唯一方法是关闭并重新打开程序。具有我正在使用的,随着用户添加更多玩家,尺寸会增加