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