绑定侦听器在JavaFX 8

时间:2016-09-06 12:51:40

标签: javafx properties

目前我正在学习如何正确使用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>

1 个答案:

答案 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);
    }
}

可能有一种更明显的方法可以做到这一点,但我无法找到有效的方法。