user4786688
user4786688

Reputation:

JavaFX TextField OnKeyTyped Not Working Properly

Given

JavaFX App, Input TextField and Submit Button

There's an EventHandler attached to the TextField so that if no text is typed, the Button should be disabled and vice versa.

Here's my code:

button.setDisable(true); // initially disabled
textField.setOnKeyTyped(new EventHandler<KeyEvent>() {
       @Override
       public void handle(KeyEvent event) {
            if (textField.getText().trim().isEmpty()) { // if blank textfield
                 button.setDisable(true);
            } else {
                 button.setDisable(false); 
             }
        }
});

Both Controls are initialized properly. And as far as I know, the difference between KeyTyped and KeyPressed/Released is that Typed is for characters that can actually be typed and shown; whereas Pressed/Released is for any keyboard key including Ctrl, Alt...

Problem

I type into the textField one character, the button is still disabled! If I type 2 characters and more and it becomes enabled!

However, when if I do the following:

button.setDisable(true);
textField.setOnKeyReleased(
    /*
     exact same code as above
    */
);

my problem will be solved.

Question

WHY? Where's the flaw in my code? Why doesn't KeyTyped work as expected?

Upvotes: 2

Views: 2721

Answers (1)

DVarga
DVarga

Reputation: 21799

The problem is: in the time when you handle the KeyEvent, it is not sure that the textProperty of your TextField was updated or not.

Cleaner and safer approach to add a listener for the textProperty of the TextField to be notified when the text of the TextField changes. Imagine also the usecase that the user pasted some content: in this case there is no KeyEvent at all, therefore your Button remains disabled.

Using a listener on textProperty()

button.setDisable(true);
textField.textProperty().addListener((obj, oldVal, newVal) -> {

    if (newVal.trim().isEmpty()) { // if blank textfield
         button.setDisable(true);
    } else {
         button.setDisable(false); 
     }
});

... or even shorter ...

button.setDisable(true);
textField.textProperty().addListener((obj, oldVal, newVal) -> button.setDisable(newVal.trim().isEmpty()));

But, the much cleaner solution to use binding between the disableProperty of the Button and the textProperty of the TextField.

Using a binding between disableProperty and textProperty

BooleanBinding textIsEmpty = Bindings.createBooleanBinding(() -> textField.getText().trim().isEmpty(), textField.textProperty());
button.disableProperty().bind(textIsEmpty);

Upvotes: 2

Related Questions