web-dev-qa-db-fra.com

Restreindre l'entrée JTextField à des entiers

Je sais que cette question a dû être posée et répondue un million de fois, mais je ne peux tout simplement pas trouver une solution facile. J'ai un JTextField qui est destiné à n'accepter que des entiers positifs en entrée. J'ai besoin d'un moyen de m'assurer que rien d'autre ne soit introduit ici.

J'ai déjà un keyListener attaché à ce contrôle. En supprimant l'autre code que cet écouteur est là pour gérer, j'ai ceci:

       txtAnswer.addKeyListener(new KeyAdapter() {
        @Override
        public void keyPressed(KeyEvent e) {

            int key = e.getKeyCode();

            /* Restrict input to only integers */
            if (key < 96 && key > 105) e.setKeyChar('');
        };
    });

Comme vous pouvez le constater, j'essaie d'utiliser le KeyCode pour vérifier si la touche que vous venez d'appuyer tombe dans la plage des nombres entiers. Cela semble fonctionner. Mais ce que je veux faire, c'est simplement ne pas tenir compte de l'entrée si elle se situe en dehors de cette plage. Le code e.setKeyChar('') était destiné à gérer cela, mais cela ne fonctionnait pas. Le code sera compilé, mais il n’a aucun effet visible.

Quelqu'un peut-il me dire si je suis sur la bonne voie? Que puis-je remplacer par e.setKeyChar('') pour que cela fonctionne? Ou vais-je totalement dans la mauvaise direction?

Merci.

38
Alex

N'utilisez pas KeyListener pour cela car vous manquerez beaucoup de choses, notamment le collage de texte. De plus, KeyListener est une construction de très bas niveau et doit donc être évité dans les applications Swing.

La solution a été décrite à plusieurs reprises sur SO: utilisez un DocumentFilter . Il y a plusieurs exemples de ceci sur ce site, certains écrits par moi.

Par exemple: sing-documentfilter-filterbypass

Également pour l'aide du didacticiel, veuillez consulter: Implémentation d'un DocumentFilter .

Modifier

Par exemple:

import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.DocumentFilter;
import javax.swing.text.PlainDocument;

public class DocFilter {
   public static void main(String[] args) {
      JTextField textField = new JTextField(10);

      JPanel panel = new JPanel();
      panel.add(textField);

      PlainDocument doc = (PlainDocument) textField.getDocument();
      doc.setDocumentFilter(new MyIntFilter());


      JOptionPane.showMessageDialog(null, panel);
   }
}

class MyIntFilter extends DocumentFilter {
   @Override
   public void insertString(FilterBypass fb, int offset, String string,
         AttributeSet attr) throws BadLocationException {

      Document doc = fb.getDocument();
      StringBuilder sb = new StringBuilder();
      sb.append(doc.getText(0, doc.getLength()));
      sb.insert(offset, string);

      if (test(sb.toString())) {
         super.insertString(fb, offset, string, attr);
      } else {
         // warn the user and don't allow the insert
      }
   }

   private boolean test(String text) {
      try {
         Integer.parseInt(text);
         return true;
      } catch (NumberFormatException e) {
         return false;
      }
   }

   @Override
   public void replace(FilterBypass fb, int offset, int length, String text,
         AttributeSet attrs) throws BadLocationException {

      Document doc = fb.getDocument();
      StringBuilder sb = new StringBuilder();
      sb.append(doc.getText(0, doc.getLength()));
      sb.replace(offset, offset + length, text);

      if (test(sb.toString())) {
         super.replace(fb, offset, length, text, attrs);
      } else {
         // warn the user and don't allow the insert
      }

   }

   @Override
   public void remove(FilterBypass fb, int offset, int length)
         throws BadLocationException {
      Document doc = fb.getDocument();
      StringBuilder sb = new StringBuilder();
      sb.append(doc.getText(0, doc.getLength()));
      sb.delete(offset, offset + length);

      if (test(sb.toString())) {
         super.remove(fb, offset, length);
      } else {
         // warn the user and don't allow the insert
      }

   }
}

Pourquoi est-ce important?

  • Que se passe-t-il si l'utilisateur utilise copier et coller pour insérer des données dans le composant texte? Cela peut-il manquer à un KeyListener?
  • Vous semblez vouloir vérifier que les données peuvent représenter un int. Que se passe-t-il si elles entrent des données numériques qui ne correspondent pas?
  • Que se passe-t-il si vous souhaitez autoriser l'utilisateur à saisir ultérieurement des données en double? En notation scientifique?
64

Vous pouvez également utiliser JFormattedTextField, qui est beaucoup plus simple à utiliser. Exemple:

