Quelle est la méthode recommandée pour créer un champ de texte numérique dans JavaFX?

je dois restreindre l'entrée dans un champ de texte à des entiers. Tous les conseils?

68
demandé sur Harry Mitchell 2011-09-26 17:10:02

21 réponses

très vieux fil, mais cela semble plus net et les bandes hors des caractères non numériques si 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]", ""));
        }
    }
});
75
répondu Evan Knowles 2017-10-14 08:52:26

je sais que c'est un fil assez vieux, mais pour les futurs lecteurs voici une autre solution que j'ai trouvé tout à fait intuitif:

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.

36
répondu Burkhard 2016-01-06 10:14:30

Mise À Jour Avril 2016

cette réponse a été créée il y a quelques années et la réponse originale est largement obsolète aujourd'hui.

depuis Java 8u40, Java a un TextFormatter qui est généralement le meilleur pour appliquer l'entrée de formats spécifiques tels que numerics sur JavaFX TextFields:

Voir aussi les autres réponses à cette question qui mentionnent expressément la matière textuelle.


Réponse Originale

Il y a des 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()));
      }
    });
  }
}
29
répondu jewelsea 2017-05-23 12:34:50

le TextInput a un TextFormatter qui peut être utilisé pour formater, convertir et limiter les types de texte qui peuvent être entrés.

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 aussi un convertisseur que nous devons régler pour convertir la valeur de 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 une des trois choses, il peut renvoyer le changement non modifiée de l'accepter comme il est, il peut modifier le changement d'une certaine façon qu'il juge appropriée ou elle peut renvoyer null pour rejeter le changement tous ensemble.

nous utiliserons la norme IntegerStringConverter comme convertisseur.

Mettre tous 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 voulez n'avez pas besoin d'un filtre réutilisable vous peut faire cette fantaisie une doublure à la place:

TextFormatter<Integer> formatter = new TextFormatter<>(
    new IntegerStringConverter(), 
    defaultValue,  
    c -> Pattern.matches("\d*", c.getText()) ? c : null );
21
répondu Emily L. 2017-02-19 16:13:24

à 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 le sous-classement et la duplication d'événements de changement que vous obtiendrez lorsque vous ajoutez un listener de changement à la propriété text et modifiez le texte dans ce listener.

20
répondu Uwe 2017-08-08 06:26:48

Je n'aime pas les exceptions donc j'ai utilisé la fonction matches de la classe de corde

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);
        }
    }
});
20
répondu thatsIch 2017-10-17 06:14:42
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 traiter des entrées comme 0.5 d ou 0.7 F qui sont correctement interprétées par Int.parseInt(), mais ne devrait pas apparaître dans le champ de texte.

6
répondu ubuntudroid 2017-10-14 08:54:14

à 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èche vers le haut/Flèche vers le bas du clavier ou les boutons Flèche vers le haut/Flèche vers le bas fournis.

vous pouvez également définir une min , une max et une initiale valeur pour limiter les valeurs autorisées et un montant à incrémenter ou décrémenter de, par pas.

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" entier "spinner et a " double "spinner

enter image description here

A spinner est un contrôle de champ de texte en ligne simple qui permet à l'utilisateur sélectionnez un numéro ou une la valeur de l'objet à partir d'une séquence ordonnée de ces valeur. Spinners généralement fournir une paire de boutons de flèches minuscules pour parcourant les éléments de la séquence. Le clavier est les touches flèche/flèche bas passent aussi à travers les éléments. L'utilisateur peut il est également permis de taper une valeur (juridique) directement dans le fileur. Bien que les boîtes combo offrent des fonctionnalités similaires, les parfois préférables car elles ne nécessitent pas une liste déroulante qui peut masquer des données importantes, et aussi parce qu'ils permettent de fonctionnalités comme l'enroulement de la valeur maximale vers la valeur minimale (par exemple, de l'entier positif le plus grand à 0).

Plus de détails à propos de le contrôle Spinner

5
répondu Nicolas Filotto 2017-06-30 12:48:10

la réponse peut être encore plus petit si vous faites usage de Java 1.8 Lambdas

textfield.textProperty().addListener((observable, oldValue, newValue) -> {
    if (!newValue.matches("\d*")) {
        textfield.setText(newValue.replaceAll("[^\d]", ""));
    }
});
3
répondu Vincent Engel 2017-03-14 08:11:19

celui-ci travaillait 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);
            }
        }
    });
}
3
répondu Martin 2017-10-14 08:54:55

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);
        }

    }
});

