J'ai besoin de restreindre l'entrée dans un TextField aux entiers. Aucun conseil?
J'ai besoin de restreindre l'entrée dans un TextField aux entiers. Aucun conseil?
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]", ""));
}
}
});
\\D+
(ou simplement \\D
) à la place de [^\\d]
, si vous souhaitez enregistrer quelques caractères.
Integer.parseInt(newValue)
et utiliser try
et catch
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()));
}
});
}
}
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.
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.
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 );
Je n'aime pas les exceptions donc j'ai utilisé la matches
fonction de String-Class
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)
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 "
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).
Plus de détails sur le contrôle Spinner
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.
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;
}
}));
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);
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);
}
}
});
}
Je veux aider avec mon idée de combiner la réponse Evan Knowles avec TextFormatter
de JavaFX 8
textField.setTextFormatter(new TextFormatter<>(c -> {
if (!c.getControlNewText().matches("\\d*"))
return null;
else
return c;
}
));
donc bonne chance;) restez calme et codez java
Voici une classe simple qui gère quelques validations de base sur TextField
, en utilisant TextFormatter
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());
}
}
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);
}
});
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:
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).
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));
}
}
});
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);
}
});
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.
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.
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).