javafxlistenertextfieldchangelistener

One ChangeListener for multiple TextFields in Javafx


I want to use a single ChangeListener for multiple TextFields in my application.

I know that you can create a listener object and add this to all TextFields, then you can check via the event key which TextField-Object triggered the event.

With lamda expression I could use this for every TextField, unfortunately this isn't suited for my problem. I use the input of all TextFields so one single Listener should be enough.

TextField textField = new TextField();
textField.textProperty().addListener((observable, oldValue, newValue) -> {
    System.out.println("textfield changed from " + oldValue + " to " + newValue);
});

Solution

  • As Slaw pointed out in his comment, you can easily create a single ChangeListener and then apply it to each of your TextField nodes.

    ChangeListener<String> listener = ((observable, oldValue, newValue) -> {
        System.out.println("TextField changed from " + oldValue + " to " + newValue + "!");
    });
    

    Here is a complete sample you can run to see it in action:

    import javafx.application.Application;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    import javafx.beans.value.ChangeListener;
    import javafx.beans.value.ObservableValue;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.ListView;
    import javafx.scene.control.TextField;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    
    public class ReusableListenerSample extends Application {
    
        public static void main(String[] args) {
            launch(args);
        }
    
        @Override
        public void start(Stage primaryStage) {
    
            // Simple Interface
            VBox root = new VBox(10);
            root.setAlignment(Pos.CENTER);
            root.setPadding(new Insets(10));
    
            // Create a ChangeListener that we will apply to multiple TextFields
            ChangeListener<String> listener = ((observable, oldValue, newValue) -> {
                System.out.println("TextField changed from " + oldValue + " to " + newValue + "!");
            });
    
            // Create some TextFields
            TextField txt1 = new TextField();
            TextField txt2 = new TextField();
            TextField txt3 = new TextField();
    
            // Now, we can add our preconfigured listener to each of the TextFields
            txt1.textProperty().addListener(listener);
            txt2.textProperty().addListener(listener);
            txt3.textProperty().addListener(listener);
    
            // Add our TextFields to the scene
            root.getChildren().addAll(txt1, txt2, txt3);
    
            // Show the stage
            primaryStage.setScene(new Scene(root));
            primaryStage.setTitle("ListViewSample Sample");
            primaryStage.show();
    
        }
    }
    

    Now, the above example does not indicate which TextField was changed. In order to track that, you could create your own inner class that implements ChangeListener<String> and pass your TextField (or any other data to it):

    class MyChangeListener implements ChangeListener<String> {
    
        private final TextField myTextField;
    
        public MyChangeListener(TextField myTextField) {
            this.myTextField = myTextField;
        }
    
        @Override
        public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
            System.out.println(myTextField + " changed from " + oldValue + " to " + newValue + "!");
        }
    }
    

    Then, just add a new instance of that MyChangeListener to each TextField instead:

    txt1.textProperty().addListener(new MyChangeListener(txt1));