Je dois limiter l'entrée dans un champ de texte aux entiers. Aucun conseil?
Très vieux fil, mais cela semble plus ordonné 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]", ""));
}
}
});
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 les améliorations suggérées.
Mise à jour avril 2016
Cette réponse a été créée il y a quelques années et la réponse d'origine est en grande partie obsolète maintenant.
Depuis Java 8u40, Java a un TextFormatter _ qui convient généralement mieux pour imposer la saisie de formats spécifiques tels que les valeurs numériques sur JavaFX TextFields:
Voir aussi les autres réponses à cette question qui mentionnent spécifiquement TextFormatter.
Réponse originale
Il y a quelques exemples de cela dans ce Gist , 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 (!"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()));
}
});
}
}
La TextInput
a une TextFormatter
qui peut être utilisée pour formater, convertir et limiter les types de texte pouvant être saisis.
La TextFormatter
a un filtre qui peut être utilisé pour rejeter une entrée. Nous devons définir ceci pour rejeter tout ce qui n'est pas un entier valide. Il possède également un convertisseur que nous devons définir pour convertir la valeur de chaîne en valeur entière que nous pourrons lier ultérieurement.
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 effectuer l'une des trois opérations suivantes: il peut renvoyer le changement sans modification et l'accepter tel quel, il peut modifier le changement de la manière qu'il juge appropriée ou renvoyer null
pour le refuser complètement.
Nous utiliserons le standard IntegerStringConverter
en tant que convertisseur.
En réunissant tout cela, 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 souhaitez ne pas avoir besoin d'un filtre réutilisable, vous pouvez utiliser cette option à la place:
TextFormatter<Integer> formatter = new TextFormatter<>(
new IntegerStringConverter(),
defaultValue,
c -> Pattern.matches("\\d*", c.getText()) ? c : null );
À 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 les événements de modification de sous-classe et de duplication que vous obtiendrez lorsque vous ajoutez un écouteur de modification à la propriété text et que vous modifiez le texte dans cet écouteur.
Je n'aime pas les exceptions et j'ai donc utilisé la fonction matches
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 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 clause if
est importante pour gérer des entrées telles que 0.5d ou 0.7f qui sont correctement analysées par Int.parseInt (), mais ne doivent pas apparaître dans le champ de texte.
À partir de Java SE 8u40, vous pouvez utiliser un "entier" Spinner
pour sélectionner en toute sécurité un entier valide à l’aide des touches fléchées haut/bas du clavier ou flèches haut/bas fournies.
Vous pouvez également définir une valeur min, max et initiale pour limiter les valeurs autorisées et un montant à incrémenter ou à diminuer.
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"
Un spinner est un contrôle de champ de texte d'une seule ligne qui permet à l'utilisateur sélectionner un nombre ou une valeur d'objet dans une séquence ordonnée de ce type valeurs. Les filateurs fournissent généralement une paire de minuscules boutons fléchés pour parcourir les éléments de la séquence. Le clavier est en place Les touches fléchées/bas permettent également de parcourir les éléments. L'utilisateur peut également être autorisé à taper une valeur (légale) directement dans la roulette . Bien que les listes déroulantes offrent des fonctionnalités similaires, les centrifugeuses sont parfois préféré car ils ne nécessitent pas une liste déroulante que peut masquer des données importantes, et aussi parce qu’elles permettent des fonctionnalités tels que le retour à la ligne de la valeur maximale à la valeur minimale (par exemple, du plus grand entier positif à 0).
Plus de détails sur le contrôle Spinner
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*")) {
textfield.setText(newValue.replaceAll("[^\\d]", ""));
}
});
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);
}
}
});
}
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);
Essayez ce code simple, il fera le travail.
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;
}
}));
Cette méthode permet à TextField de terminer tous les traitements (copier/coller/annuler sûr) . Ne nécessite pas d’extension de classes et vous permet de décider quoi faire du nouveau texte après chaque modification ou revenir à la valeur précédente, ou même la 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. Marche 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);
}
C'est 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);
}
});
Voici une classe simple qui gère certaines validations de base sur TextField
, en utilisant TextFormatter
introduit dans JavaFX 8u40
MODIFIER:
(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 xml 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());
}
}
Ce code fonctionne bien pour moi même si vous essayez de copier/coller.
myTextField.textProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue.matches("\\d*")) {
myTextField.setText(oldValue);
}
});
je veux aider avec mon idée de combiner Evan Knowles répondre au format texte de Javafx 8
textField.setTextFormatter(new TextFormatter<>(c -> {
if (!c.getControlNewText().matches("\\d*"))
return null;
else
return c;
}));
alors bonne chance;) garde ton calme et code Java
Dans les dernières mises à jour de JavaFX, vous devez définir un nouveau texte dans la méthode Platform.runLater comme suit:
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.
ce code Faites votre textField Accepter seulement le nombre
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));
}
}
});
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 d’entrer uniquement la valeur entière et la valeur décimale (code ASCII 46).
J'aimerais améliorer la réponse de Evan Knowles: https://stackoverflow.com/a/30796829/2628125
Dans mon cas, j'ai eu cours avec des gestionnaires pour la partie composant d'interface utilisateur. 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);
}
}
Le mot clé synchronized est ajouté pour éviter un problème de verrouillage de rendu dans javafx si setText est appelé avant l'exécution de l'ancien. Il est facile à reproduire si vous commencez à taper très vite les mauvais caractères.
Un autre avantage est que vous ne conservez qu'un seul motif et que vous effectuez une restauration. C'est mieux, car vous pouvez facilement faire abstraction de la solution pour différents modèles d'assainissement.
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 voudrez peut-être 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'un TextBox
normal,
mais a aussi un attribut value
qui stocke l’entier saisi.
Lorsque le contrôle perd le focus, il valide la valeur et la restaure (si non valide).