在每次 window 出现后复制 属性 的 ChangeListener
Duplication of Property's ChangeListener after every window's appearance
假设我们有一个根 window 和 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
将 window 更改为新的。它的控制器,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();
}
}
}
在我向您展示 'New Window' 的视图和控制器之前,您必须知道应用程序有一个单例 class 和 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>
在 'New Window' 创建过程中,我在控制器的 initialize
方法中向 MySingleton
的 BooleanProperty
添加了一个侦听器。新的侦听器代码引用了非静态控制器的私有方法,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!");
}
}
现在,问题来了。假设第一步是去 'New Window':
在每个 "Back" -> "Go to New Window" 序列之后,当我单击 "Change BooleanProperty" 时,有 i+1
个 "Boolean property changed!" 的打印件,其中 i
是 [= 的数量69=] -> 'New Window' 转换(从 0
开始)。为什么不是只有一个打印?
我知道每次启动 'New Window' 时,应用程序都会向 BooleanProperty
添加新的侦听器,问题可能是由多个 属性 引起的听众。但是,如果在新的侦听器代码中我引用了在 window 转换后被销毁的对象的非静态方法,那怎么可能呢?
我认为 "Maybe controller is not destroyed? Maybe initialize
method works in a way I don't understand and controller's object is still there?" 所以正如您可能看到的那样,我添加了一个额外的变量 duplicateCounter
,它在 initialize
方法的末尾递增。但每次都是 1
,所以我假设创建了全新的 NewWindowController
对象。
如何防止 BooleanProperty
听众重复?
But how is it possible if in a new listener's code I refer to the non-static method of the object which is destroyed after window transition?
I thought "Maybe controller is not destroyed? Maybe initialize
method works in a way I don't understand and controller's object is still there?" So as you probably see, I added an extra variable, duplicateCounter
which increments at the end of the initialize
method. But every time it's 1
, so I assume the brand new NewWindowController
object is created.
确实,每次加载 fxml 时都会创建一个新控制器,但是旧控制器不是 "destroyed"(可用于垃圾回收),因为仍然有对该对象的引用:
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);
...
}
假设我们有一个根 window 和 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
将 window 更改为新的。它的控制器,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();
}
}
}
在我向您展示 'New Window' 的视图和控制器之前,您必须知道应用程序有一个单例 class 和 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>
在 'New Window' 创建过程中,我在控制器的 initialize
方法中向 MySingleton
的 BooleanProperty
添加了一个侦听器。新的侦听器代码引用了非静态控制器的私有方法,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!");
}
}
现在,问题来了。假设第一步是去 'New Window':
在每个 "Back" -> "Go to New Window" 序列之后,当我单击 "Change BooleanProperty" 时,有 i+1
个 "Boolean property changed!" 的打印件,其中 i
是 [= 的数量69=] -> 'New Window' 转换(从 0
开始)。为什么不是只有一个打印?
我知道每次启动 'New Window' 时,应用程序都会向 BooleanProperty
添加新的侦听器,问题可能是由多个 属性 引起的听众。但是,如果在新的侦听器代码中我引用了在 window 转换后被销毁的对象的非静态方法,那怎么可能呢?
我认为 "Maybe controller is not destroyed? Maybe initialize
method works in a way I don't understand and controller's object is still there?" 所以正如您可能看到的那样,我添加了一个额外的变量 duplicateCounter
,它在 initialize
方法的末尾递增。但每次都是 1
,所以我假设创建了全新的 NewWindowController
对象。
如何防止 BooleanProperty
听众重复?
But how is it possible if in a new listener's code I refer to the non-static method of the object which is destroyed after window transition?
I thought "Maybe controller is not destroyed? Maybe
initialize
method works in a way I don't understand and controller's object is still there?" So as you probably see, I added an extra variable,duplicateCounter
which increments at the end of theinitialize
method. But every time it's1
, so I assume the brand newNewWindowController
object is created.
确实,每次加载 fxml 时都会创建一个新控制器,但是旧控制器不是 "destroyed"(可用于垃圾回收),因为仍然有对该对象的引用:
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);
...
}