Search code examples
javafxlistenertextfieldkeypress

Why JavaFX TextField listener repeats 3 times?


I am trying to track input to TextField and allow user to input only 1 symbol per TextField, here is my code:

package sample;

import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        TextField textField = new TextField(); //creating new textfield
        Pane window = new Pane();
        Parent root = window;
        window.getChildren().addAll(textField); //adding textfield to the window
        primaryStage.setScene(new Scene(root, 200, 50));
        primaryStage.show();
        textField.textProperty().addListener(event ->
             {
                 try {
                     if (textField.getLength() > 1) { //check if the length of the textfield text exceeds 1
                         System.out.println("NOT Accepted");
                         textField.setText(String.valueOf(textField.getText().charAt(0))); //set textfield text to first char only
                     } else {
                         System.out.println("Accepted");
                     }
                 } catch (IndexOutOfBoundsException Bound) {}
             }
             );
        }

    public static void main(String[] args) {
        launch(args);
    }
}

When I press any symbol first time, everything works good, but when I press second time, event listener repeats 3 times. Here the exapmle: "a" key has been pressed, console output:

Accepted  //<---Correct

"a" key (or any another key) has been pressed second time, console output:

NOT Accepted  //<---Correct
Accepted      //<---Not correct
Accepted      //<---Not correct

As shown above listener repeats 3 times. But I expect that console should show only "NOT Accepted" and shouldn't repeat 2 times more.


Solution

  • Instead of using a ChangeListener, I recommend a simple TextFormatter instead. This allows you to prevent the change without triggering another event.

        textField.setTextFormatter(new TextFormatter<String>((TextFormatter.Change change) -> {
            String newText = change.getControlNewText();
            if (newText.length() == 1) {
                System.out.println("Accepted");
            } else if (newText.length() > 1) {
                System.out.println("NOT Accepted");
                return null;
            }
    
            return change;
        }));