限制JTextField输入为整数

58

我知道这个问题可能已经被问过和回答了无数次,但我就是找不到一个简单的解决方案。我有一个用于接收正整数输入的JTextField。我需要一种方法来确保此处没有输入其他内容。

我已经将一个keyListener附加到这个控件上。删除该侦听器存在的其他代码后,我有以下内容:

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

正如您所看到的,我正在尝试使用KeyCode来检查刚刚按下的键是否在整数范围内。这似乎行得通。但是我想做的是,如果输入不在此范围内,就简单地忽略它。代码e.setKeyChar('')本来应该处理这个问题,但它不起作用。代码会编译,但没有任何可见效果。

有人能告诉我我是否正确?我可以用什么替换e.setKeyChar('')来使其起作用?或者我完全走错了方向?

谢谢。


@Randy:请看一下这个示例 - nIcE cOw
6个回答

77

请勿使用KeyListener,因为这样会忽略很多内容,包括文本粘贴。此外,KeyListener是一种非常低级的结构,在Swing应用程序中应该避免使用。

解决方案已经在SO上多次描述过:使用DocumentFilter。本网站上有几个示例,其中一些是由我编写的。

例如:using-documentfilter-filterbypass

另外,如果需要教程帮助,请参考:Implementing a DocumentFilter

编辑

例如:

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
      }

   }
}

这为什么很重要?

  • 如果用户使用复制和粘贴将数据插入文本组件,KeyListener会错过这个吗?
  • 您似乎希望检查数据是否可以表示为int。如果他们输入不适合的数字数据怎么办?
  • 如果您希望允许用户稍后输入双倍数据?用科学计数法表示?

1
另外还要考虑InputVerifier - trashgod
1
@Randy:修改test(String text)方法。如果text.trim().isEmpty()为真,则使其返回true。 - Hovercraft Full Of Eels
谢谢您提供的示例,非常有用。 您的示例似乎仅限于最多10个数字的输入。请问如何增加此限制? - user1296058
@user1296058:关键在于test(...)方法中的DocumentFilter。如果字符串解析为一个整数,则测试通过;如果不是整数,则测试失败,并且你看到的10位数字限制只是整数类型的限制而已。如果你想要其他限制,可以使用不同的测试方法。 - Hovercraft Full Of Eels
5
我遇到了一个问题,就是无法删除最后一位数字。如果你也遇到了同样的问题,你可以在remove函数中使用以下这段代码来解决:`if(sb.toString().length() == 0) { super.replace(fb, offset, length, "0", null); } else { if (test(sb.toString())) { super.remove(fb, offset, length); } else { // 提醒用户并且不允许插入 } }` 在我的示例中,我用 "0" 替换了它,但你也可以用 "" 来替换。 - BaptisteL
显示剩余3条评论

56

您也可以使用JFormattedTextField,它的使用方式要简单得多。例如:

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

4
用户可以使用此功能在字段中插入字母。 - user800014
5
这种方法很有效,但有一个问题:一旦您输入了一个数字,使用退格键无法删除第一个数字。 - cesAR
@cesAR:我通过编码自己的实现来解决了这个问题,你可以参考Hovercraft Full Of Eels的答案,我还在最后添加了一条评论来解决这个问题。问题出在它没有将""视为有效的整数/双精度数。 - BaptisteL
field.setColumns(..) //用于设置宽度 1000以上的数字无法解析为整数(通常的方式) - Halfacht
我尝试了这种方法,但需要设置minimum=100和maximum=300,而且设置这些值会防止字段被编辑,因为没有办法修改文本而不使其成为无效值。我采用了后处理阶段,在其中检查该值并在超出范围时发出警告。 - Stevey

23

我简直不敢相信在stack overflow上找不到这个简单的解决方案,它绝对是最有用的。更改Document或DocumentFilter对于JFormattedTextField无效。 Peter Tseng的答案非常接近。

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

10
一旦您在输入框中开始输入内容,该字段也不能保持为空(例如,如果我键入1234,然后决定“实际上,我不想在此字段中输入数字”,至少会保留1个字符)。 - LeeCambl
6
可以通过创建NumberFormatter的子类并重写stringToValue(String text)方法来解决这个问题,使其在输入为空字符串时返回null - Enwired

21

以下是使用KeyListener的一种方法,但是使用keyChar而不是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();
      }
    }
  });
另一种方法(个人认为几乎和Swing的JTree模型一样复杂)是使用格式化文本字段: http://docs.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html

如果您想使用JTextField,这绝对是最简单和最有用的方法。 - Markus Duft

6

我曾尝试使用键盘监听器来实现此功能,但是失败了。目前最好的方法是使用文档过滤器。以下是我为构建仅允许输入数字的文本字段创建的实用程序方法。请注意,它还会接受单个点(“.”)字符,因为它可用于十进制输入。

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

3

当您在键释放后向JtextField1输入整数时,它将进入try语句中。对于任何其他字符,它将抛出NumberFormatException异常。如果您将空字符串设置为catch内的jTextField1,则用户无法键入除正数外的任何其他键,因为每次尝试不良输入时,JTextField1都将被清除。

 //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("");
    }
}   

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接