Дублирование списка изменений свойства после появления каждого окна

#java #javafx #changelistener

#java #javafx #список изменений

Вопрос:

Давайте предположим, что у нас есть корневое окно с fx:include :

 <?import javafx.scene.layout.VBox?>

<VBox fx:controller="sample.StartWindowController"
      xmlns:fx="http://javafx.com/fxml" alignment="center">
    <fx:include source="startwindow.fxml"/>
</VBox>
  

Код startwindow.fxml :

 <?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>

<VBox fx:id="mainPane" fx:controller="sample.StartWindowController"
      xmlns:fx="http://javafx.com/fxml" alignment="center">
    <Button text="Go to New Window" onAction="#goToNewWindow"/>
</VBox>
  

Щелчок Button изменяет окно на новое. Его контроллер, StartWindowController :

 package sample;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import java.io.IOException;

public class StartWindowController {

  @FXML
  VBox mainPane;

  @FXML
  private void goToNewWindow() {
    Pane parentPane = (Pane) mainPane.getParent();
    parentPane.getChildren().clear();
    try {
      parentPane.getChildren().add(FXMLLoader.load(getClass().getResource("newwindow.fxml")));
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}
  

Прежде чем я покажу вам представление и контроллер «Нового окна», вы должны знать, что приложение имеет одноэлементный класс с BooleanProperty полем. Код MySingleton :

 package sample;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;

public class MySingleton {

  private static MySingleton instance;
  private BooleanProperty booleanProperty;

  private MySingleton() {
    booleanProperty = new SimpleBooleanProperty(false);
  }

  public static MySingleton getInstance() {
    if (instance == null) {
      instance = new MySingleton();
    }
    return instance;
  }

  public boolean isBooleanProperty() {
    return booleanProperty.get();
  }

  public BooleanProperty booleanPropertyProperty() {
    return booleanProperty;
  }
}
  

Код newwindow.fxml :

 <?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>

<VBox fx:id="mainPane" fx:controller="sample.NewWindowController"
      xmlns:fx="http://javafx.com/fxml" alignment="center">
    <Button text="Change BooleanProperty" onAction="#changeBooleanProperty"/>
    <Button text="Back" onAction="#goBack"/>
</VBox>
  

Во время создания «Нового окна» я добавляю прослушиватель к MySingleton окнам BooleanProperty в initialize методе контроллера. Код нового прослушивателя ссылается на нестатический частный метод контроллера printMessage . NewWindowController Код:

 package sample;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import java.io.IOException;

public class NewWindowController {

  private MySingleton mySingleton;
  @FXML VBox mainPane;
  private int duplicateCounter = 1;

  @FXML private void initialize() {
    System.out.println("Initializing New Window's Controller.");
    System.out.println("Duplicate counter: "   duplicateCounter);
    mySingleton = MySingleton.getInstance();
    mySingleton.booleanPropertyProperty().addListener(
            (observable, oldValue, newValue) -> printMessage());
    duplicateCounter  ;
  }

  @FXML private void changeBooleanProperty() {
    mySingleton.booleanPropertyProperty().setValue(!mySingleton.booleanPropertyProperty().getValue());
  }

  @FXML private void goBack() {
    Pane parentPane = (Pane) mainPane.getParent();
    parentPane.getChildren().clear();
    try {
      parentPane.getChildren().add(FXMLLoader.load(getClass().getResource("startwindow.fxml")));
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  private void printMessage() {
    System.out.println("Boolean property changed!");
  }
}
  

Теперь проблема. Предполагая, что первым шагом является переход к «Новому окну»:
После каждой последовательности «Назад» -> «Перейти к новому окну», когда я нажимаю «Изменить BooleanProperty», появляются i 1 отпечатки «Изменено логическое свойство!», где i — количество переходов «Стартовое окно» -> «Новое окно» (начиная с 0 ). Почему нет только одной печати?

Я осознаю тот факт, что каждый раз, когда я запускаю «Новое окно», приложение добавляет нового слушателя к BooleanProperty и проблема, вероятно, вызвана несколькими слушателями свойства. Но как это возможно, если в коде нового прослушивателя я ссылаюсь на нестатический метод объекта, который уничтожается после перехода в окно?

Я подумал: «Может быть, контроллер не уничтожен? Может быть, initialize метод работает непонятным мне образом, и объект контроллера все еще там?» Итак, как вы, вероятно, видите, я добавил дополнительную переменную, duplicateCounter которая увеличивается в конце initialize метода. Но каждый раз, когда это происходит, 1 поэтому я предполагаю, что создается совершенно новый NewWindowController объект.

Как я могу предотвратить BooleanProperty дублирование слушателей?

Ответ №1:

Но как это возможно, если в коде нового прослушивателя я ссылаюсь на нестатический метод объекта, который уничтожается после перехода в окно?

Я подумал: «Может быть, контроллер не уничтожен? Может быть, initialize метод работает непонятным мне образом, и объект контроллера все еще там?» Итак, как вы, вероятно, видите, я добавил дополнительную переменную, duplicateCounter которая увеличивается в конце initialize метода. Но каждый раз, когда это происходит, 1 поэтому я предполагаю, что создается совершенно новый NewWindowController объект.

Действительно, новый контроллер создается каждый раз при загрузке fxml, однако старый не «уничтожается» (доступен для сборки мусора), поскольку ссылка на объект все еще существует:

MySingleton сохраняет экземпляр в статическом элементе, делая его недоступным для сборки мусора. Этот экземпляр содержит ссылку на BooleanProperty , которая содержит ссылку на прослушиватель, который содержит ссылку на NewWindowController .

Чтобы напечатать сообщение только один раз, вы должны отменить регистрацию прослушивателя:

 private final ChangeListener<Boolean> listener = (observable, oldValue, newValue) -> printMessage();

@FXML private void initialize() {
    ...
    mySingleton.booleanPropertyProperty().addListener(listener);
    ...
}

...

@FXML private void goBack() {
    // remove listener
    MySingleton.getInstance().booleanPropertyProperty().removeListener(listener);        
    ...
}