gpt4 book ai didi

javafx - 为什么我的 Spinner 在第一次初始化时不更新它的绑定(bind)属性?

转载 作者:行者123 更新时间:2023-12-01 12:26:08 24 4
gpt4 key购买 nike

我在 Controller 中有一个 Spinner:

@FXML
private Spinner<Integer> spnMySpinner;

和 Controller 中的 SimpleIntegerProperty:

private static final SimpleIntegerProperty myValue = 
new SimpleIntegerProperty(3); //load a default value

我在 Controller 的 initialize 方法中将它们绑定(bind)在一起:

spnMySpinner.getValueFactory().valueProperty().bindBidirectional(myValueProperty().asObject());

但是绑定(bind)只有在 Controller 第二次初始化后才能正常工作。以下是我如何重现它:

  1. 我打开带有关联 Controller 的舞台,它加载了在 myValue 属性中正确指定的默认值(数字 3)。
  2. 我单击微调器上的递增按钮使其变为 4。它更改了微调器的值属性中的值,但绑定(bind)属性 myValue 保持原样,数字为 3。
  3. 我关闭舞台/窗口。
  4. 我重新打开它,微调器的值再次为 3。
  5. 我再次增加它。现在绑定(bind)开始工作了,我在微调器和绑定(bind)属性中都有一个“4”。

整个简约但可启动/可重现的代码:

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

}

主窗口.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>

主窗口 Controller .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();
}

}

微调窗口.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>

微调窗口 Controller .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 获得。)

我错过了什么?

最佳答案

您遇到了“过早的垃圾回收”问题。查看此 here 的说明.您可能会发现,并非每次您向微调器显示它都会失败,而只是偶尔发生,并且行为会因一台机器而异。如果您限制 JVM 可用的内存,您可能会发现它永远无法工作。

当您调用 IntegerProperty.asObject() 时, 它

Creates an ObjectProperty that bidirectionally bound to this IntegerProperty.

现在请注意,双向绑定(bind)具有 this feature to prevent accidental memory leaks :

JavaFX bidirectional binding implementation use weak listeners. This means bidirectional binding does not prevent properties from being garbage collected.

因此,您显式创建的双向绑定(bind)不会阻止它所绑定(bind)的对象(ObjectProperty<Integer> 创建的 asObject())被垃圾回收。由于您不保留对它的引用,因此一旦您退出 initialize(),它就有资格进行垃圾回收。 SpinnerWindow Controller 中的方法.显然,一旦您的微调器值双向绑定(bind)到的值被垃圾回收,绑定(bind)将不再有效。

仅出于演示目的,您可以通过放置一个钩子(Hook)来强制进行垃圾回收来看到这一点。例如。做

<ScrollPane onMouseClicked="#gc" xmlns:fx="http://javafx.com/fxml/1" ...>

在 SpinnerWindow.fxml 和

@FXML
private void gc() {
System.out.println("Invoking GC");
System.gc();
}

在 SpinnerWindowController 中。如果这样做,则单击滚动 Pane 将强制进行垃圾回收,并且更改微调器值将不会更新属性。

要解决此问题,请保留对从 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);
}

}

关于javafx - 为什么我的 Spinner 在第一次初始化时不更新它的绑定(bind)属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39437360/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com