我在控制器中有一个Spinner
:
@FXML
private Spinner<Integer> spnMySpinner;
和控制器中的SimpleIntegerProperty
:
private static final SimpleIntegerProperty myValue =
new SimpleIntegerProperty(3); //load a default value
我已将它们绑定在控制器的initialize
方法中:
spnMySpinner.getValueFactory().valueProperty().bindBidirectional(myValueProperty().asObject());
但绑定仅在控制器初始化第二次后才能正常工作。以下是我如何重现它:
myValue
属性中指定的默认值(数字3)。myValue
保持不变,编号为3。完整简约但可启动/可重现的代码:
Main.java:
package spinnerpoc;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage stage) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("MainWindow.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
MainWindow.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane fx:id="myRoot" id="AnchorPane" prefHeight="231.0" prefWidth="337.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.60" fx:controller="spinnerpoc.MainWindowController">
<children>
<Button fx:id="btnOpenSpinnerWindow" layoutX="102.0" layoutY="103.0" mnemonicParsing="false" text="Open SpinnerWindow" onAction="#onOpenSpinnerWindow"/>
</children>
</AnchorPane>
MainWindowController.java:
package spinnerpoc;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class MainWindowController implements Initializable {
@FXML
private Button btnOpenSpinnerWindow;
@FXML
private AnchorPane myRoot;
@Override
public void initialize(URL url, ResourceBundle rb) {
}
@FXML
private void onOpenSpinnerWindow(ActionEvent event) throws IOException{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("SpinnerWindow.fxml"));
Parent root = (Parent) fxmlLoader.load();
Stage stage = new Stage();
stage.initOwner(myRoot.getScene().getWindow());
stage.initModality(Modality.WINDOW_MODAL);
stage.setTitle("SpinnerWindow");
stage.setScene(new Scene(root));
stage.show();
}
}
SpinnerWindow.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.control.Spinner?>
<?import javafx.scene.control.SpinnerValueFactory.DoubleSpinnerValueFactory?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<ScrollPane xmlns:fx="http://javafx.com/fxml/1" fitToWidth="true" prefHeight="200.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/8.0.60" fx:controller="spinnerpoc.SpinnerWindowController">
<content>
<VBox maxWidth="1.7976931348623157E308">
<children>
<Spinner fx:id="spnMySpinner" editable="true" prefWidth="50.0" max="10" min="1" />
</children>
</VBox>
</content>
</ScrollPane>
SpinnerWindowController.java:
package spinnerpoc;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Spinner;
public class SpinnerWindowController implements Initializable {
private static final SimpleIntegerProperty myValue = new SimpleIntegerProperty(3);
public static SimpleIntegerProperty myValueProperty() {
return myValue;
}
public static Integer getMyValue() {
return myValue.getValue();
}
public static void setMyValue(int value) {
myValue.set(value);
}
@FXML
private Spinner<Integer> spnMySpinner;
@Override
public void initialize(URL url, ResourceBundle rb) {
spnMySpinner.getValueFactory().valueProperty().bindBidirectional(myValueProperty().asObject());
}
}
(代码也可在BitBucket repo处获得。)
我错过了什么?
答案 0 :(得分:3)
您正在遇到“过早的垃圾收集”问题。请参阅此here的说明。您可能会发现,并不总是每隔一段时间向旋转器显示它失败,但它只是零星的,并且行为会因机器而异。如果限制JVM可用的内存,您可能会发现它永远不会起作用。
当您致电IntegerProperty.asObject()
时,
创建与此
ObjectProperty
双向绑定的IntegerProperty
。
现在请注意,双向绑定有this feature to prevent accidental memory leaks:
JavaFX双向绑定实现使用弱侦听器。这意味着双向绑定不会阻止属性被垃圾回收。
因此,您显式创建的双向绑定不会阻止它被绑定的东西(ObjectProperty<Integer>
创建的asObject()
)被垃圾收集。由于您没有引用它,因此只要退出initialize()
中的SpinnerWindow Controller
方法,它就有资格进行垃圾回收。显然,一旦你的微调器值双向绑定的值被垃圾收集,绑定将不再起作用。
仅出于演示目的,您可以通过放入一个强制垃圾回收的钩子来看到这一点。例如。做
<ScrollPane onMouseClicked="#gc" xmlns:fx="http://javafx.com/fxml/1" ...>
在SpinnerWindow.fxml和
中@FXML
private void gc() {
System.out.println("Invoking GC");
System.gc();
}
SpinnerWindowController中的。如果执行此操作,则单击滚动窗格将强制进行垃圾回收,更改微调器值将不会更新属性。
要解决此问题,请保留对您从asObject()
获取的属性的引用:
public class SpinnerWindowController implements Initializable {
private static final SimpleIntegerProperty myValue = new SimpleIntegerProperty(3);
public static SimpleIntegerProperty myValueProperty() {
return myValue;
}
public static Integer getMyValue() {
return myValue.getValue();
}
public static void setMyValue(int value) {
myValue.set(value);
}
@FXML
private Spinner<Integer> spnMySpinner;
private ObjectProperty<Integer> spinnerValue = myValueProperty().asObject();
@Override
public void initialize(URL url, ResourceBundle rb) {
spnMySpinner.getValueFactory().valueProperty().bindBidirectional(spinnerValue);
}
}