我需要将TextField的输入限制为整数。有什么建议吗?
答案 0 :(得分:93)
非常旧的线程,但这看起来更整洁,如果粘贴了它就会删除非数字字符。
// force the field to be numeric only
textField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue,
String newValue) {
if (!newValue.matches("\\d*")) {
textField.setText(newValue.replaceAll("[^\\d]", ""));
}
}
});
答案 1 :(得分:38)
我知道这是一个相当古老的主题,但对于未来的读者来说,这是另一个我发现非常直观的解决方案:
public class NumberTextField extends TextField
{
@Override
public void replaceText(int start, int end, String text)
{
if (validate(text))
{
super.replaceText(start, end, text);
}
}
@Override
public void replaceSelection(String text)
{
if (validate(text))
{
super.replaceSelection(text);
}
}
private boolean validate(String text)
{
return text.matches("[0-9]*");
}
}
答案 2 :(得分:32)
2016年4月更新
这个答案是几年前创建的,原来的答案现在基本上已经过时了。
从Java 8u40开始,Java有一个TextFormatter,它通常最适合强制输入特定格式,例如JavaFX TextFields上的数字:
另请参阅此问题的其他答案,其中特别提到TextFormatter。
原始答案
这个gist中有一些例子,我复制了下面的一个例子:
// helper text field subclass which restricts text input to a given range of natural int numbers
// and exposes the current numeric int value of the edit box as a value property.
class IntField extends TextField {
final private IntegerProperty value;
final private int minValue;
final private int maxValue;
// expose an integer value property for the text field.
public int getValue() { return value.getValue(); }
public void setValue(int newValue) { value.setValue(newValue); }
public IntegerProperty valueProperty() { return value; }
IntField(int minValue, int maxValue, int initialValue) {
if (minValue > maxValue)
throw new IllegalArgumentException(
"IntField min value " + minValue + " greater than max value " + maxValue
);
if (maxValue < minValue)
throw new IllegalArgumentException(
"IntField max value " + minValue + " less than min value " + maxValue
);
if (!((minValue <= initialValue) && (initialValue <= maxValue)))
throw new IllegalArgumentException(
"IntField initialValue " + initialValue + " not between " + minValue + " and " + maxValue
);
// initialize the field values.
this.minValue = minValue;
this.maxValue = maxValue;
value = new SimpleIntegerProperty(initialValue);
setText(initialValue + "");
final IntField intField = this;
// make sure the value property is clamped to the required range
// and update the field's text to be in sync with the value.
value.addListener(new ChangeListener<Number>() {
@Override public void changed(ObservableValue<? extends Number> observableValue, Number oldValue, Number newValue) {
if (newValue == null) {
intField.setText("");
} else {
if (newValue.intValue() < intField.minValue) {
value.setValue(intField.minValue);
return;
}
if (newValue.intValue() > intField.maxValue) {
value.setValue(intField.maxValue);
return;
}
if (newValue.intValue() == 0 && (textProperty().get() == null || "".equals(textProperty().get()))) {
// no action required, text property is already blank, we don't need to set it to 0.
} else {
intField.setText(newValue.toString());
}
}
}
});
// restrict key input to numerals.
this.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() {
@Override public void handle(KeyEvent keyEvent) {
if (!"0123456789".contains(keyEvent.getCharacter())) {
keyEvent.consume();
}
}
});
// ensure any entered values lie inside the required range.
this.textProperty().addListener(new ChangeListener<String>() {
@Override public void changed(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
if (newValue == null || "".equals(newValue)) {
value.setValue(0);
return;
}
final int intValue = Integer.parseInt(newValue);
if (intField.minValue > intValue || intValue > intField.maxValue) {
textProperty().setValue(oldValue);
}
value.set(Integer.parseInt(textProperty().get()));
}
});
}
}
答案 3 :(得分:28)
从JavaFX 8u40开始,您可以在文本字段上设置TextFormatter对象:
let y = 5 as CGFloat
这可以避免在将更改侦听器添加到text属性并修改该侦听器中的文本时获得的子类化和重复更改事件。
答案 4 :(得分:24)
TextInput
有一个TextFormatter
,可用于格式化,转换和限制可输入的文本类型。
TextFormatter
有一个过滤器,可用于拒绝输入。我们需要将其设置为拒绝任何不是有效整数的内容。它还有一个转换器,我们需要将其设置为将字符串值转换为我们稍后可以绑定的整数值。
让我们创建一个可重用的过滤器:
public class IntegerFilter implements UnaryOperator<TextFormatter.Change> {
private final static Pattern DIGIT_PATTERN = Pattern.compile("\\d*");
@Override
public Change apply(TextFormatter.Change aT) {
return DIGIT_PATTERN.matcher(aT.getText()).matches() ? aT : null;
}
}
过滤器可以执行以下三种操作之一:它可以返回未修改的更改以接受它,它可以以它认为合适的某种方式更改更改,或者它可以返回null
以拒绝所有更改在一起。
我们将使用标准IntegerStringConverter
作为转换器。
总而言之,我们有:
TextField textField = ...;
TextFormatter<Integer> formatter = new TextFormatter<>(
new IntegerStringConverter(), // Standard converter form JavaFX
defaultValue,
new IntegerFilter());
formatter.valueProperty().bindBidirectional(myIntegerProperty);
textField.setTextFormatter(formatter);
如果你不想要一个可重复使用的过滤器,你可以做这个花哨的单行代替:
TextFormatter<Integer> formatter = new TextFormatter<>(
new IntegerStringConverter(),
defaultValue,
c -> Pattern.matches("\\d*", c.getText()) ? c : null );
答案 5 :(得分:21)
我不喜欢异常,因此我使用了String-Class
中的matches
函数
text.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue,
String newValue) {
if (newValue.matches("\\d*")) {
int value = Integer.parseInt(newValue);
} else {
text.setText(oldValue);
}
}
});
答案 6 :(得分:6)
TextField text = new TextField();
text.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
try {
Integer.parseInt(newValue);
if (newValue.endsWith("f") || newValue.endsWith("d")) {
manualPriceInput.setText(newValue.substring(0, newValue.length()-1));
}
} catch (ParseException e) {
text.setText(oldValue);
}
}
});
if
子句对于处理由Int.parseInt()正确解析的输入(如0.5d或0.7f)非常重要,但不应出现在文本字段中。
答案 7 :(得分:6)
从 Java SE 8u40 开始,出于这种需要,您可以使用“ integer ”Spinner
,以便通过使用键盘安全地选择有效整数箭头/向下箭头键或向上箭头/向下箭头提供的按钮。
您还可以定义 min , max 和初始值来限制允许的值以及增加或减少的数量,每一步。
例如
// Creates an integer spinner with 1 as min, 10 as max and 2 as initial value
Spinner<Integer> spinner1 = new Spinner<>(1, 10, 2);
// Creates an integer spinner with 0 as min, 100 as max and 10 as initial
// value and 10 as amount to increment or decrement by, per step
Spinner<Integer> spinner2 = new Spinner<>(0, 100, 10, 10);
带有“整数”微调器和“ double ”微调器的结果示例
的更多详情微调器是一个允许用户使用的单行文本字段控件 从有序序列中选择一个数字或一个对象值 值。旋转器通常提供一对小箭头按钮 单步执行序列的元素。键盘了 箭头/向下箭头键也循环通过元素。用户可以 也可以直接在微调器中键入(合法)值。 虽然组合框提供类似的功能,但是微调器是 有时候首选,因为它们不需要下拉列表 可以隐藏重要数据,也因为它们允许使用功能 例如从最大值换回到最小值 (例如,从最大正整数到0)。
答案 8 :(得分:4)
尝试使用这个简单的代码来完成这项工作。
DecimalFormat format = new DecimalFormat( "#.0" );
TextField field = new TextField();
field.setTextFormatter( new TextFormatter<>(c ->
{
if ( c.getControlNewText().isEmpty() )
{
return c;
}
ParsePosition parsePosition = new ParsePosition( 0 );
Object object = format.parse( c.getControlNewText(), parsePosition );
if ( object == null || parsePosition.getIndex() < c.getControlNewText().length() )
{
return null;
}
else
{
return c;
}
}));
答案 9 :(得分:3)
如果您使用Java 1.8 Lambdas
,那么优先答案可能会更小textfield.textProperty().addListener((observable, oldValue, newValue) -> {
if (newValue.matches("\\d*")) return;
textfield.setText(newValue.replaceAll("[^\\d]", ""));
});
答案 10 :(得分:3)
这个对我有用。
public void RestrictNumbersOnly(TextField tf){
tf.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue,
String newValue) {
if (!newValue.matches("|[-\\+]?|[-\\+]?\\d+\\.?|[-\\+]?\\d+\\.?\\d+")){
tf.setText(oldValue);
}
}
});
}
答案 11 :(得分:3)
如果你想将同一个监听器应用于多个TextField,这是最简单的解决方案:
TextField txtMinPrice, txtMaxPrice = new TextField();
ChangeListener<String> forceNumberListener = (observable, oldValue, newValue) -> {
if (!newValue.matches("\\d*"))
((StringProperty) observable).set(oldValue);
};
txtMinPrice.textProperty().addListener(forceNumberListener);
txtMaxPrice.textProperty().addListener(forceNumberListener);
答案 12 :(得分:2)
这就是我使用的:
private TextField textField;
textField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if(!newValue.matches("[0-9]*")){
textField.setText(oldValue);
}
}
});
lambda表示法中的相同内容是:
private TextField textField;
textField.textProperty().addListener((observable, oldValue, newValue) -> {
if(!newValue.matches("[0-9]*")){
textField.setText(oldValue);
}
});
答案 13 :(得分:2)
此方法允许TextField完成所有处理(复制/粘贴/撤消安全)。 不需要扩展类,并允许您决定在每次更改后如何处理新文本 (将其推送到逻辑,或转回以前的值,甚至修改它)。
// fired by every text property change
textField.textProperty().addListener(
(observable, oldValue, newValue) -> {
// Your validation rules, anything you like
// (! note 1 !) make sure that empty string (newValue.equals(""))
// or initial text is always valid
// to prevent inifinity cycle
// do whatever you want with newValue
// If newValue is not valid for your rules
((StringProperty)observable).setValue(oldValue);
// (! note 2 !) do not bind textProperty (textProperty().bind(someProperty))
// to anything in your code. TextProperty implementation
// of StringProperty in TextFieldControl
// will throw RuntimeException in this case on setValue(string) call.
// Or catch and handle this exception.
// If you want to change something in text
// When it is valid for you with some changes that can be automated.
// For example change it to upper case
((StringProperty)observable).setValue(newValue.toUpperCase());
}
);
对于你的情况,只需在里面添加这个逻辑。效果很好。
if (newValue.equals("")) return;
try {
Integer i = Integer.valueOf(newValue);
// do what you want with this i
} catch (Exception e) {
((StringProperty)observable).setValue(oldValue);
}
答案 14 :(得分:1)
这是一个简单的类,使用JavaFX 8u40中引入的TextField
处理TextFormatter
上的一些基本验证
编辑:
(代码添加了关于Floern的评论)
import java.text.DecimalFormatSymbols;
import java.util.regex.Pattern;
import javafx.beans.NamedArg;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
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("countOf") int countOf) {
this(modus.createPattern(countOf));
}
public TextFieldValidator(@NamedArg("regex") String regex) {
this(Pattern.compile(regex));
}
public TextFieldValidator(Pattern inputPattern) {
INPUT_PATTERN = inputPattern;
}
public static TextFieldValidator maxFractionDigits(int countOf) {
return new TextFieldValidator(maxFractionPattern(countOf));
}
public static TextFieldValidator maxIntegers(int countOf) {
return new TextFieldValidator(maxIntegerPattern(countOf));
}
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 countOf) {
return Pattern.compile("\\d*(\\" + DECIMAL_SEPARATOR + "\\d{0," + countOf + "})?");
}
private static Pattern maxCurrencyFractionPattern(int countOf) {
return Pattern.compile("^\\" + CURRENCY_SYMBOL + "?\\s?\\d*(\\" + DECIMAL_SEPARATOR + "\\d{0," + countOf + "})?\\s?\\" +
CURRENCY_SYMBOL + "?");
}
private static Pattern maxIntegerPattern(int countOf) {
return Pattern.compile("\\d{0," + countOf + "}");
}
private static Pattern integersOnlyPattern() {
return Pattern.compile("\\d*");
}
public enum ValidationModus {
MAX_CURRENCY_FRACTION_DIGITS {
@Override
public Pattern createPattern(int countOf) {
return maxCurrencyFractionPattern(countOf);
}
},
MAX_FRACTION_DIGITS {
@Override
public Pattern createPattern(int countOf) {
return maxFractionPattern(countOf);
}
},
MAX_INTEGERS {
@Override
public Pattern createPattern(int countOf) {
return maxIntegerPattern(countOf);
}
},
INTEGERS_ONLY {
@Override
public Pattern createPattern(int countOf) {
return integersOnlyPattern();
}
};
public abstract Pattern createPattern(int countOf);
}
}
你可以像这样使用它:
textField.setTextFormatter(new TextFieldValidator(ValidationModus.INTEGERS_ONLY).getFormatter());
或者您可以在fxml文件中对其进行实例化,并将其应用于具有相应属性的customTextField。
app.fxml:
<fx:define>
<TextFieldValidator fx:id="validator" modus="INTEGERS_ONLY"/>
</fx:define>
CustomTextField.class:
public class CustomTextField {
private TextField textField;
public CustomTextField(@NamedArg("validator") TextFieldValidator validator) {
this();
textField.setTextFormatter(validator.getFormatter());
}
}
答案 15 :(得分:1)
即使您尝试复制/粘贴,此代码也适用于我。
myTextField.textProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue.matches("\\d*")) {
myTextField.setText(oldValue);
}
});
答案 16 :(得分:1)
我想通过将Evan Knowles答案与Java fx 8中的文本格式化程序结合使用来帮助我的想法
textField.setTextFormatter(new TextFormatter<>(c -> {
if (!c.getControlNewText().matches("\\d*"))
return null;
else
return c;
}));
祝您好运;)保持冷静并编写Java代码
答案 17 :(得分:0)
嗯。 几周前我遇到了这个问题。由于API没有提供控制来实现这一点, 你可能想要使用自己的。我使用了类似的东西:
public class IntegerBox extends TextBox {
public-init var value : Integer = 0;
protected function apply() {
try {
value = Integer.parseInt(text);
} catch (e : NumberFormatException) {}
text = "{value}";
}
override var focused = false on replace {apply()};
override var action = function () {apply()}
}
它的使用方法与普通TextBox
相同
但是还有一个value
属性存储输入的整数
当控件失去焦点时,验证值并将其恢复(如果无效)。
答案 18 :(得分:0)
此代码使您的textField仅接受数字
textField.lengthProperty().addListener((observable, oldValue, newValue) -> {
if(newValue.intValue() > oldValue.intValue()){
char c = textField.getText().charAt(oldValue.intValue());
/** Check if the new character is the number or other's */
if( c > '9' || c < '0'){
/** if it's not number then just setText to previous one */
textField.setText(textField.getText().substring(0,textField.getText().length()-1));
}
}
});
答案 19 :(得分:0)
在JavaFX的最新更新中,您必须在Platform.runLater方法中设置新文本,如下所示:
private void set_normal_number(TextField textField, String oldValue, String newValue) {
try {
int p = textField.getCaretPosition();
if (!newValue.matches("\\d*")) {
Platform.runLater(() -> {
textField.setText(newValue.replaceAll("[^\\d]", ""));
textField.positionCaret(p);
});
}
} catch (Exception e) {
}
}
设置插入位置也是一个好主意。
答案 20 :(得分:0)
我想改进Evan Knowles的回答:https://stackoverflow.com/a/30796829/2628125
在我的情况下,我有一个用于UI组件部分的处理程序的类。初始化:
this.dataText.textProperty().addListener((observable, oldValue, newValue) -> this.numericSanitization(observable, oldValue, newValue));
还有numbericSanitization方法:
private synchronized void numericSanitization(ObservableValue<? extends String> observable, String oldValue, String newValue) {
final String allowedPattern = "\\d*";
if (!newValue.matches(allowedPattern)) {
this.dataText.setText(oldValue);
}
}
添加了关键字 synchronized ,以防止在旧的执行完成之前调用setText时javafx中可能的渲染锁定问题。如果你真的开始快速输入错误的字符,很容易重现。
另一个优点是你只保留一个匹配模式,只做回滚。它更好,因为您可以轻松地针对不同的清理模式对解决方案进行抽象化。
答案 21 :(得分:0)
rate_text.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
String s="";
for(char c : newValue.toCharArray()){
if(((int)c >= 48 && (int)c <= 57 || (int)c == 46)){
s+=c;
}
}
rate_text.setText(s);
}
});
这很好用,因为它允许你只输入整数值和十进制值(有ASCII码46)。
答案 22 :(得分:-1)
另一个非常简单的解决方案是:
TextField tf = new TextField();
tf.addEventFilter(KeyEvent.ANY, event -> {
if (!event.getCharacter().trim().matches("\\d?")) {
event.consume();
}
});
答案 23 :(得分:-2)
有点晚了,但如果你还包括小数:
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if (!newValue.matches("\\d{0,7}([\\.]\\d{0,4})?")) {
textField.setText(oldValue);
}
}