I am trying to change the value of a TextField for display only. Ie - when users attempt to enter phone number, they only enter the digits and when they leave the field, it displays formatted without changing the data in the field.
Let's say I have a TextField for a phone number and it should allow digits only, maximum of 10 characters:
2085551212
I can handle that with a TextFormatter using a 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;
}
};
Run Code Online (Sandbox Code Playgroud)
However I would like to format the value to be when it's 10 characters long (likely unformatted when != 10):
(208) 555-1212
When I use a TextFormatter to format it, it changes the value of the string to (208) 555-1212. We store only the digits in the database 2085551212.
I attempted this with a StringConverter. But I couldn't make it work. In the toString() method I strip out the formatting, however when I do that my TextField doesn't display.
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;
}
};
Run Code Online (Sandbox Code Playgroud)
I bound to a TextField like this which I assumed would work:
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.
Run Code Online (Sandbox Code Playgroud)
So I am at a loss. I essentially want to allow only digits and then when the user leaves the field present the value in a formatted way - without actually changing the string value that goes to the database.
您在转换器中混淆了彼此的目的toString()和方法。将文本编辑器的 value 属性转换为显示的文本,而不是相反。尝试在这些方法中切换代码,它应该可以工作。fromString()toString()
文本字段在失去焦点后不显示任何内容的原因是因为fromString()调用方法并返回 null(从else子句)。这会提交null给编辑器的 value 属性。value 属性的更改通过调用将toString(null)编辑器的 text 属性更改为空字符串来更新显示的文本 (textProperty)。
编辑
下面是我的测试代码,它是评论中讨论的后续内容。我重复使用了大量的原始代码。我创建了一个 FXML JavaFX 项目并TextField在LabelFXML 文件中进行了定义。接受TextField用户的输入并对其进行格式化。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());
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7168 次 |
| 最近记录: |