JavaFX 在文本字段上按模式设置输入过滤器

标签 java regex javafx formatting

我不敢相信我还没有找到任何关于此的信息。它必须是一个非常常见的用例。

我想设置我的 TextField (JavaFX),以便用户只能按模式输入数据。例如,具有类似 (###) ###-#### 模式的标准美国电话号码。事后格式化没有问题,并且我看到很多关于将输入限制为数字以及正确位置的“(”、“)”和“-”的教程/示例,但我不希望用户必须输入括号和破折号 - 它们应该就在那里,并且数字应该在它们“周围”输入。

我一直在网页上看到这一点 - 所以 javascript 必须很容易做到这一点。我曾经使用一种非常简单的语言,例如:

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

您可以对任何模式执行相同的操作 - ss#、日期输入、IP 地址等。令我有点惊讶的是,我在 JavaFX 中找不到相关的内容。

最佳答案

据我所知,目前还没有实现这样的功能。不过,您可以使用带有过滤器的 TextFormatter 来修改任何输入以遵循您的模式。

以下代码将 # 视为数字占位符,其余部分保持原样:

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();
}

关于JavaFX 在文本字段上按模式设置输入过滤器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56247504/

相关文章:

java - Log4J 能够从磁盘已满中恢复吗?

java - 带有 Microsoft 错误的 Firebase 身份验证

javascript - 从变量中删除括号和尾随空格

JavaFX - 无需 Lambda 即可获取 ChoiceBox 的值

javafx:如何将 mousepressed 事件应用于循环中的 ImageView?

java - 我可以从语音识别中获取语音数据(例如 mp3 格式)吗?

java - 在 URL 对象中设置自定义 HTTP 请求 header 不起作用

java - 像在 C# 中一样在 Java 中捕获正则表达式

php - 正则表达式中的正则表达式可选词

JavaFX 将标签文本属性绑定(bind)到多个属性更改