目前我正在学习如何正确使用Bindings和Binding-Events。我已经阅读了一本关于它的书的章节,一般来说我使用Bindings没有问题。
为了测试我的知识,我写了一个小的JavaFX8应用程序。我有2个TextFields,但此刻我专注于一个TextField,称为“firstName”。我正在使用BooleanBinding。每当TextField被填充时,BooleanBinding都设置为“true”。如果字段中没有输入,则BooleanBinding设置为“false”。我的目标是每当BooleanBinding发生变化时都更新名为“statusLabel”的Label。
这是绑定的外观:
BooleanBinding nameEntered = firstName.textProperty().isNotEmpty();
这是我的ChangeListener:
nameEntered.addListener((o, oldValue, newValue) -> {
statusLabel.setText(newValue.toString());
});
在很短的时间内,监听器正常工作。当BooleanBinding发生更改时,Label会更新。但是在一些输入更改(删除输入,再次填充等等)之后,Label不再进行更新。任何想法如何解决这个问题?
以下是完整代码:
FXMLController:
package gui;
/*
import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.StringBinding;
import javafx.beans.binding.When;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
*/
public class LayoutController implements Initializable {
/**
* Initializes the controller class.
*/
@FXML
private TextField firstName;
@FXML
private TextField secondName;
@FXML
private CheckBox checkBox1;
@FXML
private CheckBox checkBox2;
@FXML
private CheckBox checkBox3;
@FXML
private Label statusLabel;
@Override
public void initialize(URL url, ResourceBundle rb) {
BooleanBinding nameEntered = firstName.textProperty().isNotEmpty();
nameEntered.addListener((o, oldValue, newValue) -> {
statusLabel.setText(newValue.toString());
});
}
}
MainView.java:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* @author xyz
*/
public class MainView extends Application{
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Layout.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("Benutzerauswahl");
primaryStage.show();
}
public static void main(String args[]){
launch(args);
}
}
FXMLLayout:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="310.0" prefWidth="343.0" xmlns:fx="http://javafx.com/fxml/1" fx:controller="gui.LayoutController">
<center>
<AnchorPane prefHeight="371.0" prefWidth="380.0" BorderPane.alignment="CENTER">
<children>
<GridPane layoutX="50.0" layoutY="103.0" prefHeight="234.0" prefWidth="281.0" AnchorPane.leftAnchor="50.0" AnchorPane.rightAnchor="49.0" AnchorPane.topAnchor="50.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="155.0" minWidth="10.0" prefWidth="110.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="197.0" minWidth="10.0" prefWidth="171.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="40.0" minHeight="10.0" prefHeight="40.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="40.0" minHeight="10.0" prefHeight="40.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="400.0" minHeight="10.0" prefHeight="88.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label prefHeight="17.0" prefWidth="56.0" text="Vorname:" GridPane.halignment="CENTER" />
<Label prefHeight="17.0" prefWidth="69.0" text="Nachname:" GridPane.halignment="CENTER" GridPane.rowIndex="1" />
<TextField fx:id="firstName" prefHeight="25.0" prefWidth="144.0" GridPane.columnIndex="1" />
<TextField fx:id="secondName" prefHeight="25.0" prefWidth="144.0" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="2">
<children>
<CheckBox fx:id="checkBox1" layoutX="14.0" layoutY="42.0" mnemonicParsing="false" text="Kurs 1" />
<CheckBox fx:id="checkBox2" layoutX="14.0" layoutY="69.0" mnemonicParsing="false" text="Kurs 2" />
<CheckBox fx:id="checkBox3" layoutX="14.0" layoutY="96.0" mnemonicParsing="false" text="Kurs 3" />
<Label fx:id="statusLabel" layoutX="43.0" layoutY="132.0" prefHeight="17.0" prefWidth="84.0" text="Status" />
</children>
</AnchorPane>
</children>
</GridPane>
</children>
</AnchorPane>
</center>
</BorderPane>
答案 0 :(得分:4)
这实际上是JavaFX Beans Binding suddenly stops working的重复:问题在于绑定正在过早收集垃圾&#34;因为没有保留的实时引用。强制将引用保留在控制器中似乎有点棘手。
首先请注意,如果您实际绑定UI元素的属性(只要显示它就必须在范围内),则UI元素间接保留对绑定的引用。因此,在您的代码中,这将解决问题:
statusLabel.textProperty().bind(nameEntered.asString());
(而不是你现在拥有的监听器)。如果你实际上不能使用绑定,那么首先你需要让控制器保留对绑定的引用:
public class LayoutController implements Initializable {
// existing code...
private BooleanBinding nameEntered ;
public void initialize(URL url, ResourceBundle rb) {
nameEntered = firstName.textProperty().isNotEmpty();
nameEntered.addListener((o, oldValue, newValue) -> {
statusLabel.setText(newValue.toString());
});
}
}
然后另外你需要强制引用控制器本身以保持在范围内:
public class MainView extends Application{
private LayoutController controller ;
@Override
public void start(Stage primaryStage) throws Exception {
FMXLLoader loader = new FXMLLoader(getClass().getResource("Layout.fxml"));
Parent root = loader.load();
controller = loader.getController();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("Benutzerauswahl");
primaryStage.show();
}
public static void main(String args[]){
launch(args);
}
}
可能有一种更明显的方法可以做到这一点,但我无法找到有效的方法。