même dans le lambda de la notation serait:

private TextField textField;
textField.textProperty().addListener((observable, oldValue, newValue) -> {
    if(!newValue.matches("[0-9]*")){
        textField.setText(oldValue);
    }
});
2
répondu Aren Isa 2016-05-14 07:21:55

si vous voulez appliquer le même auditeur à plus D'un champ de texte 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);
2
répondu javasuns 2016-05-21 08:35:00

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;
    }
}));
2
répondu Hassan Latif 2016-11-25 22:15:28

cette méthode permet à TextField de terminer tout le traitement (copier/coller/annuler sûr). 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 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 il suffit d'ajouter 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);
   }
2
répondu gmatagmis 2016-12-15 05:34:41

Voici une classe simple qui gère quelques validations de base sur TextField , en utilisant TextFormatter introduit dans JavaFX 8u40

EDIT:

(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 champ customTextField avec les propriétés correspondantes.

app.fxml:

<fx:define>
    <TextFieldValidator fx:id="validator" modus="INTEGERS_ONLY"/>
</fx:define>

CustomTextField.classe:

public class CustomTextField {

private TextField textField;

public CustomTextField(@NamedArg("validator") TextFieldValidator validator) {
        this();
        textField.setTextFormatter(validator.getFormatter());
    }
}

Code sur github

1
répondu jns 2016-04-05 21:19:59

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);

    }
});
1
répondu Ahmed Jaouadi 2018-03-21 01:55:28

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 votre propre.
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 normal TextBox ,

mais a aussi un attribut value qui stocke l'entier entré.

Lorsque le contrôle perd le focus, il valide la valeur et la renverse (si elle n'est pas valide).

0
répondu Alba Mendez 2011-10-12 15:04:35

ce Code fait que votre textField accepte seulement le 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));
            }
        }
    });
0
répondu Amine Harbaoui 2016-04-25 22:35:47

dans les dernières mises à jour de JavaFX, vous devez définir un nouveau texte dans la plate-forme.méthode 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 une bonne idée de positionner caret aussi.

0
répondu bdshahab 2017-05-14 15:11:32

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 manipulateurs pour les composants de L'assurance-chômage. Initialisation:

this.dataText.textProperty().addListener((observable, oldValue, newValue) -> this.numericSanitization(observable, oldValue, newValue));

et la méthode de numbericsanitisation:

private synchronized void numericSanitization(ObservableValue<? extends String> observable, String oldValue, String newValue) {
    final String allowedPattern = "\d*";

    if (!newValue.matches(allowedPattern)) {
        this.dataText.setText(oldValue);
    }
}

Keyword synchronized est ajouté pour éviter un problème de verrouillage de rendu dans javafx si setText sera appelé avant l'ancien on est fini de l'exécution. Il est facile de reproduire si vous allez commencer à taper des caractères erronés très rapidement.

un autre avantage est que vous ne conservez qu'un motif pour correspondre et faites simplement un rollback. Il est préférable parce que vous pouvez facilement abstragate solution pour différents modèles de désinfection.

0
répondu Oleksandr Knyga 2017-05-23 12:26:32
    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 très bien car il vous permet d'entrer seulement la valeur entière et la valeur décimale (ayant le code ASCII 46).

0
répondu ShubhamWanne 2017-06-13 06:18:20