绑定侦听器不是';t在javafx8中工作正常

绑定侦听器不是';t在javafx8中工作正常,javafx,properties,Javafx,Properties,目前我正在学习如何正确使用绑定和绑定事件。我已经读过一本关于它的书的一章,一般来说,我在使用绑定方面没有问题 为了测试我的知识,我编写了一个小小的JavaFX8应用程序。我有两个文本字段,但目前我只关注一个文本字段,名为“firstName”。我用的是布尔绑定。每当文本字段被填充时,BooleanBinding就被设置为“true”。如果字段中没有输入,BooleanBinding将设置为“false”。我的目标是在布尔绑定发生更改时更新名为“statusLabel”的标签 以下是绑定的外观:

目前我正在学习如何正确使用绑定和绑定事件。我已经读过一本关于它的书的一章,一般来说,我在使用绑定方面没有问题

为了测试我的知识,我编写了一个小小的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,你能给我看一下最后一个解决方案的代码片段吗?(创建实例变量..)只是为了更好地理解。。太好了,谢谢