为什么此JavaFX双向绑定不起作用

时间:2020-04-26 19:11:46

标签: javafx

我有一个奇怪的情况,在System.gc()调用之后,ComboBox和IntegerProperty之间的双向绑定始终断开。不知何故,如果我使用ObjectProperty,一切都会按预期进行。

我附上了下面的所有代码。当应用程序开始运行时,标签会正确显示ComboBox1和ComboBox2的相应选定值。但是,一旦单击GC按钮,ComboBox1和fubar.mID1属性之间的双向绑定就会中断。

我知道双向绑定是一个弱绑定,一旦对象超出范围,gc就会清除它。但是这里没有一个对象超出范围。为什么?

这是MainController.java:

package sample;

import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.util.StringConverter;

public class MainController {
    private final Fubar fubar = new Fubar(1, 2);

    @FXML
    ComboBox<Integer> mComboBox1;
    @FXML
    ComboBox<Integer> mComboBox2;
    @FXML
    Label mLabel;

    @FXML
    public void initialize() {
        mComboBox1.setConverter(new StringConverter<Integer>() {
            @Override
            public String toString(Integer integer) {
                if (integer == null)
                    return "";
                switch (integer) {
                    case 1:
                        return "One";
                    case 2:
                        return "Two";
                    default:
                        return "";
                }
            }

            @Override
            public Integer fromString(String s) {
                if (s == null)
                    return 0;
                switch (s) {
                    case "One":
                        return 1;
                    case "Two":
                        return 2;
                    default:
                        return 0;
                }
            }
        });
        for (int i = 0; i < 3; i++)
            mComboBox1.getItems().add(i);
        mComboBox1.valueProperty().bindBidirectional(fubar.getID1Property().asObject());

        mComboBox2.setConverter(mComboBox1.getConverter());
        for (int i = 0; i < 3; i++)
            mComboBox2.getItems().add(i);
        mComboBox2.valueProperty().bindBidirectional(fubar.getID2Property());

        mLabel.textProperty().bind(Bindings.createStringBinding(() ->
                fubar.getID1Property().get() + "/" + fubar.getID2Property().get(),
                fubar.getID1Property(), fubar.getID2Property()));
    }

    @FXML
    private void handleGC() {
        System.gc();
    }
}

这是Fubar.java:

package sample;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;

public class Fubar {
    private final IntegerProperty mID1 = new SimpleIntegerProperty(0);
    private final ObjectProperty<Integer> mID2 = new SimpleObjectProperty<>(0);

    Fubar(Integer id1, Integer id2) {
        mID1.set(id1);
        mID2.set(id2);
    }

    IntegerProperty getID1Property() { return mID1; }
    ObjectProperty<Integer> getID2Property() { return mID2; }
}

这是Main.java:

package sample;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;

public class Fubar {
    private final IntegerProperty mID1 = new SimpleIntegerProperty(0);
    private final ObjectProperty<Integer> mID2 = new SimpleObjectProperty<>(0);

    Fubar(Integer id1, Integer id2) {
        mID1.set(id1);
        mID2.set(id2);
    }

    IntegerProperty getID1Property() { return mID1; }
    ObjectProperty<Integer> getID2Property() { return mID2; }
}

这是Main.java:

package sample;

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 primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("Main.fxml"));
        primaryStage.setTitle("Main");
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

和Main.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>

<GridPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.MainController">
  <columnConstraints>
      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
    <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
    <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
  </columnConstraints>
  <rowConstraints>
    <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
    <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
    <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
  </rowConstraints>
   <children>
      <ComboBox fx:id="mComboBox1" prefWidth="150.0" />
      <ComboBox fx:id="mComboBox2" prefWidth="150.0" GridPane.rowIndex="1" />
      <Button mnemonicParsing="false" onAction="#handleGC" text="GC" GridPane.rowIndex="2" />
      <Label fx:id="mLabel" text="Main" GridPane.columnIndex="2" />
   </children>
</GridPane>

1 个答案:

答案 0 :(得分:0)

啊哈,我知道了。问题出在fubar.getID1Property().asObject()。我相信Java将此asObject()视为一个临时对象。临时对象会立即超出范围,而System.gc()调用只需清除临时对象并破坏绑定。

要解决此问题,需要保留对此asObject()的引用。在我自己的示例问题中,执行此操作的一种方法是在控制器中声明字段ObjectProperty<Integer> middleMan并将其初始化为 ObjectProperty<Integer> middleMan = fubar.getID1Property().asObject();,然后将mCombobox1.valueProperty()绑定到此middleMan

相关问题