确定JavaFX中的调用节点更改侦听器

时间:2013-12-14 16:11:43

标签: java javafx-2

我需要在更改文本时对多个TextField执行验证。验证完全相同,所以我认为我使用一个程序。我无法使用 onInputMethodTextChanged ,因为即使控件没有焦点,我也需要执行验证。所以我将 ChangeListener 添加到 textProperty

private TextField firstTextField;
private TextField secondTextField;
private TextField thirdTextField;

protected void initialize() {
    ChangeListener<String> textListener = new ChangeListener<String>() {
        @Override
        public void changed(ObservableValue<? extends String> observable,
                String oldValue, String newValue) {
            // Do validation
        }
    };

    this.firstTextField.textProperty().addListener(textListener);
    this.secondTextField.textProperty().addListener(textListener);
    this.thirdTextField.textProperty().addListener(textListener);
}

但是,在执行验证时,无法知道哪个TextField触发了更改。我如何获得这些信息?

5 个答案:

答案 0 :(得分:12)

有两种方法:

假设您只使用TextField的text属性注册此侦听器,则传递到ObservableValue方法的changed(...)是对textProperty的引用。它有一个getBean()方法,它将返回TextField。所以你可以做到

StringProperty textProperty = (StringProperty) observable ;
TextField textField = (TextField) textProperty.getBean();

如果您使用ClassCastException的{​​{1}}之外的其他内容注册侦听器,这显然会中断(使用textProperty),但它允许您重用相同的侦听器实例。

更强大的方法可能是将侦听器类创建为内部类而不是匿名类,并保留对TextField的引用:

TextField

然后

private class TextFieldListener implements ChangeListener<String> {
  private final TextField textField ;
  TextFieldListener(TextField textField) {
    this.textField = textField ;
  }
  @Override
  public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
    // do validation on textField
  }
}

答案 1 :(得分:0)

@Override
public void changed(ObservableValue observableValue, Object o, Object n) {
    try {
        StringProperty textProperty = (StringProperty) observableValue ;
        TextField textField = (TextField) textProperty.getBean();

        if (textField == textFieldChannel1) {

        } else if (textField == textFieldChannel2) {

        } else if (textField == textFieldChannel3) {

        }
    } catch (Exception e) {
        //e.printStackTrace();
    }
}

答案 2 :(得分:0)

我有类似的问题,但无法从给定的答案中得出解决方案。所以,我想我会制作一个示例程序,可以帮助下一个人更好地理解。在所有文本字段都有数字输入之前,该按钮才会启用。

  

主:

/**
 *
 * @author blj0011
 */
public class DoubleFieldValidator extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));

        Scene scene = new Scene(root);

        stage.setScene(scene);
        stage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }    
}
  

控制器:

import java.net.URL;
import java.util.ArrayList;
import java.util.Random;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;

/**
 *
 * @author blj0011
 */
public class FXMLDocumentController implements Initializable {    
    @FXML private Button btnMain;
    @FXML private AnchorPane apScrollPane;

    ObservableList<TextField> textFieldContainer = FXCollections.observableArrayList();
    ArrayList<Boolean> fieldValidatorContainer = new ArrayList();

    @FXML
    private void handleButtonAction(ActionEvent event) {
        System.out.println("You pressed me!");
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
        btnMain.setDisable(true);//Disable button until all fields are validated
        Random random = new Random();
        //create textfields on the fly
        for(int i = 0; i < random.nextInt(20) + 1; i++)//creates 1 to 20 textfields
        {
            textFieldContainer.add(new TextField());//create textfield and add it to container
            fieldValidatorContainer.add(false);//set corresponding validator container to false;           
        }
        VBox vbox = new VBox();
        vbox.getChildren().addAll(textFieldContainer);
        apScrollPane.getChildren().add(vbox);

        //create a listener for each textfield
        for(int i = 0; i < textFieldContainer.size(); i++)
        {
            textFieldContainer.get(i).textProperty().addListener((observable, oldValue, newValue) -> {
                System.out.println("observable: " + observable);
                //loop though the textfieldContainer to find right container
                for(int t = 0; t < textFieldContainer.size(); t++)
                {
                    //look for right container. once found get container's index t
                    if(textFieldContainer.get(t).textProperty().equals((observable)))
                    {
                        System.out.println("object t: " + t);
                        fieldValidatorContainer.set(t, fieldValidator(newValue)) ;//set validator container at corresponding index
                        fieldValidatorCheck(); //run the check to see if the button should be enabled or not.
                    }
                }   
            });
        }
    }

    //used to check if field has double value or not.
    private boolean fieldValidator(String data){        
        try
        {
            double d = Double.parseDouble(data);
            return true;
        }
        catch(NumberFormatException ex)
        {
            return false;
        }        
    }

    //used to disable or enable update button
    private void fieldValidatorCheck()
    {        
        for(int i = 0; i < fieldValidatorContainer.size(); i++)
        {
            System.out.println("poition " + i + ": " + fieldValidatorContainer.get(i));
            if(fieldValidatorContainer.get(i) == false)
            {
                btnMain.setDisable(true);
                return;
            }
        }

        btnMain.setDisable(false);
    }
}
  

FXML:

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

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane id="AnchorPane" prefHeight="330.0" prefWidth="459.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="doublefieldvalidator.FXMLDocumentController">
    <children>
        <Button fx:id="btnMain" layoutX="284.0" layoutY="144.0" onAction="#handleButtonAction" text="Click Me!" />
      <ScrollPane fx:id="spMain" layoutX="30.0" layoutY="30.0" prefHeight="297.0" prefWidth="200.0">
        <content>
          <AnchorPane fx:id="apScrollPane" minHeight="0.0" minWidth="0.0" prefHeight="279.0" prefWidth="200.0" />
        </content>
      </ScrollPane>
    </children>
</AnchorPane>

答案 3 :(得分:0)

只需使用 valueProperty 即可;)

this.firstTextField.valueProperty().addListener(textListener);
this.secondTextField.valueProperty().addListener(textListener);
this.thirdTextField.valueProperty().addListener(textListener);

答案 4 :(得分:0)

它有一个简单的解决方案。

public void setListener(TextField textField) {
    BiConsumer<TextField, String> check = (tField, newValue) -> {
        // Do validation
    };
    textField.textProperty().addListener((ov, o, n)->check.accept(textField, n));
}