问题
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:
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();
}
来源:https://stackoverflow.com/questions/56247504/javafx-set-an-input-filter-by-pattern-on-a-textfield