JavaFX Set an Input filter by Pattern on a TextField

空扰寡人 提交于 2019-12-10 23:16:51

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!