слушатели детской сцены не будут убиты после закрытия этапа - PullRequest
2 голосов
/ 06 марта 2020

У меня есть поток, который запускается каждые 5 секунд на моей дочерней стадии. Когда я открываю и закрываю дочернюю сцену, этот поток продолжает работать. Он должен удалить все события и содержимое дочерней сцены при нажатии кнопки закрытия (крестик в правом верхнем углу экрана). Я печатаю текст на основе текста в дочерней стадии.

Он печатается как

NOT NULL

, что означает, что узел все еще присутствует на экране , Это повлияет на производительность приложения, так как мне нужно часто открывать и закрывать эти дочерние этапы. Пожалуйста, совет, вот мой код.

Основной класс :

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);
    }
}

1 Ответ

1 голос
/ 06 марта 2020

Для выполнения кода, связанного с GUI, через фиксированные промежутки времени в JavaFX, лучше использовать специально предназначенный для этого JavaFX API. Например, здесь вы можете использовать Timeline: см., Например, JavaFX periodi c фоновая задача . Вы можете легко позвонить 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();
    }
});

Это не прервет поток, поэтому он продолжит свое текущее состояние сна, но он гарантированно завершится в течение пяти секунд после закрытия диалога. Если вам нужно убить его как можно скорее, вы можете сохранить ссылку на него и вызвать Thread.interrupt(), когда вызывается requestStop(), но это несколько усложняется. Как я уже говорил в начале, лучше использовать Timeline и полностью избегать потоков для такого рода функций.

...