Fil très ancien, mais cela semble plus net et supprime les caractères non numériques s'il est collé.
// force the field to be numeric only
textField.textProperty().addListener(new ChangeListener<String>() {
@Overridepublicvoidchanged(ObservableValue<? extends String> observable, String oldValue,
String newValue){
if (!newValue.matches("\\d*")) {
textField.setText(newValue.replaceAll("[^\\d]", ""));
}
}
});
Fonctionne à merveille. Notez que vous pouvez également utiliser \\D+(ou simplement \\D) à la place de [^\\d], si vous souhaitez enregistrer quelques caractères.
ricky3350
5
Encore plus simple est de redéfinir le texte sur oldValue. De cette façon, vous n'avez pas à vous soucier des remplacements de regex (ce qui n'a malheureusement pas fonctionné pour moi).
geisterfurz007
@ geisterfurz007 Cela donne la possibilité de supprimer les éléments non numériques du texte collé.
Evan Knowles le
10
Obsolète, voir la réponse TextFormatter ci-dessous.
gerardw
3
Pourrait également utiliser Integer.parseInt(newValue)et utiliser tryet catchdétecter une erreur surNumberFormatException
Pixel
43
Mise à jour avril 2016
Cette réponse a été créée il y a quelques années et la réponse originale est aujourd'hui largement obsolète.
Depuis Java 8u40, Java a un TextFormatter qui est généralement le meilleur pour appliquer la saisie de formats spécifiques tels que les nombres sur JavaFX TextFields:
Voir également d'autres réponses à cette question qui mentionnent spécifiquement TextFormatter.
Réponse originale
Il y a quelques exemples de cela dans cet essentiel , j'ai dupliqué l'un des exemples ci-dessous:
// 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.classIntFieldextendsTextField{
finalprivate IntegerProperty value;
finalprivateint minValue;
finalprivateint maxValue;
// expose an integer value property for the text field.publicintgetValue(){ return value.getValue(); }
publicvoidsetValue(int newValue){ value.setValue(newValue); }
public IntegerProperty valueProperty(){ return value; }
IntField(int minValue, int maxValue, int initialValue) {
if (minValue > maxValue)
thrownew IllegalArgumentException(
"IntField min value " + minValue + " greater than max value " + maxValue
);
if (maxValue < minValue)
thrownew IllegalArgumentException(
"IntField max value " + minValue + " less than min value " + maxValue
);
if (!((minValue <= initialValue) && (initialValue <= maxValue)))
thrownew 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>() {
@Overridepublicvoidchanged(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>() {
@Overridepublicvoidhandle(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>() {
@Overridepublicvoidchanged(ObservableValue<? extends String> observableValue, String oldValue, String newValue){
if (newValue == null || "".equals(newValue) || (intField.minValue<0 && "-".equals(newValue))) {
value.setValue(0);
return;
}
finalint intValue = Integer.parseInt(newValue);
if (intField.minValue > intValue || intValue > intField.maxValue) {
textProperty().setValue(oldValue);
}
value.set(Integer.parseInt(textProperty().get()));
}
});
}
}
Quelqu'un sait-il quelle expression régulière utiliser pour les nombres décimaux (par exemple 123,456)? La valeur du paramètre de fonction de validation (texte) "texte" est le dernier caractère édité par l'utilisateur, et non la chaîne entière dans le champ de texte, par conséquent l'expression régulière peut ne pas correspondre correctement. Par exemple, j'utilise cette commande comme ceci: text.matches("\\d+"); et je ne peux supprimer aucun caractère dans le champ de texte
Je pense que c'est une bonne solution, mais je changerais une chose. Au lieu d'écrire text.matches ("[0-9] *"), je créerais une variable pour le motif et la compilerais. Dans votre code, le modèle est compilé à chaque fois que quelqu'un écrit un caractère dans un champ, ce qui est coûteux pour le processeur et la mémoire. J'ajouterais donc la variable: Private static Pattern integerPattern = Pattern.compile ("[0-9] *"); Et remplacez le corps de validation par: integerPattern.matcher (text) .matches ();
P. Jowko
38
À partir de JavaFX 8u40, vous pouvez définir un objet TextFormatter sur un champ de texte:
UnaryOperator<Change> filter = change -> {
String text = change.getText();
if (text.matches("[0-9]*")) {
return change;
}
returnnull;
};
TextFormatter<String> textFormatter = new TextFormatter<>(filter);
fieldNport = new TextField();
fieldNport.setTextFormatter(textFormatter);
Cela évite à la fois de sous-classer et de dupliquer les événements de modification que vous obtiendrez lorsque vous ajoutez un écouteur de changement à la propriété text et modifiez le texte dans cet écouteur.
Le TextInputa unTextFormatter qui peut être utilisé pour formater, convertir et limiter les types de texte qui peuvent être saisis.
le TextFormatter a un filtre qui peut être utilisé pour rejeter l'entrée. Nous devons définir ceci pour rejeter tout ce qui n'est pas un entier valide. Il a également un convertisseur que nous devons définir pour convertir la valeur de la chaîne en une valeur entière que nous pouvons lier plus tard.
Le filtre peut faire l'une des trois choses suivantes, il peut renvoyer le changement non modifié pour l'accepter tel quel, il peut modifier le changement d'une manière qu'il juge appropriée ou il peut revenir null pour rejeter le changement dans son ensemble.
TextField textField = ...;
TextFormatter<Integer> formatter = new TextFormatter<>(
new IntegerStringConverter(), // Standard converter form JavaFX
defaultValue,
new IntegerFilter());
formatter.valueProperty().bindBidirectional(myIntegerProperty);
textField.setTextFormatter(formatter);
Si vous ne voulez pas avoir besoin d'un filtre réutilisable, vous pouvez faire cette fantaisie à une seule ligne à la place:
TextFormatter<Integer> formatter = new TextFormatter<>(
new IntegerStringConverter(),
defaultValue,
c -> Pattern.matches("\\d*", c.getText()) ? c : null );
parfois vous avez une exception, car le nuber peut être trop grand.
schlagi123
1
Astuce: remplacez l'expression régulière "\\ d +" par "\\ d *". Cela vous permettra de supprimer tous les caractères du champ de saisie de texte (utilisez retour arrière / suppression pour effacer le champ).
Guus
1
N'oubliez pas de régler la position du curseur après l'avoir remise à l'ancienne valeur:textField.positionCaret(textField.getLength());
hsson
Remplacez la première condition if par: if (newValue.matches("\\d*") && newValue.getText().length < 5) si vous souhaitez limiter l'entrée à 4 chiffres dans ce cas.
Cappuccino90
@ Cappuccino90 Vous devriez changer les instructions car la vérification de la longueur est beaucoup moins chère à chaque hit que l'analyse d'une regex
thatsIch
9
A partir de Java SE 8u40 , pour un tel besoin vous pouvez utiliser un " entier " Spinnerpermettant de sélectionner en toute sécurité un entier valide en utilisant les touches fléchées haut / bas du clavier ou les boutons fléchés haut / bas fournis.
Vous pouvez également définir un min , un max et une valeur initiale pour limiter les valeurs autorisées et un montant à incrémenter ou décrémenter de, par étape.
Par exemple
// 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);
Exemple de résultat avec un spinner " entier " et un spinner " double "
Une double flèche est un contrôle de champ de texte sur une seule ligne qui permet à l'utilisateur de sélectionner un nombre ou une valeur d'objet dans une séquence ordonnée de ces valeurs. Les spinners fournissent généralement une paire de minuscules boutons fléchés pour parcourir les éléments de la séquence. Les touches fléchées haut / bas du clavier font également défiler les éléments. L'utilisateur peut également être autorisé à saisir une valeur (légale) directement dans le spinner. Bien que les listes déroulantes fournissent des fonctionnalités similaires, les filateurs sont parfois préférés car ils ne nécessitent pas de liste déroulante qui peut masquer des données importantes, et aussi parce qu'ils permettent des fonctionnalités telles que le retour de la valeur maximale à la valeur minimale (par exemple, du plus grand entier positif à 0).
La ifclause est importante pour gérer les entrées comme 0.5d ou 0.7f qui sont correctement analysées par Int.parseInt (), mais ne devraient pas apparaître dans le champ de texte.
Woot? Depuis quand 0.5d est-il correctement analysé par Integer.parseInt ()?! Il lance une exception java.lang.NumberFormatException: Pour la chaîne d'entrée: "0.5d" (comme prévu, car ce n'est pas un entier)
Burkhard
4
Essayez ce code simple, il fera l'affaire.
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() )
{
returnnull;
}
else
{
return c;
}
}));
Cette méthode permet à TextField de terminer tout le traitement (copier / coller / annuler en toute sécurité). Ne nécessite pas d'étendre les classes et vous permet de décider quoi faire avec le nouveau texte après chaque changement (pour le pousser à la logique, ou revenir à la valeur précédente, ou même pour le modifier).
// 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());
}
);
Pour votre cas, ajoutez simplement cette logique à l'intérieur. Fonctionne parfaitement.
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. J'ai rencontré ce problème il y a des semaines. Comme l'API ne fournit pas de contrôle pour y parvenir,
vous pouvez utiliser le vôtre. J'ai utilisé quelque chose comme:
publicclassIntegerBoxextendsTextBox{
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()}
}
Il est utilisé de la même manière qu'une normale TextBox,
mais possède également un valueattribut qui stocke l'entier saisi.
Lorsque le contrôle perd le focus, il valide la valeur et la rétablit (si elle n'est pas valide).
ce code Rendre votre texte Champ Accepter uniquement Numéro
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));
}
}
});
privatesynchronizedvoidnumericSanitization(ObservableValue<? extends String> observable, String oldValue, String newValue){
final String allowedPattern = "\\d*";
if (!newValue.matches(allowedPattern)) {
this.dataText.setText(oldValue);
}
}
Mot-clé synchronisé est ajouté pour éviter un éventuel problème de verrouillage de rendu dans javafx si setText sera appelé avant que l'ancien ne soit terminé. Il est facile à reproduire si vous commencez à taper très rapidement de mauvais caractères.
Un autre avantage est que vous ne conservez qu'un seul modèle pour correspondre et effectuez simplement une restauration. C'est mieux car vous pouvez facilement résumer la solution pour différents modèles de désinfection.
Réponses:
Fil très ancien, mais cela semble plus net et supprime les caractères non numériques s'il est collé.
// 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]", "")); } } });
la source
\\D+
(ou simplement\\D
) à la place de[^\\d]
, si vous souhaitez enregistrer quelques caractères.Integer.parseInt(newValue)
et utilisertry
etcatch
détecter une erreur surNumberFormatException
Mise à jour avril 2016
Cette réponse a été créée il y a quelques années et la réponse originale est aujourd'hui largement obsolète.
Depuis Java 8u40, Java a un TextFormatter qui est généralement le meilleur pour appliquer la saisie de formats spécifiques tels que les nombres sur JavaFX TextFields:
Voir également d'autres réponses à cette question qui mentionnent spécifiquement TextFormatter.
Réponse originale
Il y a quelques exemples de cela dans cet essentiel , j'ai dupliqué l'un des exemples ci-dessous:
// 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())); } }); } }
la source
Je sais que c'est un fil assez ancien, mais pour les futurs lecteurs, voici une autre solution que j'ai trouvée assez intuitive:
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: Merci none_ et SCBoy pour vos suggestions d'améliorations.
la source
text.matches("\\d+");
et je ne peux supprimer aucun caractère dans le champ de texteÀ partir de JavaFX 8u40, vous pouvez définir un objet TextFormatter sur un champ de texte:
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);
Cela évite à la fois de sous-classer et de dupliquer les événements de modification que vous obtiendrez lorsque vous ajoutez un écouteur de changement à la propriété text et modifiez le texte dans cet écouteur.
la source
Le
TextInput
a unTextFormatter
qui peut être utilisé pour formater, convertir et limiter les types de texte qui peuvent être saisis.le
TextFormatter
a un filtre qui peut être utilisé pour rejeter l'entrée. Nous devons définir ceci pour rejeter tout ce qui n'est pas un entier valide. Il a également un convertisseur que nous devons définir pour convertir la valeur de la chaîne en une valeur entière que nous pouvons lier plus tard.Permet de créer un filtre réutilisable:
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; } }
Le filtre peut faire l'une des trois choses suivantes, il peut renvoyer le changement non modifié pour l'accepter tel quel, il peut modifier le changement d'une manière qu'il juge appropriée ou il peut revenir
null
pour rejeter le changement dans son ensemble.Nous utiliserons la norme
IntegerStringConverter
comme convertisseur.En mettant tout cela ensemble, nous avons:
TextField textField = ...; TextFormatter<Integer> formatter = new TextFormatter<>( new IntegerStringConverter(), // Standard converter form JavaFX defaultValue, new IntegerFilter()); formatter.valueProperty().bindBidirectional(myIntegerProperty); textField.setTextFormatter(formatter);
Si vous ne voulez pas avoir besoin d'un filtre réutilisable, vous pouvez faire cette fantaisie à une seule ligne à la place:
TextFormatter<Integer> formatter = new TextFormatter<>( new IntegerStringConverter(), defaultValue, c -> Pattern.matches("\\d*", c.getText()) ? c : null );
la source
Je n'aime pas les exceptions donc j'ai utilisé la
matches
fonction de String-Classtext.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); } } });
la source
textField.positionCaret(textField.getLength());
if (newValue.matches("\\d*") && newValue.getText().length < 5)
si vous souhaitez limiter l'entrée à 4 chiffres dans ce cas.A partir de Java SE 8u40 , pour un tel besoin vous pouvez utiliser un " entier "
Spinner
permettant de sélectionner en toute sécurité un entier valide en utilisant les touches fléchées haut / bas du clavier ou les boutons fléchés haut / bas fournis.Vous pouvez également définir un min , un max et une valeur initiale pour limiter les valeurs autorisées et un montant à incrémenter ou décrémenter de, par étape.
Par exemple
// 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);
Exemple de résultat avec un spinner " entier " et un spinner " double "
Plus de détails sur le contrôle Spinner
la source
La réponse préférée peut être encore plus petite si vous utilisez Java 1.8 Lambdas
textfield.textProperty().addListener((observable, oldValue, newValue) -> { if (newValue.matches("\\d*")) return; textfield.setText(newValue.replaceAll("[^\\d]", "")); });
la source
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); } } });
La
if
clause est importante pour gérer les entrées comme 0.5d ou 0.7f qui sont correctement analysées par Int.parseInt (), mais ne devraient pas apparaître dans le champ de texte.la source
Essayez ce code simple, il fera l'affaire.
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; } }));
la source
Si vous souhaitez appliquer le même écouteur à plusieurs TextField, voici la solution la plus simple:
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);
la source
Celui-ci a fonctionné pour moi.
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); } } }); }
la source
Je veux aider avec mon idée de combiner la réponse Evan Knowles avec
TextFormatter
de JavaFX 8textField.setTextFormatter(new TextFormatter<>(c -> { if (!c.getControlNewText().matches("\\d*")) return null; else return c; } ));
donc bonne chance;) restez calme et codez java
la source
Voici une classe simple qui gère quelques validations de base sur
TextField
, en utilisantTextFormatter
introduit dans JavaFX 8u40ÉDITER:
(Code ajouté concernant le commentaire de 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); } }
Vous pouvez l'utiliser comme ceci:
textField.setTextFormatter(new TextFieldValidator(ValidationModus.INTEGERS_ONLY).getFormatter());
ou vous pouvez l'instancier dans un fichier fxml et l'appliquer à un customTextField avec les propriétés correspondantes.
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()); } }
Code sur github
la source
Voici ce que j'utilise:
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); } } });
La même chose en notation lambda serait:
private TextField textField; textField.textProperty().addListener((observable, oldValue, newValue) -> { if(!newValue.matches("[0-9]*")){ textField.setText(oldValue); } });
la source
Cette méthode permet à TextField de terminer tout le traitement (copier / coller / annuler en toute sécurité). Ne nécessite pas d'étendre les classes et vous permet de décider quoi faire avec le nouveau texte après chaque changement (pour le pousser à la logique, ou revenir à la valeur précédente, ou même pour le modifier).
// 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()); } );
Pour votre cas, ajoutez simplement cette logique à l'intérieur. Fonctionne parfaitement.
if (newValue.equals("")) return; try { Integer i = Integer.valueOf(newValue); // do what you want with this i } catch (Exception e) { ((StringProperty)observable).setValue(oldValue); }
la source
Mmmm. J'ai rencontré ce problème il y a des semaines. Comme l'API ne fournit pas de contrôle pour y parvenir,
vous pouvez utiliser le vôtre. J'ai utilisé quelque chose comme:
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()} }
Il est utilisé de la même manière qu'une normale
TextBox
,mais possède également un
value
attribut qui stocke l'entier saisi.Lorsque le contrôle perd le focus, il valide la valeur et la rétablit (si elle n'est pas valide).
la source
ce code Rendre votre texte Champ Accepter uniquement Numéro
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)); } } });
la source
Ce code fonctionne très bien pour moi même si vous essayez de copier / coller.
myTextField.textProperty().addListener((observable, oldValue, newValue) -> { if (!newValue.matches("\\d*")) { myTextField.setText(oldValue); } });
la source
Dans les mises à jour récentes de JavaFX, vous devez définir un nouveau texte dans la méthode Platform.runLater comme ceci:
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) { } }
C'est aussi une bonne idée de définir la position du curseur.
la source
Platform.runLater
est nécessaire?Je voudrais améliorer la réponse d'Evan Knowles: https://stackoverflow.com/a/30796829/2628125
Dans mon cas, j'avais une classe avec des gestionnaires pour la partie UI Component. Initialisation:
this.dataText.textProperty().addListener((observable, oldValue, newValue) -> this.numericSanitization(observable, oldValue, newValue));
Et la méthode 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); } }
Mot-clé synchronisé est ajouté pour éviter un éventuel problème de verrouillage de rendu dans javafx si setText sera appelé avant que l'ancien ne soit terminé. Il est facile à reproduire si vous commencez à taper très rapidement de mauvais caractères.
Un autre avantage est que vous ne conservez qu'un seul modèle pour correspondre et effectuez simplement une restauration. C'est mieux car vous pouvez facilement résumer la solution pour différents modèles de désinfection.
la source
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); } });
Cela fonctionne bien car cela vous permet de saisir uniquement une valeur entière et une valeur décimale (ayant le code ASCII 46).
la source