Ограничение ввода JTextField целыми числами [дубликаты]

This question already has an answer here:

Is there any way to accept only numeric values in a JTextField? 19 answers

Я знаю, что на этот вопрос нужно было ответить и ответить миллион раз, но я просто не могу найти легкого решения. У меня есть 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('') чтобы сделать эту работу? Или я совершенно не в том направлении?

Благодарю.

 nIcE cOw19 июн. 2012 г., 07:41
@ Рэнди: Пожалуйста, посмотрите на этоexample также

Ответы на вопрос(6)

Я не могу поверить, что пока не нашел это простое решение где-либо при переполнении стека, оно, безусловно, самое полезное. Изменение Document или 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);
 14 нояб. 2014 г., 16:41
Поле также никогда не может быть пустым после того, как вы начали что-то набирать в нем (то есть, если я наберу 1234 и решу «на самом деле, мне не нужно число в этом поле», останется хотя бы один символ).
 27 янв. 2017 г., 02:19
@LeeCambl Эту проблему можно решить, сделав подклассNumberFormatter который переопределяетstringToValue(String text) возвращатьnull для пустой строки.

Вы также можете использовать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());
}
 03 окт. 2016 г., 14:23
Этот метод работает хорошо, за исключением одного: после ввода числа первая цифра не может быть удалена клавишей Backspace.
 19 дек. 2013 г., 17:24
При этом пользователь может вставлять буквы в поле.
 09 окт. 2017 г., 12:15
field.setColumns (..) // для числа ширины больше 1000 не может быть проанализировано в целое число. (обычный способ)
 21 февр. 2017 г., 14:47
@cesAR: я исправил это, кодировав свою собственную реализацию, вы можете обратиться к ответу Hovercraft Full Of Eels, я также добавил комментарий в конце, чтобы решить эту проблему. Проблема возникает из-за того, что она не учитывает"" в качестве действительного Interger / Double ..

Когда вы вводите целые числа в JtextField1 после отпускания ключа, он идет внутрь try, для любого другого символа он генерирует исключение NumberFormatException. Если вы установите пустую строку в 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("");
    }
}   
Решение Вопроса

Не используйте KeyListener для этого, так как вы многое пропустите, включая вставку текста. Кроме того, KeyListener - это очень низкоуровневая конструкция, поэтому ее следует избегать в приложениях Swing.

Решение было описано много раз на SO:DocumentFilter, На этом сайте есть несколько примеров, некоторые из которых написаны мной.

Например:используя-documentfilter-filterbypass

Также для учебной помощи, пожалуйста, посмотрите на:Реализация DocumentFilter.

Edit

Например:

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
      }

   }
}

Почему это важно?

What if the user uses copy and paste to insert data into the text component? A KeyListener can miss this? You appear to be desiring to check that the data can represent an int. What if they enter numeric data that doesn't fit? What if you want to allow the user to later enter double data? In scientific notation?
 14 февр. 2014 г., 21:09
Спасибо вам за образец. Это очень полезно. Ваш пример, кажется, ограничивает ввод максимум 10 цифрами. Подскажите, пожалуйста, как увеличить это?
 20 июн. 2012 г., 04:46
@Randy: изменить метод теста (текст строки). Пусть это вернет, еслиtext.trim().isEmpty() правда.
 21 февр. 2017 г., 14:41
Я столкнулся с проблемой, что вы не можете стереть последнюю цифру, если у вас есть та же проблема, вы можете использовать этот кусок кода в функции удаления.if(sb.toString().length() == 0) { super.replace(fb, offset, length, "0", null); } else { if (test(sb.toString())) { super.remove(fb, offset, length); } else { // warn the user and don't allow the insert } }                 В моем примере я заменяю его"0", но вы можете заменить его на""
 Alex20 июн. 2012 г., 04:37
Это довольно круто. Я давно смотрю на это и тестирую в упрощенном приложении. Я более или менее понимаю, что он делает и почему (более или менее!). Это создает для меня одну проблему. На моем JTextBox после того, как пользователь нажал клавишу ввода, я вызываю несколько методов, для выполнения которых я все еще использую KeyListener. Это похоже на работу. Но ОЧЕНЬ последнее, что мне нужно сделать с этим текстовым полем, это очистить его, чтобы можно было вводить новый текст. Я делал это с txtAnswer.setText (& quot; & quot;); но теперь кажется, что этот фильтр документов блокирует это действие. Кто-нибудь может подсказать? Поблагодарить
 19 июн. 2012 г., 04:11
+1 Тоже посмотримInputVerifier.

Раньше я использовал 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();
                }
            }
        });
    }

Вот один из подходов, который использует 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();
      }
    }
  });

Другой подход (который лично я нахожу почти таким же сложным, как и модель JTree в Swing), заключается в использовании полей форматированного текста:

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

Ваш ответ на вопрос