如何在TextField中格式化字符串而不用JavaFX更改它的值

问题描述 投票:3回答:1

我试图改变TextField的值仅供显示。即 - 当用户尝试输入电话号码时,他们只输入数字,当他们离开该字段时,它显示格式化而不更改字段中的数据。

假设我有一个电话号码的TextField,它应该只允许数字,最多10个字符:

2085551212

我可以使用TextFormatter处理UnaryOperator

 UnaryOperator<TextFormatter.Change> filter = new UnaryOperator<TextFormatter.Change>() {
      @Override
      public TextFormatter.Change apply(TextFormatter.Change change) {
           int maxlength = 14;

           if(change.getControlText().indexOf('(') == -1) {
                maxlength = 10;
           }

           System.out.println(change);

           if (change.getControlText().length() + change.getText().length() >= maxlength) {
                int maxPos = maxlength - change.getControlText().length();
                change.setText(change.getText().substring(0, maxPos));
           }

           String text = change.getText();

           for (int i = 0; i < text.length(); i++)
                if (!Character.isDigit(text.charAt(i)))
                     return null;

           return change;
      }
 };

但是我想将值格式化为10个字符长(当!= 10时可能未格式化):

(208) 555-1212

当我使用TextFormatter格式化它时,它会将字符串的值更改为(208)555-1212。我们只存储数据库2085551212中的数字。

我用StringConverter尝试了这个。但我无法使其发挥作用。在toString()方法中我删除格式,但是当我这样做时,我的TextField不会显示。

 StringConverter<String> formatter = new StringConverter<String>() {
      @Override
      public String fromString(String string) {
           System.out.println("fromString(): before = " + string);

           if (string.length() == 14) {
                System.out.println("fromString(): after = " + string);

                return string;
           } else if (string.length() == 10 && string.indexOf('-') == -1) {
                String result =  String.format("(%s) %s-%s", string.substring(0, 3), string.substring(3, 6),
                    string.substring(6, 10));
                System.out.println("fromString(): after = " + result);

                return result;
           } else {
                return null;
           }
      }

      @Override
      public String toString(String object) {

           System.out.println("toString(): before = " + object);

           if(object == null) {
                return "";
           }

           Pattern p = Pattern.compile("[\\p{Punct}\\p{Blank}]", Pattern.UNICODE_CHARACTER_CLASS);
           Matcher m = p.matcher(object);
           object = m.replaceAll("");

           System.out.println("toString(): after = " + object);

           return object;
      }
 };

我绑定了这样的TextField,我认为它会起作用:

 txtPhone.setTextFormatter(new TextFormatter<String>(formatter, null, filter));
 t2.textProperty().bindBidirectional(foo.fooPropertyProperty(), formatter); //I was just testing to see the results in another textfield to see if it would work.

所以我不知所措。我本质上只想允许数字,然后当用户离开字段时以格式化方式显示值 - 而不实际更改进入数据库的字符串值。

javafx-8
1个回答
4
投票

你在转换器中混淆了toString()fromString()方法的目的。 toString()将文本编辑器的value属性转换为显示的文本,而不是相反。尝试在这些方法中切换代码,它应该工作。

失去焦点后文本字段不显示任何内容的原因是因为调用fromString()方法并返回null(来自else子句)。这会将null提交给编辑器的value属性。 value属性的更改通过调用toString(null)更新显示的文本(textProperty),TextField将编辑器的text属性更改为空字符串。

编辑

以下是我的测试代码,它是评论中讨论的后续内容。我重复使用了大量的原始代码。我创建了一个FXML JavaFX项目,并在FXML文件中定义了LabelTextFieldLabel接受用户的输入并对其进行格式化。 formatter.valueProperty().get()显示应该转到数据库的文本格式化程序的值(仅限数字)。通过调用import java.net.URL; import java.util.ResourceBundle; import java.util.function.UnaryOperator; import java.util.regex.Matcher; import java.util.regex.Pattern; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.control.TextFormatter; import javafx.util.StringConverter; public class FXMLDocumentController implements Initializable { // label displays phone number containing only digits (for database) @FXML private Label label; /* field displays formatted text (XXX)-XXX-XXXX after user types 10 digits and presses Enter or if the field looses focus */ @FXML private TextField field; @Override public void initialize(URL url, ResourceBundle rb) { UnaryOperator<TextFormatter.Change> filter = new UnaryOperator<TextFormatter.Change>() { @Override public TextFormatter.Change apply(TextFormatter.Change change) { if (!change.isContentChange()) { /* nothing is added or deleted but change must be returned * as it contains selection info and caret position */ return change; } int maxlength = 14; if (change.getControlText().indexOf('(') == -1) { maxlength = 10; } if (change.getControlNewText().length() > maxlength || change.getText().matches("\\D+")) { // invalid input. Cancel the change return null; } return change; } }; StringConverter<String> converter = new StringConverter<String>() { // updates displayed text from commited value @Override public String toString(String commitedText) { if (commitedText == null) { // don't change displayed text return field.getText(); } if (commitedText.length() == 10 && !commitedText.matches("\\D+")) { return String.format("(%s) %s-%s", commitedText.substring(0, 3), commitedText.substring(3, 6), commitedText.substring(6, 10)); } else { /* Commited text can be either null or 10 digits. * Nothing else is allowed by fromString() method unless changed directly */ throw new IllegalStateException( "Unexpected or incomplete phone number value: " + commitedText); } } // commits displayed text to value @Override public String fromString(String displayedText) { // remove formatting characters Pattern p = Pattern.compile("[\\p{Punct}\\p{Blank}]", Pattern.UNICODE_CHARACTER_CLASS); Matcher m = p.matcher(displayedText); displayedText = m.replaceAll(""); if (displayedText.length() != 10) { // user is not done typing the number. Don't commit return null; } return displayedText; } }; TextFormatter<String> formatter = new TextFormatter<String>(converter, "1234567890", filter); field.setTextFormatter(formatter); label.textProperty().bind(formatter.valueProperty()); } } 可以访问该值。我希望它有所帮助。

qazxswpoi
© www.soinside.com 2019 - 2024. All rights reserved.