【问题标题】:Update TextArea while executing processes在执行进程时更新 TextArea
【发布时间】:2016-07-14 19:27:33
【问题描述】:

我发现当我单击“无响应”等“开始”按钮时,我的 GUI 在 3-4 秒后开始冻结。当我继续点击应用程序时,它会被强制关闭。
现在我想阻止这种情况,但我不知道如何。据我所知,JavaFX 在单线程中运行,因此,要在方法执行时更新我的​​ TextArea,我需要在另一个线程中运行这些方法。
我希望有人可以帮助我。

我的项目是什么样子的?

我有一个 FXML、一个 Controller、一个 Handler、一个 Transformer ,还有一个 Writer 和一个 Reader 类(在 Handler 类中使用)。
当我单击绑定到控制器中的方法的按钮时,将创建一个 Handler 实例,并调用 Reader 读取文本文件,并转换为字符串列表(逐行)。
此外,线条正在被操纵。在此之后,Writer 用于创建一个新文件并将新操作的行写入该文件。
还允许用户引用多个文件。
我想要的是,每当读者开始阅读诸如“文件...正在阅读”之类的文件时,都会显示文本区域。
然后在 Transformer 开始动作时附加“文件...正在被操作”,然后
当新行写入新文件时,“文件...正在写入”。

这是一些代码..
控制器:

public class FXMLDocumentController implements Initializable {

@FXML
private TextArea console;

@FXML
private void handleButtonAction(ActionEvent event) {
    System.out.println("You clicked me!");
    Handler hand = new Handler();
    hand.handle(files);
}

@Override
public void initialize(URL url, ResourceBundle rb) {
    // TODO
}    

}

处理程序:

public class Handler {

public void handle(List<String> files) {
    for (String s : files) {
        List<String> ls = Reader.readFile(s);
        Writer.writeFile(Transformer.transform(ls));
    }
}

每当读取、操作或写入文件时,我应该如何更改我的代码以更新 TextArea?

信息:我知道类处理程序不会编译,因为我删除了包含文件路径字符串的列表“文件”的初始化。

如果我遗漏了相关信息,请随时询问。
提前谢谢你!

【问题讨论】:

标签: user-interface javafx concurrency updates fxml


【解决方案1】:

您应该在后台线程中执行handle() 方法:

@FXML
private void handleButtonAction(ActionEvent event) {
    System.out.println("You clicked me!");

    Thread thread = new Thread(() -> {
        Handler hand = new Handler();
        hand.handle(files);
    });

    // this line means the background thread will not prevent application exit:
    thread.setDaemon(true);

    thread.start();
}

如果您想使用当前状态更新文本区域,您需要使用 Platform.runLater() 将其安排回 FX 应用程序线程。可能最干净的方法是不要在 Handler 类中包含 Platform.runLater(),而是在 Handler 中定义一个字段用于“消费”状态消息:

public class Handler {

    private final Consumer<String> statusMessageProcessor ;

    public Handler(Consumer<String> statusMessageProcessor) {
        this.statusMessageProcessor = statusMessageProcessor ;
    }

    // default processor does nothing:
    public Handler() {
        this(s -> {});
    }

    public void handle(List<String> files) {
        for (String s : files) {

            // similarly for other status updates:
            statusMessageProcessor.accept("The file "+s+" is being read");

            List<String> ls = Reader.readFile(s);
            Writer.writeFile(Transformer.transform(ls));
        }
    }
}

然后

Handler hand = new Handler() ;

可以变成

Handler hand = new Handler(message -> 
    Platform.runLater(() -> console.appendText(message + "\n")));

在按钮处理方法中。

【讨论】:

  • 我更喜欢第二种方式,不是吗?我可以看到读取、操作和写入文件时的状态,而我在第一个变体中看不到。 s in Handler hand = new Handler(s -> Platform.runLater(() -> console.appendText(s+"\n")));是我的初始化列表吗?
  • 什么意思?这里只有一种解决方案。我只是把它分成两部分以便更容易理解:第一,如何在后台线程中运行处理程序,第二,如何提供更新。
  • s 在对 Handler 构造函数的调用中只是 lambda 的参数名称(因此它是传递给 statusMessageProcessor.accept(...) 的状态消息)。您可以随意调用它(显然,如果您更改它,也可以在 console.appendText(...) 中更改它的使用)。
  • 谢谢!我删除了“Handler hand = new Handler();”并复制“Handler hand = new Handler(s -> Platform.runLater(() -> console.appendText(s+"\n")));”在 Thread 声明之前,它现在工作正常!并发是一个非常复杂的话题,我永远不知道我应该使用什么实现(线程、任务、后台工作人员、消费者、服务......)。你能给我一个链接或建议吗?是否有特别支持线程实现的设计模式?编辑:对不起,可以投票给你,因为我错过了 5 个代表点..
  • 如果回答了问题,请将答案标记为正确(您不需要声誉)。如果后台任务计算出您想在 UI 中显示的结果(或者如果您想在完成时更改 UI),请使用 Task。您可以为此使用Task&lt;Void&gt;,但对于您所描述的用法,简单的可运行文件几乎没有什么好处。如果您希望能够多次重新启动任务,请使用Service
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-14
  • 1970-01-01
  • 1970-01-01
  • 2014-12-15
  • 2019-08-26
  • 1970-01-01
  • 2012-02-02
相关资源
最近更新 更多