Ограничение ввода JTextField для целых чисел
Я знаю, что этот вопрос, должно быть, спросил и ответил миллион раз, но я просто не могу найти легкое решение. У меня есть 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('')
, чтобы сделать эту работу? Или я полностью иду в неправильном направлении?
Спасибо.
Ответы
Ответ 1
Не используйте KeyListener для этого, поскольку вы пропустите много, включая вставку текста. Также KeyListener является конструкцией очень низкого уровня и как таковой, следует избегать в приложениях Swing.
Решение было описано много раз на SO: используйте DocumentFilter. На этом сайте есть несколько примеров этого, некоторые из которых написаны мной.
Например: using-documentfilter-filterbypass
Также для справки по учебному пособию, пожалуйста, просмотрите: Внедрение 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. Что делать, если они вводят числовые данные, которые не подходят?
- Что делать, если вы хотите, чтобы пользователь позже вводил двойные данные? В научной нотации?
Ответ 2
Вы также можете использовать 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());
}
Ответ 3
Не могу поверить, что я еще не нашел это простое решение в любом месте при переполнении стека, это, безусловно, самое полезное. Изменение документа или DocumentFilter не работает для JFormattedTextField. Ответ Питера Цзэна очень близок.
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);
Ответ 4
Здесь один подход, который использует 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
Ответ 5
Раньше я использовал Key Listener для этого, но с таким подходом мне не хватило времени. Лучший подход, как рекомендовано, - использовать DocumentFilter. Ниже приведен метод утилиты, созданный для построения текстовых полей с использованием только ввода номера. Просто будьте осторожны, что он также будет принимать одиночные '.' а также потому, что он используется для ввода в десятичной форме.
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();
}
}
});
}
Ответ 6
Когда вы вводите целочисленные числа в JtextField1 после отпускания ключа, он переходит внутрь try, для любого другого персонажа он будет вызывать NumberFormatException. Если вы установите пустую строку в jTextField1 внутри catch, чтобы пользователь не мог вводить другие ключи, кроме положительных чисел, потому что 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("");
}
}