繁体   English   中英

从不同的线程修改JavaFX Application场景?

[英]Modify JavaFX Application scene from different thread?

我已将gui添加到我的客户端应用程序中,并尝试将套接字消息转发给它。 到目前为止,我一直将客户端模拟为单例,因此可以从Application线程访问它,但是现在我希望多个客户端访问其gui。 我知道fxml绑定了属性侦听器,但我希望它尽可能简单。 直接与线程本身一起工作。 如果Application.launch()构造自己的实例,我将如何让Platform.runLater()引用场景实例? 我已经阅读了很多文章,但是到处都有解决方案来利用fxml接口跳过我不希望的工作。 控件类(简化,在第9行发布):

public class Client {
    private Socket socket = new Socket("127.0.0.1",4444);
    private BufferedReader incoming = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    private Browser browser = new Browser();
    public static void main(String[] arguments) throws IOException {
        Application.launch(browser.class);
        while(socket.isConnected()) {
            Platform.runLater(new Runnable(){
                @Override public void run(){
                    try{browser.bubble(incoming.readLine());}
                    catch(IOException fail){fail.printStackTrace();}
                }
            });
        }
    }
}

用户界面(简体):

public class Browser extends Application {
    private Stage stage;
    private Scene scene;
    private BorderPane layout = new BorderPane();
    private VBox history = new VBox();
    @Override public void start(Stage stage) throws Exception {
        layout.setCenter(history);
        scene = new Scene(layout);
        stage = stage;
        stage.setScene(scene);
        stage.show();
    }
    public void bubble(String message){
        VBox record = new VBox();
        Label label = new Label(message);
        record.getChildren().add(label);
        history.getChildren().add(record);
    }
}

您应该只拥有Application子类的一个实例,该实例是通过Application.launch()启动Application.launch()程序时创建的实例。 您在JavaFX Application中的main()方法实际上应该只调用Application.launch()而不能执行其他任何操作; 您应该将在start()过程中调用的start() (或init() )方法视为应用程序的入口点。

因此,您应该从start()方法创建Client实例,并将其设置为在后台线程中执行的操作。

您可以通过如下重构代码来实现所有这些:

public class Browser extends Application {
    private Stage stage;
    private Scene scene;
    private BorderPane layout = new BorderPane();
    private VBox history = new VBox();
    @Override public void start(Stage stage) throws Exception {
        layout.setCenter(history);
        scene = new Scene(layout);
        stage = stage;
        stage.setScene(scene);
        stage.show();

        Client client = new Client(this);
        Thread thread = new Thread(client::processIncomingData);
        thread.setDaemon(true);
        thread.start();
    }
    public void bubble(String message){
        VBox record = new VBox();
        Label label = new Label(message);
        record.getChildren().add(label);
        history.getChildren().add(record);
    }
    public static void main(String[] args) {
        Application.launch(args);
    }
}

public class Client {
    private Socket socket = new Socket("127.0.0.1",4444);
    private BufferedReader incoming = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    private Browser browser ;

    public Client(Browser browser) {
        this.browser = browser ;
    }

    public void processIncomingData()  {
        while(socket.isConnected()) {
            try {
                String data = incoming.readLine();
                Platform.runLater(() -> browser.bubble(data));
            } catch (IOException exc) {
                // TODO: handle properly
                exc.printStackTrace();
            }
        }
    }
}

还有两点需要注意: Application.launch()阻塞直到应用程序退出; 因此,在原始代码中while直到应用程序关闭,您的while循环才开始。 另外, readLine()方法也会阻塞,因此您不想在FX Application Thread上执行此操作(它将阻止UI以任何方式响应,直到读取一行为止)。 通过将readLine()Platform.runLater()块中移出,可以解决后一个问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM