【问题标题】:JavaFX show AnchorPane from ThreadJavaFX 显示来自 Thread 的 AnchorPane
【发布时间】:2018-02-21 14:32:05
【问题描述】:

我正在尝试创建以下概念:每当启动特定屏幕时启动一个线程。该线程应该收到一条称为“标签”的消息,该消息尚未工作,因此我对其进行了硬编码。 然后根据标签的验证显示一个 AnchorPane:showError 或 showValid 函数。但是,应用程序首先运行该函数,然后显示 AnchorPane 和更新后的 ListView。

我想在特定屏幕启动时启动以下线程。

public class RFIDThread extends Thread{
       private static final Logger logger = Logger.getLogger(RFIDApplication.class);

        /**
        * The incoming data stream from the LLRP reader connection
        */
       private DataInputStream inStream = null;

       /**
        * The socket for the connection to the LLRP Reader
        */
       private Socket socket = null;

       /**
        * A queue to store incoming LLRP Messages
        */
       private LinkedBlockingQueue<LLRPMessage> queue = null;

       private String[] found_tags = new String[5];

       private JSONArray valid_tags;

       private TagsListController controller;

       /**
        * Thread for constant reading of the stream
        * 
         * @param socket
         * @param controller
         * @param tags
         * @param orderNumber
         * @throws java.io.IOException
        */
       public RFIDThread(Socket socket, TagsListController controller, JSONArray tags, String orderNumber) throws IOException {
            this.socket = socket;
            this.controller = controller;
            this.queue = new LinkedBlockingQueue<LLRPMessage>();

            try {
                this.inStream = new DataInputStream(socket.getInputStream());
            } catch (IOException e) {
                logger.error("Cannot get input stream", e);
            }

            valid_tags = tags;

            found_tags[0] = "aga9jrjahr";
            found_tags[1] = "agahs4suj";
            found_tags[2] = "a79gtvaTGBQG";
            found_tags[3] = "at3anit08av9agq4";
            //found_tags[4] = "4a05355d0000000000017cc0";

            //start();
       }

       @Override
       public void run() 
       {
           super.run();
            if (socket.isConnected()) {
                for (String found_tag : found_tags) {
                    Integer index = valid_tags.indexOf(found_tag);
                    if (index > 0) {
                        Platform.runLater(() -> {
                            controller.showValid(found_tag);
                        });
                    } else {
                        Platform.runLater(() -> {
                            controller.showError(found_tag);
                        });
                    }
                }
            }
       }
}

线程应该运行函数:showError 或 showValid 基于它接收到的标签。目前我设置了一些无效的硬编码标签,因此它应该运行 showError() 函数。此功能:将标签添加到ListView,将标签设置为标签的文本,显示AnchorPane,休眠1秒,隐藏AnchorPane,然后休眠1秒。在此之后,必须处理下一个标签。

/**
* Display red screen
* @param tag
*/
public void showError(String tag) {
    this.found_tags_list.getItems().add(tag);
    this.errorTag.setText(tag);
    System.out.println(errorTag.getText());
    this.errorPane.setVisible(true);
    pause(1000);
    this.validPane.setVisible(false);
    pause(1000);
}

【问题讨论】:

  • 如果 pause(...) 做了我认为的那样,你正在阻塞 FX 应用程序线程(因为 showError(...) 在该线程上执行)。这将阻止 UI 被渲染,直到整个 showError(...) 方法(包括它的暂停)完成。如果你想在暂停后做一些基于 UI 的事情,那么使用PauseTransition。不清楚第二次停顿的目的是什么,因为之后什么都没有发生。 (另外,你大概是指this.errorPane.setVisible(false);?)
  • 您也可以将暂停移动到后台线程并在控制器中使用hideError()hideValid() 方法,您可以从该线程通过Platform.runLater(...) 调用它们。
  • 方法在控制器中,这就是为什么我将控制器传递给线程以便它可以调用这些方法。
  • 这与我的评论有什么关系?
  • 当我回到我的电脑前。但是我很难理解为什么你不能只遵循简单的指令“将暂停移动到后台线程”。

标签: java multithreading javafx


【解决方案1】:

您没有发布您的 pause() 方法的代码,所以我将假设它执行类似 Thread.sleep(...) 的操作并适当地处理中断的异常。 IE。我假设你有类似的东西:

public void pause(int millis) {
    try {
        Thread.sleep(millis);
    } catch (InterruptedException exc) {
        Thread.currentThread().interrupt();
    }
}

showError() 方法正在(显式)在 FX 应用程序线程上执行。该线程还负责渲染 UI。因此,当showError() 方法正在执行时,UI 不能重绘(因为单个线程不能同时做两件事:这基本上是“线程”的定义)。

所以阻塞 FX 应用程序线程总是是一个错误,因为它会使 UI 无响应并阻止它被绘制。

如果您已经在 FX 应用程序线程上,并且想要安排一些代码在将来执行,您可以使用 PauseTransition 来实现。所以不是

this.errorPane.setVisible(true);
pause(1000);
this.validPane.setVisible(false);

你可以的

this.errorPane.setVisible(true);
PauseTransition pause = new PauseTransition(Duration.millis(1000));
pause.setOnFinished(e -> this.validPane.setVisible(false));
pause.play();

该方法中的第二个pause 意义不大。它只是暂停 FX 应用程序线程,然后该方法退出,所以无论如何它都没有等待。

如果这个想法是让后台线程暂停,你应该在后台线程上调用pause()。 (显然,在 FX 应用程序线程上调用它不会让后台线程暂停。)

所以我认为你的代码应该是这样的:

public class RFIDThread extends Thread {

    // ...

   @Override
   public void run() {
       super.run();
        if (socket.isConnected()) {
            for (String found_tag : found_tags) {
                Integer index = valid_tags.indexOf(found_tag);
                if (index > 0) {
                    Platform.runLater(() -> controller.showValid(found_tag));
                } else {
                    Platform.runLater(() -> controller.showError(found_tag));
                }

                pause(2000);
            }
        }
   }

}

请注意,我在这里猜测的目的是让您的后台线程暂停(大约)一秒您显示的窗格再次隐藏之后,这意味着它需要暂停两个总秒数。

在控制器中,你做

public void showError(String tag) {
    this.found_tags_list.getItems().add(tag);
    this.errorTag.setText(tag);
    System.out.println(errorTag.getText());
    this.errorPane.setVisible(true);
    PauseTransition pause = new PauseTransition(Duration.millis(1000));
    pause.setOnFinished(e -> this.validPane.setVisible(false));
    pause.play();
}

【讨论】:

    猜你喜欢
    • 2013-01-23
    • 1970-01-01
    • 2017-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多