为什么Spinner第一次初始化时不更新其bound属性? [英] Why doesn't my Spinner update its bound property the first time it is initialized?

查看:69
本文介绍了为什么Spinner第一次初始化时不更新其bound属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在控制器中有一个 Spinner

@FXML
private Spinner<Integer> spnMySpinner;

以及控制器中的 SimpleIntegerProperty

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

我已将它们绑定到控制器的 initialize 方法:

I have bound them together in controller's initialize method:

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

但是绑定只有在控制器第二次初始化后才能正常工作。复制方法如下:

But the bindings work correctly only after second time the controller initializes. Here's how I can reproduce it:


  1. 我用关联的控制器打开舞台,它加载默认值,该默认值在<$ c中指定$ c> myValue 属性正确(数字3)。

  2. 我单击微调器上的增量按钮使其变为4。它会更改旋转器的value属性,但绑定属性 myValue 保留数字3。

  3. 我关闭了舞台/窗口。

  4. 我重新打开它,微调器再次具有3的值。

  5. 我再次增加它。现在,Boom可以绑定了,并且在微调框和绑定的属性中都有 4。

  1. I open the stage with the associated controller, it loads the default value, specified in the myValue property correctly (a number 3).
  2. I click the increment button on the spinner to make it a 4. It changes the value in the spinner's value property, but the bound property myValue is left intact with the number 3.
  3. I close the stage/window.
  4. I reopen it, the spinner has again a value of 3.
  5. I increment it again. Boom now the binding works and I have a "4" both inside the spinner and the bound property.

整个简约,但是可以启动/可复制的代码:

Entire minimalistic, but launchable/reproducible code:

Main.java:

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:

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:

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:

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:

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());
    }

}

(代码也可以在< a href = https://bitbucket.org/Leprechaunius/spinnerpoc/src/446228851e5dad6efaf64e065d89099b44416158/src/spinnerpoc/?at=master rel = nofollow> BitBucket存储库。)

我缺少什么?

推荐答案

您正在遇到过早的垃圾收集问题。请在此处。您可能会发现,并非每隔一次您向微调器显示失败,而是偶发性的,并且行为因一台机器而异。如果限制了JVM可用的内存,您可能会发现它永远无法工作。

You are running into the "premature garbage collection" problem. See a description of this here. You will likely find that it's not always every other time you show the spinner that it fails, but is just sporadic, and that the behavior will vary from one machine to another. If you limit the memory available to the JVM, you might find that it never works.

调用 IntegerProperty.asObject()


创建一个 ObjectProperty 并双向绑定到该 IntegerProperty

现在请注意,双向绑定具有此功能可防止意外的内存泄漏

Now note that a bidirectional binding has this feature to prevent accidental memory leaks:


JavaFX双向绑定实现使用弱侦听器。这意味着双向绑定不会阻止垃圾回收属性。

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

因此,您显式创建的双向绑定不会阻止它是绑定到( asObject()创建的 ObjectProperty< Integer> )进行垃圾回收。由于您没有对其进行任何引用,因此退出 SpinnerWindow Controller initialize()方法后,它就有资格进行垃圾回收。 c $ c>。显然,一旦将旋转器值双向绑定到的值被垃圾回收,该绑定将不再起作用。

So the bidirectional binding you explicitly create does not prevent the thing it is bound to (the ObjectProperty<Integer> created by asObject()) from being garbage collected. Since you keep no references to it, it is eligible for garbage collections as soon as you exit the initialize() method in the SpinnerWindow Controller. Obviously, once the value to which your spinner value is bidirectionally bound is garbage collected, the binding will not work any more.

仅出于演示目的,您可以通过以下方式查看钩上垃圾桶。例如。做

Just for demonstration purposes, you can see this by putting in a hook to force garbage collection. E.g. do

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

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

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

in SpinnerWindowController. If you do this, then clicking in the scroll pane will force garbage collection, and changing the spinner value will not update the property.

要解决此问题,请保留对从 asObject()获得的属性:

To fix this, retain a reference to the property you get from 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);
    }

}

这篇关于为什么Spinner第一次初始化时不更新其bound属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