我想在javafx TextField中添加验证,以便用户只能插入整数值([0-9]和Dot)。用户也应该能够插入B或b(对于Billion)和K或k(对于千)和M或m(对于百万)。基本上它应该是一个数量字段。删除和退格也应该有效。
例如:
一旦用户点击k,10k就应该变成10,000.00而K不应该显示在amountfield(textfield)上 同样,10M或10m应转换为10,000,000.00不允许在文本字段中输入adsadi342fn3或31233123werwer或dsad342134k。
我在使用Swing的情况下验证TextField时使用了getKeyChar方法。但是在JavaFx的情况下我需要相同的实现,我们没有getKeyChar方法。
我使用了以下方法,但问题是它允许用户输入任何值。例如:sdafewr23rf
private void amountEntered() {
if (amountField != null) {
String value;
char[] charArray = amountField.getText().toCharArray();
if (charArray.length > 0)
switch (charArray[charArray.length - 1]) {
case 't':
case 'T':
value = multiplyValue(amountField.getText(), new BigDecimal(1000000000000.0));
updateAmount(value);
break;
case 'b':
case 'B':
value = multiplyValue(amountField.getText(), new BigDecimal(1000000000.0));
updateAmount(value);
break;
case 'm':
case 'M':
value = multiplyValue(amountField.getText(), new BigDecimal(1000000.0));
updateAmount(value);
break;
case 'k':
case 'K':
value = multiplyValue(amountField.getText(), new BigDecimal(1000.0));
updateAmount(value);
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
case ',':
updateAmount(amountField.getText());
break;
default:
break;
}
}
}
private String multiplyValue(String number, BigDecimal multValue) {
//get rid of "," for double parsing
BigDecimal value = new BigDecimal(cleanDouble(number.substring(0, number.length() - 1)));
value = value.multiply(multValue);
return value.toPlainString();
}
答案 0 :(得分:1)
除了收听文本属性中的更改并恢复它们无效之外,您还可以使用TextFormatter
否决对文本的更改。使用这种方法可以避免textProperty
的其他听众看到无效值,然后看到它恢复到之前的值:即textProperty
将始终包含有效的内容。
TextFormatter
需要UnaryOperator<TextFormatter.Change>
作为过滤器。过滤器可以返回null以完全否定更改,或者可以根据需要修改Change
的属性。
这是一个相当简单的例子,其中&#34; k&#34;或&#34; K&#34;取而代之的是&#34; 000&#34;,&#34; m&#34;或&#34; M&#34; by&#34; 000000&#34;,并删除其他非数字字符:
import java.util.function.UnaryOperator;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class TextFieldFilteringExample extends Application {
@Override
public void start(Stage primaryStage) {
TextField textField = new TextField();
textField.textProperty().addListener((obs, oldValue, newValue) -> {
System.out.println("Text change from "+oldValue+" to "+newValue);
});
UnaryOperator<Change> filter = change -> {
if (change.isAdded()) {
String addedText = change.getText();
if (addedText.matches("[0-9]*")) {
return change ;
}
// remove illegal characters:
int length = addedText.length();
addedText = addedText.replaceAll("[^0-9kKmM]", "");
// replace "k" and "K" with "000":
addedText = addedText.replaceAll("[kK]", "000");
// replace "m" and "M" with "000000":
addedText = addedText.replaceAll("[mM]", "000000");
change.setText(addedText);
// modify caret position if size of text changed:
int delta = addedText.length() - length ;
change.setCaretPosition(change.getCaretPosition() + delta);
change.setAnchor(change.getAnchor() + delta);
}
return change ;
};
textField.setTextFormatter(new TextFormatter<String>(filter));
StackPane root = new StackPane(textField);
root.setPadding(new Insets(20));
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
您也可以修改文本以引入分组分隔符(例如1,000,000),尽管逻辑在那里非常棘手。您还可以为文本格式化程序指定StringConverter<BigInteger>
,以便格式化程序本身具有类型BigInteger
的值,这是通过提供的转换器传递文本的结果。
答案 1 :(得分:1)
我创建了以下类来过滤TextField
上的输入,它也使用了JavaFX 8中引入的TextFormatter
。
public class TextFieldValidator {
private static final String CURRENCY_SYMBOL = DecimalFormatSymbols.getInstance().getCurrencySymbol();
private static final char DECIMAL_SEPARATOR = DecimalFormatSymbols.getInstance().getDecimalSeparator();
private final Pattern INPUT_PATTERN;
public TextFieldValidator(@NamedArg("modus") ValidationModus modus, @NamedArg("maxCountOf") int maxCountOf) {
this(modus.createPattern(maxCountOf));
}
public TextFieldValidator(@NamedArg("regex") String regex){
this(Pattern.compile(regex));
}
public TextFieldValidator(Pattern pattern){
INPUT_PATTERN = pattern;
}
public static TextFieldValidator maxFractionDigits(int maxCountOf) {
return new TextFieldValidator(maxFractionPattern(maxCountOf));
}
public static TextFieldValidator maxIntegers(int maxCountOf) {
return new TextFieldValidator(maxIntegerPattern(maxCountOf));
}
public static TextFieldValidator integersOnly() {
return new TextFieldValidator(integersOnlyPattern());
}
public TextFormatter<Object> getFormatter() {
return new TextFormatter<>(this::validateChange);
}
private Change validateChange(Change c) {
if (validate(c.getControlNewText())) {
return c;
}
return null;
}
public boolean validate(String input) {
return INPUT_PATTERN.matcher(input).matches();
}
private static Pattern maxFractionPattern(int maxCountOf) {
return Pattern.compile("\\d*(\\" + DECIMAL_SEPARATOR + "\\d{0," + maxCountOf+ "})?");
}
private static Pattern maxCurrencyFractionPattern(int maxCountOf) {
return Pattern.compile("^\\" + CURRENCY_SYMBOL + "?\\s?\\d*(\\" + DECIMAL_SEPARATOR + "\\d{0," + maxCountOf+ "})?\\s?\\" +
CURRENCY_SYMBOL + "?");
}
private static Pattern maxIntegerPattern(int maxCountOf) {
return Pattern.compile("\\d{0," + maxCountOf+ "}");
}
private static Pattern integersOnlyPattern() {
return Pattern.compile("\\d*");
}
public enum ValidationModus {
MAX_CURRENCY_FRACTION_DIGITS {
@Override
public Pattern createPattern(int maxCountOf) {
return maxCurrencyFractionPattern(maxCountOf);
}
},
MAX_FRACTION_DIGITS {
@Override
public Pattern createPattern(int maxCountOf) {
return maxFractionPattern(maxCountOf);
}
},
MAX_INTEGERS {
@Override
public Pattern createPattern(int maxCountOf) {
return maxIntegerPattern(maxCountOf);
}
},
INTEGERS_ONLY {
@Override
public Pattern createPattern(int maxCountOf) {
return integersOnlyPattern();
}
};
public abstract Pattern createPattern(int maxCountOf);
}
}
你可以像这样使用它:
textField.setTextFormatter(new TextFieldValidator(ValidationModus.MAX_INTEGERS, 4).getFormatter());
或者您可以在fxml文件中实例化它,并将其应用于具有相应属性的customTextField。
app.fxml:
<fx:define>
<TextFieldValidator fx:id="validator" modus="MAX_INTEGERS" maxCountOf="4"/>
</fx:define>
<CustomTextField validator="$validator" />
CustomTextField:
public class CustomTextField {
private TextField textField;
public CustomTextField(@NamedArg("validator") TextFieldValidator validator) {
this();
textField.setTextFormatter(validator.getFormatter());
}
}
对于您的用例,您可以使用适当的正则表达式模式调用TextFieldValidor构造函数,并将James-D的答案过滤器添加到validateChange(Change c)
答案 2 :(得分:0)
您可以收听text
property中的更改以检查有效输入。我个人更喜欢用户能够输入任何字符串,并且在用户提交编辑之前不会阻止任何编辑。
以下示例仅适用于spring.rabbitmq.password
(为简单起见),并且允许任何以非零开头的数字,并且只能通过将数字或数字分组为3位数,然后将其与{{1}分开}。如果输入无效,它会添加CSS类BigInteger
,如果用户按下回车键,则将其转换为仅包含数字的字符串:
,
在样式表中,我将无效文本字段的背景设置为红色,以便为用户提供可视反馈:
invalid
如果您想阻止用户输入任何无法通过剩余字符创建有效字符串的内容,您可以删除// regex for matching the input and extracting the parts
private static final Pattern NUMBER_PATTERN = Pattern.compile("([1-9](?:\\d*|\\d{0,2}(?:\\,\\d{3})*))([tbmk]?)", Pattern.CASE_INSENSITIVE);
// map from suffix to exponent for 10
private static final Map<Character, Byte> SUFFIX_EXPONENTS;
static {
Map<Character, Byte> prefixes = new HashMap<>();
prefixes.put('k', (byte) 3);
prefixes.put('m', (byte) 6);
prefixes.put('b', (byte) 9);
prefixes.put('t', (byte) 12);
SUFFIX_EXPONENTS = Collections.unmodifiableMap(prefixes);
}
private static BigInteger convert(String s) {
if (s == null) {
return null;
}
Matcher m = NUMBER_PATTERN.matcher(s);
if (!m.matches()) {
return null;
}
String numberString = m.group(1).replace(",", "");
String suffix = m.group(2);
BigInteger factor = suffix.isEmpty() ? BigInteger.ONE : BigInteger.TEN.pow(SUFFIX_EXPONENTS.get(Character.toLowerCase(suffix.charAt(0))));
return new BigInteger(numberString).multiply(factor);
}
@Override
public void start(Stage primaryStage) throws Exception {
TextField tf = new TextField();
tf.getStyleClass().add("invalid");
// property bound to the current number in the TextField or null, if invalid
ObjectProperty<BigInteger> numberProperty = new SimpleObjectProperty<>();
// Binding reevaluated on every change of the text property.
// A listener could be used instead to change the text to the
// previous value, if the new input is invalid.
numberProperty.bind(Bindings.createObjectBinding(() -> convert(tf.getText()), tf.textProperty()));
// change styleclass, if the string becomes (in)valid input
numberProperty.addListener((observable, oldValue, newValue) -> {
if (oldValue == null) {
tf.getStyleClass().remove("invalid");
} else if (newValue == null) {
tf.getStyleClass().add("invalid");
}
});
// handle user pressing enter
tf.setOnAction(evt -> {
BigInteger num = numberProperty.get();
tf.setText(num == null ? null : num.toString());
});
Pane root = new StackPane(tf);
Scene sc = new Scene(root, 300, 300);
sc.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.setScene(sc);
primaryStage.show();
}
以及与之相关的所有内容,并添加一个恢复为旧值的侦听器:
.text-field.invalid {
-fx-background-color: #f55;
}