Input 在JavaFX中创建数字文本字段的推荐方法是什么?

Input 在JavaFX中创建数字文本字段的推荐方法是什么?,input,filter,textfield,javafx,Input,Filter,Textfield,Javafx,我需要将文本字段的输入限制为整数。有什么建议吗?嗯我几周前遇到了这个问题。由于API没有提供实现这一点的控件, 您可能想使用您自己的。我使用了类似于: public class IntegerBox extends TextBox { public-init var value : Integer = 0; protected function apply() { try { value = Integer.parseInt(text);

我需要将文本字段的输入限制为整数。有什么建议吗?

我几周前遇到了这个问题。由于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()}
}
它的使用方式与普通的
文本框

但是还有一个
属性,用于存储输入的整数。
当控件失去焦点时,它将验证该值并将其还原(如果无效)。

TextField text=new TextField();
text.textProperty().addListener(新的ChangeListener()){
@凌驾
公共无效变更(可观察值2016年4月更新

这个答案是几年前创建的,而原来的答案现在基本上已经过时了

自Java 8u40以来,Java有一个用于强制输入特定格式(如JavaFX文本字段上的数字)的最佳方法:

另请参阅此问题的其他答案,其中特别提到TextFormatter


原始答案

这里有一些例子,我重复了下面的一个例子:

// 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(intField.minValue<0) {
                if (!"-0123456789".contains(keyEvent.getCharacter())) {
                    keyEvent.consume();
                }
            }
            else {
                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) || (intField.minValue<0 && "-".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()));
      }
    });
  }
}
//帮助器文本字段子类,它将文本输入限制在给定的自然整数范围内
//并将编辑框的当前数值int值作为值属性公开。
类IntField扩展了TextField{
最终私有IntegerProperty值;
最终私有值;
最终私有整数最大值;
//公开文本字段的整数值属性。
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){
如果(最小值>最大值)
抛出新的IllegalArgumentException(
“IntField最小值”+minValue+“大于最大值”+maxValue
);
如果(最大值<最小值)
抛出新的IllegalArgumentException(
“IntField最大值”+最小值+“小于最小值”+最大值
);

如果(!((minValue我知道这是一个相当古老的线程,但对于未来的读者,我发现另一个解决方案非常直观:

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]*");
    }
}

编辑:感谢并感谢您提出的改进建议。

我不喜欢例外情况,因此我使用了String类中的
匹配
函数

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);
        }
    }
});
text.textProperty().addListener(新的ChangeListener()){
@凌驾

public void changed(observeValue此方法允许TextField完成所有处理(复制/粘贴/撤消安全)。 不需要扩展类,并允许您在每次更改后决定如何处理新文本 (将其推送到逻辑,或返回到上一个值,甚至修改它)

对于您的情况,只需在内部添加此逻辑即可。效果完美

   if (newValue.equals("")) return; 
   try {
     Integer i = Integer.valueOf(newValue);
     // do what you want with this i
   } catch (Exception e) {
     ((StringProperty)observable).setValue(oldValue);
   }

非常旧的线程,但这看起来更整洁,如果粘贴,会去掉非数字字符

// 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]", ""));
        }
    }
});
//强制该字段仅为数字
textField.textProperty().addListener(新的ChangeListener()){
@凌驾

public void changed(observeValue从JavaFX 8u40开始,您可以在文本字段上设置TextFormatter对象:

UnaryOperator<Change> filter = change -> {
    String text = change.getText();

    if (text.matches("[0-9]*")) {
        return change;
    }

    return null;
};
TextFormatter<String> textFormatter = new TextFormatter<>(filter);
fieldNport = new TextField();
fieldNport.setTextFormatter(textFormatter);
UnaryOperator筛选器=更改->{
String text=change.getText();
if(text.matches(“[0-9]*”)){
回报变化;
}
返回null;
};
TextFormatter TextFormatter=新的TextFormatter(过滤器);
fieldNport=newtextfield();
fieldNport.setTextFormatter(textFormatter);

这避免了将更改侦听器添加到text属性并修改该侦听器中的文本时将得到的子类化和重复更改事件。

下面是一个简单的类,它使用JavaFX 8u40中引入的
TextFormatter
处理
TextField
上的一些基本验证

编辑:

(增加了有关弗洛恩评论的代码)

或者,您可以在fxml文件中实例化它,并将其应用于具有相应属性的customTextField

app.fxml:

<fx:define>
    <TextFieldValidator fx:id="validator" modus="INTEGERS_ONLY"/>
</fx:define>

这个对我很有用

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);
            }
        }
    });
}
public void restricts numbers only(TextField){
tf.textProperty().addListener(新的ChangeListener()){
@凌驾

public void changed(observeValue该
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;
    }
}
如果您不需要可重复使用的过滤器,您可以使用这种奇特的单层过滤器:

TextFormatter<Integer> formatter = new TextFormatter<>(
    new IntegerStringConverter(), 
    defaultValue,  
    c -> Pattern.matches("\\d*", c.getText()) ? c : null );
TextFormatter formatter=新的TextFormatter(
新的IntegerStringConverter(),
默认值,
c->Pattern.matches(“\\d*”,c.getText())?c:null);

此代码使您的文本字段只接受数字

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));
            }
        }
    });
textField.lengthProperty().addListener((可观察、旧值、新值)->{
if(newValue.intValue()>oldValue.intValue()){
char c=textField.getText().charAt(oldValue.intValue());
/**检查新字符是数字还是其他字符*/
如果(c>'9'| | c<'0'){
/**如果不是数字,则将文本设置为上一个*/
setText(textField.getText().substring(0,textField.getText().length()-1));
}
}
});
这就是我使用的:

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);
        }

    }
});

如果要将同一侦听器应用于多个文本字段,以下是最简单的解决方案:

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);
TextField txtMinPrice,txtMaxPrice=new TextField();
ChangeListener forceNumberListener=(可观察、旧值、新值)->{
如果(!newValue.matches(\\d*))
((StringProperty)可观察)。设置(oldValue);
};
txtMinPrice.textProperty().addListen
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;
    }
}
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 );
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));
            }
        }
    });
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);
        }

    }
});
private TextField textField;
textField.textProperty().addListener((observable, oldValue, newValue) -> {
    if(!newValue.matches("[0-9]*")){
        textField.setText(oldValue);
    }
});
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);
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;
    }
}));
textfield.textProperty().addListener((observable, oldValue, newValue) -> {
    if (newValue.matches("\\d*")) return;
    textfield.setText(newValue.replaceAll("[^\\d]", ""));
});
// 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);
    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) {
    }
}
this.dataText.textProperty().addListener((observable, oldValue, newValue) -> this.numericSanitization(observable, oldValue, newValue));
private synchronized void numericSanitization(ObservableValue<? extends String> observable, String oldValue, String newValue) {
    final String allowedPattern = "\\d*";

    if (!newValue.matches(allowedPattern)) {
        this.dataText.setText(oldValue);
    }
}
myTextField.textProperty().addListener((observable, oldValue, newValue) -> {
    if (!newValue.matches("\\d*")) {
        myTextField.setText(oldValue);

    }
});
  public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
            if (!newValue.matches("\\d{0,7}([\\.]\\d{0,4})?")) {
                textField.setText(oldValue);
            }
        }
    TextField tf = new TextField();
    tf.addEventFilter(KeyEvent.ANY, event -> {
        if (!event.getCharacter().trim().matches("\\d?")) {
            event.consume();
        }
    });