绑定侦听器不是';t在javafx8中工作正常
目前我正在学习如何正确使用绑定和绑定事件。我已经读过一本关于它的书的一章,一般来说,我在使用绑定方面没有问题 为了测试我的知识,我编写了一个小小的JavaFX8应用程序。我有两个文本字段,但目前我只关注一个文本字段,名为“firstName”。我用的是布尔绑定。每当文本字段被填充时,BooleanBinding就被设置为“true”。如果字段中没有输入,BooleanBinding将设置为“false”。我的目标是在布尔绑定发生更改时更新名为“statusLabel”的标签 以下是绑定的外观:绑定侦听器不是';t在javafx8中工作正常,javafx,properties,Javafx,Properties,目前我正在学习如何正确使用绑定和绑定事件。我已经读过一本关于它的书的一章,一般来说,我在使用绑定方面没有问题 为了测试我的知识,我编写了一个小小的JavaFX8应用程序。我有两个文本字段,但目前我只关注一个文本字段,名为“firstName”。我用的是布尔绑定。每当文本字段被填充时,BooleanBinding就被设置为“true”。如果字段中没有输入,BooleanBinding将设置为“false”。我的目标是在布尔绑定发生更改时更新名为“statusLabel”的标签 以下是绑定的外观:
BooleanBinding nameEntered = firstName.textProperty().isNotEmpty();
这是我的ChangeListener:
nameEntered.addListener((o, oldValue, newValue) -> {
statusLabel.setText(newValue.toString());
});
在短时间内,监听器工作正常。更改布尔绑定后,标签将更新。但在一些输入更改(删除输入、再次填充等)后,标签不再更新。有没有办法解决这个问题
以下是完整的代码:
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>
这实际上是重复的:问题是绑定被“过早地垃圾收集”,因为没有保留对它的活动引用。强制将引用保留在控制器中似乎有点棘手 首先请注意,如果您实际绑定了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);
}
}
也许有一种更明显的方法可以做到这一点,但我找不到一种有效的方法。我读了副本,但这个解决方案对我不起作用。即使我保留对nameEntered的引用,绑定在几秒钟后仍然不起作用。您可以使用一个简单的绑定,如:
statusLabel.textProperty().bind(firstName.textProperty().isNotEmpty().asString())代码>强制保留绑定在这里有点棘手。@DVarga中的代码将使用statusLabel.textProperty().bind(nameintered.asString())执行此操作
,因为保留了状态标签
,这使得它(间接地)保留了对绑定的引用。但是,仅在控制器中使绑定成为实例变量并不起作用,因为您没有对控制器的实时引用:您可以通过在MainView
中为控制器创建实例变量并在加载FXML时初始化来强制绑定。。。这真的很痛苦…谢谢你的帮助,我真的很感激。你说得对,那个解决方案不是最好的,所以我最好使用DVarga绑定。@James_D,你能给我看一下最后一个解决方案的代码片段吗?(创建实例变量..)只是为了更好地理解。。太好了,谢谢