Ich muss die Eingabe in ein TextField auf ganze Zahlen beschränken. Irgendein Rat?
Ich muss die Eingabe in ein TextField auf ganze Zahlen beschränken. Irgendein Rat?
Antworten:
Sehr alter Thread, aber dieser scheint ordentlicher zu sein und entfernt nicht numerische Zeichen, wenn er eingefügt wird.
// 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]", ""));
}
}
});
\\D+
(oder nur \\D
) anstelle von verwenden können [^\\d]
, wenn Sie einige Zeichen speichern möchten.
Integer.parseInt(newValue)
und verwenden try
und catch
einen Fehler aufNumberFormatException
Update Apr 2016
Diese Antwort wurde vor einigen Jahren erstellt und die ursprüngliche Antwort ist jetzt weitgehend veraltet.
Seit Java 8u40 verfügt Java über einen TextFormatter, der normalerweise am besten geeignet ist, um die Eingabe bestimmter Formate wie z. B. Zahlen in JavaFX TextFields zu erzwingen:
Siehe auch andere Antworten auf diese Frage, in denen TextFormatter ausdrücklich erwähnt wird.
Ursprüngliche Antwort
Es gibt einige Beispiele dafür in diesem Kern , ich habe eines der folgenden Beispiele dupliziert:
// 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()));
}
});
}
}
Ich weiß, dass dies ein ziemlich alter Thread ist, aber für zukünftige Leser ist hier eine andere Lösung, die ich ziemlich intuitiv fand:
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]*");
}
}
Edit: Danke none_ und SCBoy für deine Verbesserungsvorschläge.
text.matches("\\d+");
Ab JavaFX 8u40 können Sie ein TextFormatter-Objekt für ein Textfeld festlegen:
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);
Dadurch werden sowohl Unterklassen- als auch doppelte Änderungsereignisse vermieden, die Sie erhalten, wenn Sie der Texteigenschaft einen Änderungslistener hinzufügen und den Text in diesem Listener ändern.
Das TextInput
hat ein TextFormatter
, mit dem die eingegebenen Textarten formatiert, konvertiert und begrenzt werden können.
Das TextFormatter
hat einen Filter, mit dem Eingaben verworfen werden können. Wir müssen dies festlegen, um alles abzulehnen, was keine gültige Ganzzahl ist. Es gibt auch einen Konverter, den wir einstellen müssen, um den Zeichenfolgenwert in einen ganzzahligen Wert zu konvertieren, den wir später binden können.
Erstellen wir einen wiederverwendbaren Filter:
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;
}
}
Der Filter kann eines von drei Dingen ausführen: Er kann die Änderung unverändert zurückgeben, um sie so zu akzeptieren, wie sie ist, er kann die Änderung auf eine Weise ändern, die er für geeignet hält, oder er kann zurückkehren null
, um die Änderung insgesamt abzulehnen.
Wir werden den Standard IntegerStringConverter
als Konverter verwenden.
Alles zusammen haben wir:
TextField textField = ...;
TextFormatter<Integer> formatter = new TextFormatter<>(
new IntegerStringConverter(), // Standard converter form JavaFX
defaultValue,
new IntegerFilter());
formatter.valueProperty().bindBidirectional(myIntegerProperty);
textField.setTextFormatter(formatter);
Wenn Sie keinen wiederverwendbaren Filter benötigen, können Sie stattdessen diesen ausgefallenen Einzeiler verwenden:
TextFormatter<Integer> formatter = new TextFormatter<>(
new IntegerStringConverter(),
defaultValue,
c -> Pattern.matches("\\d*", c.getText()) ? c : null );
Ich mag keine Ausnahmen, deshalb habe ich die matches
Funktion von String-Class verwendet
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);
}
}
});
textField.positionCaret(textField.getLength());
if (newValue.matches("\\d*") && newValue.getText().length < 5)
Wenn Sie die Eingabe in diesem Fall auf 4 Stellen beschränken möchten.
Ab Java SE 8u40 können Sie für diesen Bedarf eine " Ganzzahl " verwenden Spinner
, mit der Sie eine gültige Ganzzahl sicher auswählen können, indem Sie die Aufwärtspfeil- / Abwärtspfeiltasten der Tastatur oder die mit den Aufwärtspfeil- / Abwärtspfeil-Tasten verwenden.
Sie können auch einen Min- , einen Max- und einen Anfangswert definieren, um die zulässigen Werte und einen Betrag zu begrenzen, um den pro Schritt erhöht oder verringert werden soll.
Beispielsweise
// 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);
Beispiel eines Ergebnisses mit einem " Integer " -Spinner und einem " Double " -Spinner
Ein Drehfeld ist ein einzeiliges Textfeldsteuerelement, mit dem der Benutzer eine Zahl oder einen Objektwert aus einer geordneten Folge solcher Werte auswählen kann. Spinner bieten normalerweise ein Paar winziger Pfeiltasten zum Durchlaufen der Elemente der Sequenz. Die Aufwärtspfeil- / Abwärtspfeiltasten der Tastatur durchlaufen ebenfalls die Elemente. Dem Benutzer kann auch gestattet werden, einen (legalen) Wert direkt in den Spinner einzugeben. Obwohl Kombinationsfelder ähnliche Funktionen bieten, werden Spinner manchmal bevorzugt, weil sie keine Dropdown-Liste benötigen, die wichtige Daten verdecken kann, und weil sie Funktionen wie das Umbrechen vom Maximalwert zurück zum Minimalwert ermöglichen (z. von der größten positiven ganzen Zahl bis 0).
Weitere Details zur Spinner-Steuerung
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);
}
}
});
Die if
Klausel ist wichtig, um Eingaben wie 0.5d oder 0.7f zu verarbeiten, die von Int.parseInt () korrekt analysiert werden, aber nicht im Textfeld erscheinen sollten.
Versuchen Sie diesen einfachen Code, der die Arbeit erledigt.
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;
}
}));
Wenn Sie denselben Listener auf mehr als ein TextField anwenden möchten, ist dies die einfachste Lösung:
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);
Dieser hat für mich gearbeitet.
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);
}
}
});
}
Ich möchte mit meiner Idee helfen, Evan Knowles Antwort mit TextFormatter
JavaFX 8 zu kombinieren
textField.setTextFormatter(new TextFormatter<>(c -> {
if (!c.getControlNewText().matches("\\d*"))
return null;
else
return c;
}
));
also viel glück;) bleib ruhig und codiere java
Hier ist eine einfache Klasse, die einige grundlegende Validierungen TextField
mit verarbeitetTextFormatter
JavaFX 8u40 behandelt
BEARBEITEN:
(Code zu Floerns Kommentar hinzugefügt)
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);
}
}
Sie können es so verwenden:
textField.setTextFormatter(new TextFieldValidator(ValidationModus.INTEGERS_ONLY).getFormatter());
oder Sie können es in einer fxml-Datei instanziieren und auf ein customTextField mit den entsprechenden Eigenschaften anwenden.
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());
}
}
Das benutze ich:
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);
}
}
});
Das gleiche in der Lambda-Notation wäre:
private TextField textField;
textField.textProperty().addListener((observable, oldValue, newValue) -> {
if(!newValue.matches("[0-9]*")){
textField.setText(oldValue);
}
});
Mit dieser Methode kann TextField die gesamte Verarbeitung abschließen (Kopieren / Einfügen / Rückgängig machen). Es ist nicht erforderlich, Klassen zu erweitern, und Sie können nach jeder Änderung entscheiden, was mit neuem Text geschehen soll (um ihn auf Logik zu verschieben, zum vorherigen Wert zurückzukehren oder ihn sogar zu ändern).
// 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());
}
);
Fügen Sie für Ihren Fall einfach diese Logik hinzu. Funktioniert perfekt.
if (newValue.equals("")) return;
try {
Integer i = Integer.valueOf(newValue);
// do what you want with this i
} catch (Exception e) {
((StringProperty)observable).setValue(oldValue);
}
Mmmm. Ich bin vor Wochen auf dieses Problem gestoßen. Da die API keine Steuerung bietet, um dies zu erreichen,
möchten Sie möglicherweise eine eigene verwenden. Ich habe so etwas benutzt wie:
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()}
}
Es wird genauso verwendet wie ein normales TextBox
, hat
aber auch ein value
Attribut, das die eingegebene Ganzzahl speichert.
Wenn das Steuerelement den Fokus verliert, es bestätigt den Wert und kehrt es (wenn nicht gültig ist).
Dieser Code Lassen Sie Ihr Textfeld nur eine Nummer akzeptieren
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));
}
}
});
Dieser Code funktioniert auch dann gut, wenn Sie versuchen, ihn zu kopieren / einzufügen.
myTextField.textProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue.matches("\\d*")) {
myTextField.setText(oldValue);
}
});
In den letzten Aktualisierungen von JavaFX müssen Sie neuen Text in der Platform.runLater-Methode wie folgt festlegen:
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) {
}
}
Es ist eine gute Idee, auch die Caret-Position festzulegen.
Platform.runLater
erforderlich ist?
Ich möchte Evan Knowles Antwort verbessern: https://stackoverflow.com/a/30796829/2628125
In meinem Fall hatte ich eine Klasse mit Handlern für den UI-Komponententeil. Initialisierung:
this.dataText.textProperty().addListener((observable, oldValue, newValue) -> this.numericSanitization(observable, oldValue, newValue));
Und die numbericSanitization-Methode:
private synchronized void numericSanitization(ObservableValue<? extends String> observable, String oldValue, String newValue) {
final String allowedPattern = "\\d*";
if (!newValue.matches(allowedPattern)) {
this.dataText.setText(oldValue);
}
}
Schlüsselwort synchronisiert wird hinzugefügt, um mögliche Probleme mit der Render-Sperre in javafx zu vermeiden, wenn setText aufgerufen wird, bevor die Ausführung des alten abgeschlossen ist. Es ist einfach zu reproduzieren, wenn Sie sehr schnell falsche Zeichen eingeben.
Ein weiterer Vorteil ist, dass Sie nur ein Muster beibehalten und nur einen Rollback durchführen. Es ist besser, weil Sie die Lösung für verschiedene Desinfektionsmuster leicht abstragieren können.
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);
}
});
Dies funktioniert einwandfrei, da Sie nur einen ganzzahligen Wert und einen Dezimalwert eingeben können (mit ASCII-Code 46).