public static void main(String[] args) {
    NumberFormat format = NumberFormat.getInstance();
    NumberFormatter formatter = new NumberFormatter(format);
    formatter.setValueClass(Integer.class);
    formatter.setMinimum(0);
    formatter.setMaximum(Integer.MAX_VALUE);
    formatter.setAllowsInvalid(false);
    // If you want the value to be committed on each keystroke instead of focus lost
    formatter.setCommitsOnValidEdit(true);
    JFormattedTextField field = new JFormattedTextField(formatter);

    JOptionPane.showMessageDialog(null, field);

    // getValue() always returns something valid
    System.out.println(field.getValue());
}
38
Peter Tseng

Je ne peux pas croire que je n'ai pas encore trouvé cette solution simple sur le dépassement de capacité de la pile, elle est de loin la plus utile. Changer le Document ou le DocumentFilter ne fonctionne pas pour JFormattedTextField. La réponse de Peter Tseng est très proche.

NumberFormat longFormat = NumberFormat.getIntegerInstance();

NumberFormatter numberFormatter = new NumberFormatter(longFormat);
numberFormatter.setValueClass(Long.class); //optional, ensures you will always get a long value
numberFormatter.setAllowsInvalid(false); //this is the key!!
numberFormatter.setMinimum(0l); //Optional

JFormattedTextField field = new JFormattedTextField(numberFormatter);
21
Umi

Voici une approche qui utilise un keylistener, mais utilise le keyChar (au lieu du keyCode):

http://edenti.deis.unibo.it/utils/Java-tips/Validating%20numerical%20input%20in%20a%20JTextField.txt

 keyText.addKeyListener(new KeyAdapter() {
    public void keyTyped(KeyEvent e) {
      char c = e.getKeyChar();
      if (!((c >= '0') && (c <= '9') ||
         (c == KeyEvent.VK_BACK_SPACE) ||
         (c == KeyEvent.VK_DELETE))) {
        getToolkit().beep();
        e.consume();
      }
    }
  });

Une autre approche (que je trouve personnellement aussi compliquée que le modèle JTree de Swing) consiste à utiliser des champs de texte mis en forme:

http://docs.Oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html

13
paulsm4

J'avais l'habitude d'utiliser le Key Listener pour cela mais j'ai échoué beaucoup avec cette approche. La meilleure approche recommandée consiste à utiliser un DocumentFilter. Vous trouverez ci-dessous une méthode utilitaire que j'ai créée pour créer des champs de texte avec une seule entrée numérique. Méfiez-vous juste que ça prendra aussi single '.' caractère aussi bien car il est utilisable pour la saisie décimale.

public static void installNumberCharacters(AbstractDocument document) {
        document.setDocumentFilter(new DocumentFilter() {
            @Override
            public void insertString(FilterBypass fb, int offset,
                    String string, AttributeSet attr)
                    throws BadLocationException {
                try {
                    if (string.equals(".")
                            && !fb.getDocument()
                                    .getText(0, fb.getDocument().getLength())
                                    .contains(".")) {
                        super.insertString(fb, offset, string, attr);
                        return;
                    }
                    Double.parseDouble(string);
                    super.insertString(fb, offset, string, attr);
                } catch (Exception e) {
                    Toolkit.getDefaultToolkit().beep();
                }

            }

            @Override
            public void replace(FilterBypass fb, int offset, int length,
                    String text, AttributeSet attrs)
                    throws BadLocationException {
                try {
                    if (text.equals(".")
                            && !fb.getDocument()
                                    .getText(0, fb.getDocument().getLength())
                                    .contains(".")) {
                        super.insertString(fb, offset, text, attrs);
                        return;
                    }
                    Double.parseDouble(text);
                    super.replace(fb, offset, length, text, attrs);
                } catch (Exception e) {
                    Toolkit.getDefaultToolkit().beep();
                }
            }
        });
    }
5
Chan

Lorsque vous saisissez des nombres entiers dans JtextField1 après le relâchement de la touche, le résultat est renvoyé à l’essai intérieur. Pour tout autre caractère, il générera une exception NumberFormatException. Si vous définissez une chaîne vide sur jTextField1 à l'intérieur de la capture, l'utilisateur ne peut pas saisir d'autres clés que des nombres positifs, car JTextField1 sera effacé à chaque tentative infructueuse.

 //Fields 
int x;
JTextField jTextField1;

 //Gui Code Here

private void jTextField1KeyReleased(Java.awt.event.KeyEvent evt) {                                        
    try {
        x = Integer.parseInt(jTextField1.getText());
    } catch (NumberFormatException nfe) {
        jTextField1.setText("");
    }
}   
4
user3498019