1

I need to limit interval of the text property of a text field

int maxLength = 64;
    int minLength = 0;
    txtSeuil.textProperty().addListener((v, oldValue, newValue) -> {
        if (!newValue.matches("\\d*")) {
            txtSeuil.setText(newValue.replaceAll("[^\\d*{1,2}]", ""));
            if (txtSeuil.getText().length() > maxLength || txtSeuil.getText().length() < minLength) {
                String s = txtSeuil.getText().substring(0, maxLength);
                txtSeuil.setText(s);
            }
        }

    });

the field does accept only numbers but any number, not just the interval values

devhicham
  • 557
  • 2
  • 6
  • 14

3 Answers3

2

To solve your problem you can implement custom Filter for TextFormatter. It will allow to enter numbers only and restrict length of string. Here is snippet which show how it can works:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.stage.Stage;

public class Main5 extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        TextField textField = new TextField();
        textField.setTextFormatter(new TextFormatter<Integer>(change -> {
            if (!change.getText().isEmpty()) {
                return change.getText().matches("\\d+") && change.getControlNewText().length() <= 5 ? change : null;
            }

            return change;
        }));

        primaryStage.setScene(new Scene(textField));
        primaryStage.show();
    }
}
Maxim
  • 9,701
  • 5
  • 60
  • 108
  • thanks, it works for the length but I made a mistake on the code, the minValue and maxValue that I want are not for the length value but vor the text value, so how can I fix that ? – devhicham Jan 07 '17 at 12:16
  • @devhicham what do you mean by "text value"? Only numbers from 0 – 64? – beatngu13 Jan 07 '17 at 14:55
  • @beatngu13 yes please, I want that the field accept only this interval numbers from 0 to 64 – devhicham Jan 07 '17 at 16:12
  • @devhicham then have a look at [this answer](http://stackoverflow.com/a/41514479/3429133). – beatngu13 Jan 07 '17 at 16:15
2

If I have understood correctly, you're trying to implement a number field that only allows values within the interval [0, 64]. According to this answer, TextFormatter is the recommended way to accomplish such a functionality. Have a look at this MWE which should solve your problem:

public class RestrictedNumberFieldDemo extends Application {

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

    @Override
    public void start(Stage primaryStage) {
        TextField numField = new TextField();
        numField.setTextFormatter(new TextFormatter<Integer>(change -> {
            // Deletion should always be possible.
            if (change.isDeleted()) {
                return change;
            }

            // How would the text look like after the change?
            String txt = change.getControlNewText();

            // There shouldn't be leading zeros.
            if (txt.matches("0\\d+")) {
                return null;
            }

            // Try parsing and check if the result is in [0, 64].
            try {
                int n = Integer.parseInt(txt);
                return 0 <= n && n <= 64 ? change : null;
            } catch (NumberFormatException e) {
                return null;
            }
        }));

        primaryStage.setScene(new Scene(numField));
        primaryStage.show();
    }

}
Community
  • 1
  • 1
beatngu13
  • 7,201
  • 6
  • 37
  • 66
0

Your problem is:

The length check is not done, if the regex matches. But the text can be a sequence of digits and be to long.

You need to do those checks independent of each other.

Also you're setting a new value in some cases which triggers the same checks again. Of course they will result in the String being evaluates as a valid input, but you could prevent checking again by introducing a field in the ChangeListener that stores, whether the listener is currently being executed:

txtSeuil.textProperty().addListener(new ChangeListener<String>() {

    private boolean validating = false;

    @Override
    public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
        if (!validating) {
            validating = true;
            String newText = newValue;
            boolean modify = false;
            if (!newValue.matches("\\d*")) {
                newText = newValue.replaceAll("[^\\d*{1,2}]", "");
                modify = true;
            }
            if (newText.length() > maxLength || newText.length() < minLength) {
                newText = newText.substring(0, maxLength);
                modify = true;
            }
            if (modify) {
                txtSeuil.setText(newText);
            }
            validating = false;
        }
    }
});
fabian
  • 80,457
  • 12
  • 86
  • 114