如何使用 JavaFX 在不更改其值的情况下格式化文本字段中的字符串



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

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

2085551212

我可以使用UnaryOperatorTextFormatter来处理这个问题。

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.

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

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

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

编辑

下面是我的测试代码,它是评论中讨论的后续。我重用了大量的原始代码。我创建了一个FXML JavaFX项目,并在FXML文件中定义了TextFieldLabelTextField接受用户的输入并设置其格式。Label显示应转到数据库的文本格式化程序的值(仅数字)。可以通过调用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());
}
}

相关内容

最新更新