永远不要为此使用 KeyListener。您上面的代码有两个严重的错误,都是由使用 KeyListener 引起的。首先,它会错过任何粘贴的文本。每当我找到一个过滤掉非数字的字段时,我总是尝试粘贴一些文本,只是为了好玩。十分之九,文本被接受,因为他们使用了一个关键的听众。
其次,您没有过滤掉修饰符。如果用户在此字段中尝试通过键入 control-s 来保存他们的工作,则您的键侦听器将使用该事件。如果您的应用程序为某个操作分配了一个 control-5 或 alt-5 快捷方式,则此 KeyListener 将在他们键入时将 5 添加到该字段,即使用户没有尝试键入字符。您确实发现您需要传递退格键,但这并不是您需要传递的全部内容。你的方向键不起作用。您的功能键也不会。您可以解决所有这些问题,但这开始需要大量工作。
还有第三个缺点,但它只有在您将应用程序调整为外国字母时才会出现,特别是需要多次击键才能生成单个字符的应用程序,例如中文。这些字母表使 KeyListeners 无用。
麻烦就在于此。您需要过滤字符,但 KeyListener 与字符无关,它们与击键有关,这不是一回事:并非所有击键都会生成字符,也不是所有字符都是由击键生成的。您需要一种方法来查看生成后的字符,并且在修改的击键已经被过滤掉之后。
最简单的方法是使用 JFormattedTextField,但我从不喜欢这种方法,因为它不会在您键入时进行格式化或过滤。因此,我将使用 DocumentFilter。DocumentFilters 不会对击键进行操作,它们会在插入到 JTextField 的数据模型中时对文本字符串进行操作。因此,所有的控制键、箭头和功能键等都无法到达 DocumentFilter。所有粘贴的文本也会通过 DocumentFilter。而且,对于需要三个击键来生成单个字符的语言,DocumentFilter 在生成字符之前不会被调用。这是它的样子:
ptoMinimoField = new JTextField();
ptoMinimoField.setBounds(348, 177, 167, 20); // Don't do this! See below.
contentPanel.add(ptoMinimoField);
ptoMinimoField.setColumns(10);
PlainDocument document = (PlainDocument) ptoMinimoField.getDocument();
document.setDocumentFilter(new DigitFilter());
}
DigitFilter 类如下所示:
public class DigitFilter extends DocumentFilter {
@Override
public void insertString(FilterBypass fb, int offset, String text,
AttributeSet attr) throws BadLocationException {
super.insertString(fb, offset, revise(text), attr);
}
@Override
public void replace(FilterBypass fb, int offset, int length, String text,
AttributeSet attrs) throws BadLocationException {
super.replace(fb, offset, length, revise(text), attrs);
}
private String revise(String text) {
StringBuilder builder = new StringBuilder(text);
int index = 0;
while (index < builder.length()) {
if (accept(builder.charAt(index))) {
index++;
} else {
// Don't increment index here, or you'll skip the next character!
builder.deleteCharAt(index);
}
}
return builder.toString();
}
/**
* Determine if the character should remain in the String. You may
* override this to get any matching criteria you want.
* @param c The character in question
* @return true if it's valid, false if it should be removed.
*/
public boolean accept(final char c) {
return Character.isDigit(c) || c == '.';
}
}
您可以将其编写为内部类,但我创建了一个单独的类,因此您可以覆盖 accept() 方法以使用您想要的任何标准。您可能还会注意到,我不会通过编写以下代码来测试数字:
(c < '0') || (c > '9')
相反,我这样做:
Character.isDigit()
这更快、更简洁,并且适用于外国编号系统。我也不需要您对退格字符 '\b' 进行测试。我猜你的第一个 KeyListener 过滤掉了退格键,这是你第一个线索,这是错误的方法。
在不相关的说明中,不要对组件位置进行硬编码。了解如何使用 LayoutManager。它们易于使用,并且它们将使您的代码更易于维护。