Je dois insérer un champ de numéro dans mon interface utilisateur. Je dois donc vérifier les événements clés sur un champ de texte afin de vérifier si le caractère saisi est un nombre. J'ai créé une classe en étendant TextField. S'il existe une méthode dans la classe TextField qui gère keyEvents, je peux simplement passer outre cette méthode avec un champ de numérotation approprié. Des idées?
Merci
Trouvé une solution. :)
public class NumFieldFX extends TextField {
public NumFieldFX() {
this.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() {
public void handle( KeyEvent t ) {
char ar[] = t.getCharacter().toCharArray();
char ch = ar[t.getCharacter().toCharArray().length - 1];
if (!(ch >= '0' && ch <= '9')) {
System.out.println("The char you entered is not a number");
t.consume();
}
}
});
}
}
Mise à jour 27 mai 2016
Java 8u40 a introduit la classe TextFormatter qui est le moyen recommandé pour réaliser cette fonctionnalité (bien que la solution fournie dans cette réponse fonctionne toujours). Pour plus d'informations, voir Réponse d'Uwe , Réponse de Hassan et d'autres réponses mentionnant TextFormatter à la question suivante:
Il existe également cette solution issue d'une autre réponse à cette question que je n'ai pas encore essayée, mais qui a l'air bien et un modérateur de StackOverflow supprimé:
TextField numberField = new TextField();
numberField.setTextFormatter(new TextFormatter<>(new NumberStringConverter()));
Le code ci-dessus manque le filtre UnaryOperator pour TextFormatter, que vous avez également généralement requis (sinon, le champ n'affiche pas les entrées utilisateur restreintes à la valeur formatée, il vous permettra simplement de surveiller la valeur non formatée via la propriété de valeur du formateur de texte. ). Pour étendre la solution à l’utilisation d’un filtre, un code comme celui ci-dessous pourrait être utilisé:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.stage.Stage;
import javafx.util.converter.NumberStringConverter;
import Java.text.ParsePosition;
import Java.util.function.UnaryOperator;
public class NumberConverterFieldTest extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
TextField numberField = new TextField();
NumberStringFilteredConverter converter = new NumberStringFilteredConverter();
final TextFormatter<Number> formatter = new TextFormatter<>(
converter,
0,
converter.getFilter()
);
numberField.setTextFormatter(formatter);
formatter.valueProperty().addListener((observable, oldValue, newValue) ->
System.out.println(newValue)
);
stage.setScene(new Scene(numberField));
stage.show();
}
class NumberStringFilteredConverter extends NumberStringConverter {
// Note, if needed you can add in appropriate constructors
// here to set locale, pattern matching or an explicit
// type of NumberFormat.
//
// For more information on format control, see
// the NumberStringConverter constructors
// DecimalFormat class
// NumberFormat static methods for examples.
// This solution can instead extend other NumberStringConverters if needed
// e.g. CurrencyStringConverter or PercentageStringConverter.
public UnaryOperator<TextFormatter.Change> getFilter() {
return change -> {
String newText = change.getControlNewText();
if (newText.isEmpty()) {
return change;
}
ParsePosition parsePosition = new ParsePosition( 0 );
Object object = getNumberFormat().parse( newText, parsePosition );
if ( object == null || parsePosition.getIndex() < newText.length()) {
return null;
} else {
return change;
}
};
}
}
}
Lors de l'exécution de l'exemple ci-dessus, éditez le champ de saisie et appuyez sur la touche Entrée pour voir la valeur mise à jour (la valeur mise à jour est sortie en System.out
lors de la modification).
Pour un tutoriel, voir:
C’est la même solution que celle citée par Urs, mais je viens de la placer dans un programme entièrement exécutable afin de fournir un exemple en contexte et de modifier l’expression régulière (en ajoutant *
à la fin) afin que le copier-coller fonctionne et qu’il ne dispose pas des question à laquelle se réfère Uluk. La solution semble assez simple et suffira probablement dans la plupart des cas:
import Java.util.regex.Pattern;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
public class NumericTextFieldTest extends Application {
public static void main(String[] args) { launch(args); }
@Override public void start(Stage stage) {
TextField numberField = new TextField() {
@Override public void replaceText(int start, int end, String text) {
if (text.matches("[0-9]*")) {
super.replaceText(start, end, text);
}
}
@Override public void replaceSelection(String text) {
if (text.matches("[0-9]*")) {
super.replaceSelection(text);
}
}
};
stage.setScene(new Scene(numberField));
stage.show();
}
}
Solutions alternatives
Vous pouvez également être intéressé par ma solution alternative dans exemple JavaFX de liaison d'une valeur de curseur à un TextField modifiable. Dans cette solution, j'étend TextField pour exposer une propriété IntegerProperty sur le terrain à des fins de liaison simples. La solution alternative est similaire à celle décrite par l’affiche originale dans la question mise à jour (c’est-à-dire qu’un filtre d’événements est ajouté pour limiter les données d’entrée aux événements clés), mais qu’un ChangeListener est ajouté à la propriété text de TextField pour garantir que les valeurs copiées et collées ne sont acceptés que s'ils sont numériques.
Il existe d'autres solutions à cette question dans le fil de discussion du forum JavaFX Numeric Textfield in JavaFX 2.0? qui inclut une référence aux champs numéros des contrôles FXExperience .
Il existe un conseil sur FXExperience qui traite un problème comme celui-ci. Pour paraphraser, vous développez la variable TextField
et remplacez les méthodes replaceText()
et replaceSelection()
, en filtrant toutes les entrées qui ne sont pas des nombres.
Une fois implémentées, les deux méthodes doivent suivre ce modèle:
if (!newText.matches("[0-9]")) {
super.call(allParams)
}
Arrays.asList(txtLongitude, txtLatitude, txtAltitude, txtSpeed, txtOrientation).forEach(textField ->
textField.textProperty().addListener((observable, oldValue, newValue) ->
textField.setText(newValue.matches("^[0-9]*\\.?[0-9]*$") ? newValue : oldValue)
));
Voici le champ CustomText que j'ai écrit. Il gère à la fois l'entrée Numbers et maximaleSize. C'est un contrôle personnalisé pouvant être utilisé dans FXML ainsi que les propriétés peuvent être définies dans FXMl lui-même.
package fxml;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.IntegerPropertyBase;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.control.TextField;
public class CustomTextField extends TextField {
/**
* numericOnly property if set, will allow accept only numeric input.
*/
private BooleanProperty numericOnly = new SimpleBooleanProperty(this,
"numericOnly", false);
public final boolean isNumericOnly() {
return numericOnly.getValue();
}
public final void setNumericOnly(boolean value) {
numericOnly.setValue(value);
}
public final BooleanProperty numericOnlyProperty() {
return numericOnly;
}
/**
* maxSize property , determines the maximum size of the text that can be
* input.
*/
public IntegerProperty maxSize = new IntegerPropertyBase(1000) {
@Override
public String getName() {
return "maxSize";
}
@Override
public Object getBean() {
return CustomTextField.this;
}
};
public final IntegerProperty maxSizeProperty() {
return maxSize;
};
public final int getMaxSize() {
return maxSize.getValue();
}
public final void setMaxSize(int value) {
maxSize.setValue(value);
}
/**
* this method is called when user inputs text into the textField
*/
@Override
public void replaceText(int start, int end, String text) {
if (numericOnly.getValue() && !text.equals("")) {
if (!text.matches("[0-9]")) {
return;
}
}
if (getText().length() < getMaxSize() || text.equals("")) {
super.replaceText(start, end, text);
}
}
/**
* this method is called when user pastes text into the textField
*/
@Override
public void replaceSelection(String text) {
if (numericOnly.getValue() && !text.equals("")) {
if (!text.matches("[0-9]+")) {
return;
}
}
super.replaceSelection(text);
if (getText().length() > getMaxSize()) {
String maxSubString = getText().substring(0, getMaxSize());
setText(maxSubString);
positionCaret(getMaxSize());
}
}
}
package com.mazeworks.cloudhms.view.components;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.IntegerPropertyBase;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.control.TextField;
public class NumericTextField extends TextField {
/**
* numericOnly property if set, will allow accept only numeric input.
*/
private BooleanProperty numericOnly = new SimpleBooleanProperty(this,
"numericOnly", false);
public final boolean isNumericOnly() {
return numericOnly.getValue();
}
public final void setNumericOnly(boolean value) {
numericOnly.setValue(value);
}
public final BooleanProperty numericOnlyProperty() {
return numericOnly;
}
/**
* maxSize property, determines the maximum size of the text that
can be
* input.
*/
public IntegerProperty maxSize = new IntegerPropertyBase(1000) {
@Override
public String getName() {
return "maxSize";
}
@Override
public Object getBean() {
return NumericTextField.this;
}
};
public final IntegerProperty maxSizeProperty() {
return maxSize;
}
;
public final int getMaxSize() {
return maxSize.getValue();
}
public final void setMaxSize(int value) {
maxSize.setValue(value);
}
/**
* this method is called when user inputs text into the textField
*/
@Override
public void replaceText(int start, int end, String text) {
if (numericOnly.getValue() && !text.equals("")) {
if (!text.matches("^[0-9]*\\.?[0-9]*$")) {
return;
}
}
if (getText().length() < getMaxSize() || text.equals("")) {
super.replaceText(start, end, text);
}
}
/**
* this method is called when user pastes text into the textField
*/
@Override
public void replaceSelection(String text) {
if (numericOnly.getValue() && !text.equals("")) {
if (!text.matches("^[0-9]*\\.?[0-9]*$")) {
return;
}
}
super.replaceSelection(text);
if (getText().length() > getMaxSize()) {
String maxSubString = getText().substring(0, getMaxSize());
setText(maxSubString);
positionCaret(getMaxSize());
}
}
}
Hériter de TextField et substituer replaceText en tant que tel, pour obtenir une valeur Double uniquement TextField:
@Override
public void replaceText(int start, int end, String text) {
String preText = getText(0, start);
String afterText = getText(end, getLength());
String toBeEnteredText = preText + text + afterText;
// Treat the case where the user inputs proper text and is not inputting backspaces or other control characters
// which would be represented by an empty text argument:
if (!text.isEmpty() && text.matches("\\d|\\.")) {
Logger.getAnonymousLogger().info("Paring non-empty.");
try {
Logger.getAnonymousLogger().info("Parsing " + toBeEnteredText);
Double.parseDouble(toBeEnteredText);
super.replaceText(start, end, text);
} catch (Exception ignored) {
}
}
// If the user used backspace or del, the result text is impossible to not parse as a Double/Integer so just
// enter that text right ahead:
if (text.isEmpty()) {
super.replaceText(start, end, text);
}
}