2

I can't believe I haven't been able to find anything on this. It has to be a very common use-case.

I want to set my TextField (JavaFX) up so that the user is limited to entering data by pattern. For example, a standard US telephone number with a pattern like (###) ###-####. I have no problem formatting after the fact, and I see lots of tutorials/examples on restricting input to just numbers and the "(", ")", and "-" in the right places, but I don't want the user to have to enter the parentheses and dash - they should just be there and the numbers should get entered "around" them.

I see this all the time on web pages - so javascript must do it easily. I used to work in a language where it was as easy as something like:

myTextField.setInputPattern("(999) 999-9999") // yes this is fictional code 

You could do the same thing with any pattern - ss#, date entry, ip addresses, etc. and I'm a little stunned that I haven't been able to find something in JavaFX for this.

1
  • use a TextFormatter Commented May 22, 2019 at 7:18

1 Answer 1

2

Afaik there is no such functionality implemented yet. You can use a TextFormatter with a filter though, to modify any input to adhere to your pattern.

The following code considers # as placeholders for digits and leaves the rest as it is:

private static String clearText(String input) { return input.replaceAll("\\D+", ""); } private static UnaryOperator<Change> createPatternFilter(String pattern) { Pattern digitsPattern = Pattern.compile("\\d*"); final int maxDigits = pattern.replaceAll("[^#]*", "").length(); return change -> { String text = change.getText(); if (!digitsPattern.matcher(text).matches()) { return null; // prevent inputs other than digits } if (change.getControlText().equals(change.getControlNewText())) { return change; // allow all changes not modifying the text } String clearText = clearText(change.getControlNewText()); String clearPrefix = clearText(change.getControlNewText().substring(0, change.getAnchor())); final int prefixLength = clearPrefix.length(); if (clearText.length() > maxDigits) { if (prefixLength > maxDigits) { return null; // cursor already positioned after the last digit placeholder } clearText = clearText.substring(0, maxDigits); // cut of excessive digits } StringBuilder resultText = new StringBuilder(pattern.length()); int index = 0; int prefixIndex = 0; // copy parts digits before the cursor while (prefixIndex < prefixLength) { char c = pattern.charAt(index); if (c == '#') { resultText.append(clearPrefix.charAt(prefixIndex)); prefixIndex++; } else { resultText.append(c); } index++; } // deal with following non-digit placeholders char c; while (index < pattern.length() && (c = pattern.charAt(index)) != '#') { resultText.append(c); index++; } int newAnchor = resultText.length(); String clearSuffix = clearText.substring(prefixLength); int suffixIndex = 0; // copy remaining digits while (index < pattern.length() && suffixIndex < clearSuffix.length()) { c = pattern.charAt(index); if (c == '#') { resultText.append(clearSuffix.charAt(suffixIndex)); suffixIndex++; } else { resultText.append(c); } index++; } resultText.append(pattern.substring(index)); change.setRange(0, change.getControlText().length()); change.setText(resultText.toString()); change.selectRange(newAnchor, newAnchor); return change; }; } @Override public void start(Stage primaryStage) { String pattern = "(###) ###-####"; TextField tf = new TextField(pattern); TextFormatter<String> formatter = new TextFormatter<>(createPatternFilter(pattern)); tf.setTextFormatter(formatter); VBox content = new VBox(tf); Scene scene = new Scene(content); primaryStage.setScene(scene); primaryStage.show(); } 
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks! That is very close to working - the only thing missing is if you need to backspace the whole thing - it gets stuck at the "-". I won't have time today, but tomorrow I can put it into my actual code and see if i can tweak to work exactly. THANKS!
Hi - this is working on my form pretty well. I did notice that the function fires whenever there is a focus change - even on unrelated text boxes, buttons, etc. Do you know why that is or if it can be prevented? #fabian

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.