【问题标题】:child stage listeners do not get killed after closing stage关闭舞台后,儿童舞台听众不会被杀死
【发布时间】:2020-03-06 06:31:56
【问题描述】:

我有一个线程在我的子阶段内每 5 秒运行一次。当我打开和关闭子阶段时,该线程将继续运行。它应该在单击关闭按钮时删除所有孩子的舞台事件和内容(屏幕右上角的十字)。我正在子阶段内基于 TEXT 打印文本。

它打印为

非空

这意味着节点仍然存在于屏幕上。会影响应用 性能,因为我需要经常打开和关闭这些子阶段。 请指教,这是我的代码。

主类

public class dashboard extends Application {
@Override
public void start(Stage primaryStage) {

    BorderPane pane = new BorderPane();
    Button btn = new Button("Open child window");
    btn.setOnAction(new EventHandler<ActionEvent>() {
        public void handle(ActionEvent event) {                     
            final Stage dialog = new Stage();
            dialog.setTitle("Sensors Assignment");
            dialog.initModality(Modality.WINDOW_MODAL);
            dialog.initOwner(primaryStage);
            dialog.setResizable(false);

            VBox dialogVbox = (new childWindowClass()).GetChildContent();
            //dialogVbox.getChildren().add(closeButton);

            Scene dialogScene = new Scene(dialogVbox);
            dialog.setScene(dialogScene);
            dialog.showAndWait();

            dialog.setOnCloseRequest(e -> {System.out.println("Stage is closing");dialog.close();});        
        }
    });

    pane.setCenter(new VBox(new Text("Test 1234"), btn));

    ScrollPane scrollPane = new ScrollPane(pane);
    scrollPane.setFitToWidth(true);
    scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
    scrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);        

    Scene scene = new Scene(scrollPane);
    primaryStage.setTitle("test");       
    primaryStage.setMaximized(true);
    primaryStage.setScene(scene);
    primaryStage.show();
}

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

儿童班

public class childWindowClass {
    private Text txt; private int _index = 0; SimpleDateFormat dateFrmt = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    public VBox GetChildContent() {
        txt = new Text("Child Window Test " + _index);
        Label refreshClock = new Label(dateFrmt.format(new Date()));

        Thread timerThread = new Thread(() -> {             
            while (true) {
                try { Thread.sleep(5000);  }
                catch (InterruptedException e) {e.printStackTrace();}
                Platform.runLater(() -> {
                    refreshClock.setText(dateFrmt.format(new Date()));
                    try {

                        System.out.println(dateFrmt.format(new Date()) + "  -  " + (txt == null ? "" : " not") + " NULL");
                        if(txt != null)
                            txt.setText("Child Window Test " + _index);
                      _index++;
                    }
                    catch (Exception e) {e.printStackTrace();}                          
                });
            }
        });
        timerThread.start();
        return new VBox(txt, refreshClock);
    }
}

【问题讨论】:

  • 与您的问题无关:请学习 java 命名约定并遵守它们。

标签: java javafx parent-child


【解决方案1】:

为了在 JavaFX 中以固定间隔执行与 GUI 相关的代码,最好使用专用于此的 JavaFX API。例如,您可以在这里使用Timeline:参见,例如JavaFX periodic background task。您可以在需要停止时轻松拨打Timeline.stop()

杀死一个正在运行的线程有点微妙。您需要在 ChildWindowClass 中添加一个布尔标志,以及设置它的方法。然后,您可以通过调用该方法来请求线程停止。您需要注意在一个线程中对标志所做的更改会被另一个线程看到;在这种情况下,设置标志volatile 就足够了。以下应该有效:

public class ChildWindowClass {
    private Text txt; 
    private int _index = 0; 
    private SimpleDateFormat dateFrmt = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    private volatile boolean stopRequested ;

    public void requestStop() {
        stopRequested = true ;
    }

    public VBox getChildContent() {
        txt = new Text("Child Window Test " + _index);
        Label refreshClock = new Label(dateFrmt.format(new Date()));

        Thread timerThread = new Thread(() -> {  

            // update to stop thread when request is sent

            while (! stopRequested) {
                try { Thread.sleep(5000);  }
                catch (InterruptedException e) {e.printStackTrace();}
                Platform.runLater(() -> {
                    refreshClock.setText(dateFrmt.format(new Date()));
                    try {

                        System.out.println(dateFrmt.format(new Date()) + "  -  " + (txt == null ? "" : " not") + " NULL");
                        if(txt != null)
                            txt.setText("Child Window Test " + _index);
                      _index++;
                    }
                    catch (Exception e) {e.printStackTrace();}                          
                });
            }
        });
        timerThread.start();
        return new VBox(txt, refreshClock);
    }
}

现在您可以执行以下操作。请注意,我在这里更改了一些其他代码; showAndWait() 实际上会等到对话框关闭,所以之后的代码可以假设对话框已经关闭。

btn.setOnAction(new EventHandler<ActionEvent>() {
    public void handle(ActionEvent event) {                     
        final Stage dialog = new Stage();
        dialog.setTitle("Sensors Assignment");
        dialog.initModality(Modality.WINDOW_MODAL);
        dialog.initOwner(primaryStage);
        dialog.setResizable(false);

        ChildWindowClass dialogUI = new ChildWindowClass();
        VBox dialogVbox = dialogUI.getChildContent();
        //dialogVbox.getChildren().add(closeButton);

        Scene dialogScene = new Scene(dialogVbox);
        dialog.setScene(dialogScene);
        dialog.setOnCloseRequest(e -> {
            System.out.println("Stage is closing");
            dialog.close();
        });        
        dialog.showAndWait();
        dialogUI.requestStop();
    }
});

这不会中断线程,所以它会继续在当前的睡眠状态,但保证在对话框关闭后的五秒内退出。如果您需要尽快杀死它,您可以保留对它的引用并在调用requestStop() 时调用Thread.interrupt(),但这有点复杂。正如我一开始所说,最好使用Timeline 并完全避免使用线程来实现这种功能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-22
    • 2017-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-25
    相关资源
    最近更新 更多