Есть ли способ принимать только числовые значения в JTextField?

Есть ли способ принимать только числовые значения в JTextField? Есть ли какой-то специальный метод для этого?


person Johanna    schedule 21.08.2009    source источник
comment
Связанный вопрос: почему JFormattedTextField злой?   -  person kwutchak    schedule 24.08.2009
comment
Я знаю, что это устарело, но может помочь на самом деле связаться с вопросом. (Если подумать, это может быть просто неработающая ссылка из-за изменений в системе с 2009 года.)   -  person Sean Allred    schedule 05.08.2013


Ответы (17)


Поскольку этот вопрос повторяется довольно часто, я приложил больше усилий к этому ответу, чем обычно.

Мой голос принадлежит JFormattedTextField. IMO каждый разработчик Swing должен иметь улучшенную версию этого класса в своем наборе инструментов, поскольку он позволяет проверить практически все, что вы можете придумать, с помощью правильного выбора Format. Примеры, для которых я уже использовал это:

  • Строковый ввод, где String не может быть пустым
  • Ввод координат
  • Ввод даты
  • Редактор на JSpinner
  • Масштабы карты
  • Числа
  • ...

Это также позволяет визуальную обратную связь, когда ввод недействителен, что, например, не в случае с InputVerifier. Он по-прежнему позволяет пользователю вводить что угодно, но это значение просто не принимается, если оно недействительно, и это значение никогда не покидает пользовательский интерфейс. Я думаю (но опять же, это мое мнение), что лучше позволить пользователю вводить неверный ввод, который просто удаляет это автоматически, например. DocumentFilter. Я бы заподозрил баг, когда набираешь символ в текстовом поле, а он не появляется.

Позвольте мне проиллюстрировать это с помощью некоторого кода (на самом деле довольно большого количества кода). Сначала небольшое демонстрационное приложение. Это приложение просто показывает JFormattedTextField для чисел. Просто использование другого формата позволяет повторно использовать этот компонент для совершенно разных проверок.

введите здесь описание изображения

import be.pcl.swing.ImprovedFormattedTextField;

import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.NumberFormat;

/**
 * See http://stackoverflow.com/q/1313390/1076463
 */
public class FormattedTextFieldDemo {
  public static void main( String[] args ) {
    EventQueue.invokeLater(new Runnable() {
      @Override
      public void run() {
        JFrame testFrame = new JFrame( "FormattedTextFieldDemo" );

        NumberFormat integerNumberInstance = NumberFormat.getIntegerInstance();
        ImprovedFormattedTextField integerFormattedTextField = new ImprovedFormattedTextField( integerNumberInstance, 100 );
        integerFormattedTextField.setColumns( 20 );

        testFrame.add( createButtonPanel( integerFormattedTextField ), BorderLayout.NORTH );

        final JTextArea textArea = new JTextArea(50, 50);
        PropertyChangeListener updateTextAreaListener = new PropertyChangeListener() {
          @Override
          public void propertyChange( PropertyChangeEvent evt ) {
            textArea.append( "New value: " + evt.getNewValue() + "\n" );
          }
        };
        integerFormattedTextField.addPropertyChangeListener( "value", updateTextAreaListener );

        testFrame.add( new JScrollPane( textArea ), BorderLayout.CENTER );

        testFrame.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE );
        testFrame.pack();
        testFrame.setVisible( true );
      }
    } );

  }

  private static JPanel createButtonPanel( final JFormattedTextField aTextField ){
    JPanel panel = new JPanel( new BorderLayout(  ) );
    panel.add( aTextField, BorderLayout.WEST );

    Action action = new AbstractAction() {
      {
        aTextField.addPropertyChangeListener( "editValid", new PropertyChangeListener() {
          @Override
          public void propertyChange( PropertyChangeEvent evt ) {
            setEnabled( ( ( Boolean ) evt.getNewValue() ) );
          }
        } );
        putValue( Action.NAME, "Show current value" );
      }
      @Override
      public void actionPerformed( ActionEvent e ) {
        JOptionPane.showMessageDialog( null, "The current value is [" + aTextField.getValue() + "] of class [" + aTextField.getValue().getClass() + "]" );
      }
    };
    panel.add( new JButton( action ), BorderLayout.EAST );
    return panel;
  }
}

который просто показывает ImprovedFormattedTextField и JButton, которые включены только тогда, когда ввод действителен (ага, съешьте это решение DocumentFilter). Он также показывает JTextArea, в котором значение печатается каждый раз, когда встречается новое допустимое значение. При нажатии на кнопку отображается значение.

Код для ImprovedFormattedTextField можно найти ниже вместе с ParseAllFormat, от которого он зависит.

package be.pcl.swing;

import javax.swing.JFormattedTextField;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.Color;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.text.Format;
import java.text.ParseException;

/**
 * <p>Extension of {@code JFormattedTextField} which solves some of the usability issues</p>
 */
public class ImprovedFormattedTextField extends JFormattedTextField {

  private static final Color ERROR_BACKGROUND_COLOR = new Color( 255, 215, 215 );
  private static final Color ERROR_FOREGROUND_COLOR = null;

  private Color fBackground, fForeground;

  /**
   * Create a new {@code ImprovedFormattedTextField} instance which will use {@code aFormat} for the
   * validation of the user input.
   *
   * @param aFormat The format. May not be {@code null}
   */
  public ImprovedFormattedTextField( Format aFormat ) {
    //use a ParseAllFormat as we do not want to accept user input which is partially valid
    super( new ParseAllFormat( aFormat ) );
    setFocusLostBehavior( JFormattedTextField.COMMIT_OR_REVERT );
    updateBackgroundOnEachUpdate();
    //improve the caret behavior
    //see also http://tips4java.wordpress.com/2010/02/21/formatted-text-field-tips/
    addFocusListener( new MousePositionCorrectorListener() );
  }

  /**
   * Create a new {@code ImprovedFormattedTextField} instance which will use {@code aFormat} for the
   * validation of the user input. The field will be initialized with {@code aValue}.
   *
   * @param aFormat The format. May not be {@code null}
   * @param aValue  The initial value
   */
  public ImprovedFormattedTextField( Format aFormat, Object aValue ) {
    this( aFormat );
    setValue( aValue );
  }

  private void updateBackgroundOnEachUpdate() {
    getDocument().addDocumentListener( new DocumentListener() {
      @Override
      public void insertUpdate( DocumentEvent e ) {
        updateBackground();
      }

      @Override
      public void removeUpdate( DocumentEvent e ) {
        updateBackground();
      }

      @Override
      public void changedUpdate( DocumentEvent e ) {
        updateBackground();
      }
    } );
  }

  /**
   * Update the background color depending on the valid state of the current input. This provides
   * visual feedback to the user
   */
  private void updateBackground() {
    boolean valid = validContent();
    if ( ERROR_BACKGROUND_COLOR != null ) {
      setBackground( valid ? fBackground : ERROR_BACKGROUND_COLOR );
    }
    if ( ERROR_FOREGROUND_COLOR != null ) {
      setForeground( valid ? fForeground : ERROR_FOREGROUND_COLOR );
    }
  }

  @Override
  public void updateUI() {
    super.updateUI();
    fBackground = getBackground();
    fForeground = getForeground();
  }

  private boolean validContent() {
    AbstractFormatter formatter = getFormatter();
    if ( formatter != null ) {
      try {
        formatter.stringToValue( getText() );
        return true;
      } catch ( ParseException e ) {
        return false;
      }
    }
    return true;
  }

  @Override
  public void setValue( Object value ) {
    boolean validValue = true;
    //before setting the value, parse it by using the format
    try {
      AbstractFormatter formatter = getFormatter();
      if ( formatter != null ) {
        formatter.valueToString( value );
      }
    } catch ( ParseException e ) {
      validValue = false;
      updateBackground();
    }
    //only set the value when valid
    if ( validValue ) {
      int old_caret_position = getCaretPosition();
      super.setValue( value );
      setCaretPosition( Math.min( old_caret_position, getText().length() ) );
    }
  }

  @Override
  protected boolean processKeyBinding( KeyStroke ks, KeyEvent e, int condition, boolean pressed ) {
    //do not let the formatted text field consume the enters. This allows to trigger an OK button by
    //pressing enter from within the formatted text field
    if ( validContent() ) {
      return super.processKeyBinding( ks, e,
                                      condition, pressed ) && ks != KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 );
    }
    else {
      return super.processKeyBinding( ks, e,
                                      condition, pressed );
    }
  }

  private static class MousePositionCorrectorListener extends FocusAdapter {
    @Override
    public void focusGained( FocusEvent e ) {
      /* After a formatted text field gains focus, it replaces its text with its
       * current value, formatted appropriately of course. It does this after
       * any focus listeners are notified. We want to make sure that the caret
       * is placed in the correct position rather than the dumb default that is
        * before the 1st character ! */
      final JTextField field = ( JTextField ) e.getSource();
      final int dot = field.getCaret().getDot();
      final int mark = field.getCaret().getMark();
      if ( field.isEnabled() && field.isEditable() ) {
        SwingUtilities.invokeLater( new Runnable() {
          @Override
          public void run() {
            // Only set the caret if the textfield hasn't got a selection on it
            if ( dot == mark ) {
              field.getCaret().setDot( dot );
            }
          }
        } );
      }
    }
  }
}

Класс ParseAllFormat:

package be.pcl.swing;

import java.text.AttributedCharacterIterator;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParseException;
import java.text.ParsePosition;

/**
 * <p>Decorator for a {@link Format Format} which only accepts values which can be completely parsed
 * by the delegate format. If the value can only be partially parsed, the decorator will refuse to
 * parse the value.</p>
 */
public class ParseAllFormat extends Format {
  private final Format fDelegate;

  /**
   * Decorate <code>aDelegate</code> to make sure if parser everything or nothing
   *
   * @param aDelegate The delegate format
   */
  public ParseAllFormat( Format aDelegate ) {
    fDelegate = aDelegate;
  }

  @Override
  public StringBuffer format( Object obj, StringBuffer toAppendTo, FieldPosition pos ) {
    return fDelegate.format( obj, toAppendTo, pos );
  }

  @Override
  public AttributedCharacterIterator formatToCharacterIterator( Object obj ) {
    return fDelegate.formatToCharacterIterator( obj );
  }

  @Override
  public Object parseObject( String source, ParsePosition pos ) {
    int initialIndex = pos.getIndex();
    Object result = fDelegate.parseObject( source, pos );
    if ( result != null && pos.getIndex() < source.length() ) {
      int errorIndex = pos.getIndex();
      pos.setIndex( initialIndex );
      pos.setErrorIndex( errorIndex );
      return null;
    }
    return result;
  }

  @Override
  public Object parseObject( String source ) throws ParseException {
    //no need to delegate the call, super will call the parseObject( source, pos ) method
    return super.parseObject( source );
  }
}

Возможные улучшения:

  • setBackground не уважают все взгляды и чувства. Иногда вместо этого можно использовать setForeground, но даже это не гарантируется соблюдением всеми L&F. Поэтому для визуальной обратной связи может быть лучше использовать восклицательный знак, размещенный рядом с полем. Недостаток в том, что это может испортить макет, если вы внезапно добавите/удалите значок.
  • обратная связь только указывает, что ввод действителен/недействителен. Нет ничего, что указывало бы на ожидаемый формат. Возможное решение — использовать самостоятельно созданное расширение Format, которое включает описание/пример допустимого ввода, и поместить его в качестве всплывающей подсказки в JFormattedTextField.
person Robin    schedule 16.11.2012
comment
@AndrewThompson Мне нужен был один. Поскольку этот вопрос возникает часто, мне нужен был ответ, на который я мог бы ссылаться в будущем. И поскольку этот вопрос уже набрал +10 тыс. просмотров, казалось подходящим местом для добавления такого ответа. - person Robin; 17.11.2012
comment
Можно ли использовать JFormattedTextField для разрешения только алфавитов переменной длины? - person Akshat; 01.04.2015
comment
@Akshat в приведенном выше решении вы можете использовать форматированное текстовое поле в сочетании с любой java.util.format реализацией, о которой вы только можете подумать. - person Robin; 01.04.2015

Этот вопрос был назван «точной копией» другого вопроса, который с тех пор был закрыт. Ответы на этот вопрос были настолько плохими, что я был вдохновлен помочь любому, кто мог бы найти его позже, сославшись на гораздо лучший ответ для этого варианта использования.

Это ответ на закрытый вопрос и может быть резюмирован как..

Вместо этого используйте JSpinner.

person Andrew Thompson    schedule 24.05.2011

Хотя есть чистое зло JFormattedTextField, нет тривиального способа сделать это, используя только библиотеку Swing. Лучший способ реализовать такую ​​функцию — использовать DocumentFilter.

Некоторый код, который я подготовил ранее. Небольшое описание.

person Tom Hawtin - tackline    schedule 21.08.2009
comment
Может быть, более простой пример использования Document для фильтрации ввода: java2s. com/Code/Java/Swing-JFC/Textfieldonlyacceptsnumbers.htm - person Thimmayya; 21.08.2009
comment
Мне любопытны комментарии @Tom и @banjollity о зле JTextField. Итак, связанный с этим вопрос: почему JFormattedTextField злой? - person kwutchak; 24.08.2009
comment
Ссылка на вопрос: stackoverflow.com/questions/1320117/ - person kwutchak; 24.08.2009

Простой подход заключается в создании подкласса JTextField и переопределении метода createDefaultModel() путем возврата настроенного подкласса PlainDocument. Пример - текстовое поле только для целых чисел:

public class NumberField extends JTextField {


@Override
protected Document createDefaultModel() {
    return new Numberdocument();
}

class Numberdocument extends PlainDocument
{
    String numbers="1234567890-";
    @Override
    public void insertString(int offs, String str, AttributeSet a)
            throws BadLocationException {
        if(!numbers.contains(str));
        else    super.insertString(offs, str, a);
    }
}

Обрабатывать ввод в insertString() любым способом.

person Alexiy    schedule 07.01.2014
comment
Спасибо, но, возможно, лучше написать if (numbers.contains(str) { super.insertString } - person Lawrence; 31.01.2016
comment
@Lawrence Да, иногда мне не хватает логики. - person Alexiy; 10.08.2016

Быстрое решение:

JTextField textField = new JTextField() {
  public void processKeyEvent(KeyEvent ev) {
    char c = ev.getKeyChar();
    if (c >= 48 && c <= 57) { // c = '0' ... c = '9'
      super.processKeyEvent(ev);
    }
  }
};

Проблема с приведенным выше решением заключается в том, что пользователь не может использовать клавиши «Удалить», «Стрелка влево», «Стрелка вправо» или «Забой» в текстовом поле, поэтому я предлагаю использовать это решение:

this.portTextField = new JTextField() {
  public void processKeyEvent(KeyEvent ev) {
    char c = ev.getKeyChar();
    try {
      // Ignore all non-printable characters. Just check the printable ones.
      if (c > 31 && c < 127) {
        Integer.parseInt(c + "");
      }
      super.processKeyEvent(ev);
    }
    catch (NumberFormatException nfe) {
      // Do nothing. Character inputted is not a number, so ignore it.
    }
  }
};
person BJ Dela Cruz    schedule 23.07.2012
comment
Пожалуйста, будьте осторожны: не все символы с KeyValue ›127 являются непечатаемыми символами. то есть «§» или «ß» все еще можно ввести во втором фрагменте кода. Просто замените 127 на 65535 и проверьте, что не-127 (ключ удаления) должен там работать (т.е. KeyArrows получил здесь значение 65535). если (c › 31 && c ‹ 65535 && c != 127) - person Constantin; 08.03.2013
comment
-1 из-за сломанного решения. Это фильтрует только такие вещи, как ввод «5». Вы можете копировать/вставлять в asdf без фильтрации. - person Alex Meiburg; 17.03.2013
comment
@BJ Peter DeLaCruz Спасибо за это решение, оно отлично сработало для меня, сэкономило мне много времени. Спасибо :) - person Luffy; 04.05.2015
comment
(1-), Не играйте с KeyEvents. Swing имеет более новый и лучший API (например, DocumentFilter), который будет работать во всех ситуациях. Это решение не будет работать, если вы вставите текст в текстовое поле. - person camickr; 19.09.2015

if (JTextField.getText().equals("") || !(Pattern.matches("^[0-9]+$", JTextField.getText()))) {
     JOptionPane.showMessageDialog(null, " JTextField Invalide !!!!! ");
   }
  • if JTextField.getText().equals("") ==-> если JTextField пуст
  • if(!(Pattern.matches("^[0-9]+$", JTextField.getText()))) ==-> если TextField содержит символы, отличные от указанных
  • JOptionPane.showMessageDialog(null, "JTextField Invalide !!!!!");==-> поэтому это сообщение будет разделено
person Rihab    schedule 14.05.2016
comment
Это может быть ответом, но добавьте адекватное описание, чтобы поддержать его. - person Ani Menon; 14.05.2016

Используйте formatter для форматирования текстового поля.

NumberFormat format = NumberFormat.getInstance();
format.setGroupingUsed(false);
NumberFormatter formatter = new NumberFormatter(format);
formatter.setValueClass(Integer.class);
formatter.setMaximum(65535);
formatter.setAllowsInvalid(false);
formatter.setCommitsOnValidEdit(true);
myTextField = new JFormattedTextField(formatter);
person XurajB    schedule 17.09.2016

Также рассмотрите возможность использования InputVerifier.

person trashgod    schedule 24.05.2011

Учитывая количество просмотров этого вопроса, я не нашел ни одного из приведенных выше решений, подходящих для моей проблемы. Я решил создать собственный PlainDocument< /strong> в соответствии с моими потребностями. Это решение также издает звуковой сигнал, когда достигается максимальное количество используемых символов или вставленный текст не является целым числом.

private class FixedSizeNumberDocument extends PlainDocument
{
    private JTextComponent owner;
    private int fixedSize;

    public FixedSizeNumberDocument(JTextComponent owner, int fixedSize)
    {
        this.owner = owner;
        this.fixedSize = fixedSize;
    }

    @Override
    public void insertString(int offs, String str, AttributeSet a)
            throws BadLocationException
    {
        if (getLength() + str.length() > fixedSize) {
            str = str.substring(0, fixedSize - getLength());
            this.owner.getToolkit().beep();
        }

        try {
            Integer.parseInt(str);
        } catch (NumberFormatException e) {
            // inserted text is not a number
            this.owner.getToolkit().beep();
            return;
        }

        super.insertString(offs, str, a);
    }               
}

реализовано следующим образом:

    JTextField textfield = new JTextField();
    textfield.setDocument(new FixedSizeNumberDocument(textfield,5));
person Terraego    schedule 15.11.2012

Попробуйте это в событии нажатия клавиши для связанного JTextField.

private void JTextField(java.awt.event.KeyEvent evt) {

    // TODO add your handling code here:
    char enter = evt.getKeyChar();
    if(!(Character.isDigit(enter))){
        evt.consume();
    }
}
person Erangad    schedule 09.09.2016

Очень простое решение — использовать прослушиватель действий.

TextFieldActionPerformed(java.awt.event.ActionEvent evt) {
    String textFieldValue = TextField.getText();
    try {
        Integer.parseInteger(textFieldValue);
    } catch(Exception e){
        JOptionPane.showMessageDialog(null, "Please insert Valid Number Only");
        TextField.setText(textFieldValue.substring(0,textFieldValue.length()-1));
    }
}

Вы также можете использовать его для Double:

Double.parseDouble(TextField.getText());
person Christo Naudé    schedule 05.06.2017
comment
Это удаляет только последний символ. Что, если пользователь вставит символ посередине? - person mattshu; 09.04.2019

Посмотрите на JFormattedTextField.

person Zed    schedule 21.08.2009

запишите этот код в набранный ключ

char c=evt.getKeyChar();
if(!(Character.isDigit(c) || (c==KeyEvent.VK_BACK_SPACE || c==KeyEvent.VK_DELETE)))
{
    getToolkit().beep();
    evt.consume();
}
person Navz    schedule 23.06.2015
comment
(1-), Не играйте с KeyEvents. Swing имеет более новый и лучший API (например, DocumentFilter), который будет работать во всех ситуациях. Это решение не будет работать, если вы вставите текст в текстовое поле. - person camickr; 19.09.2015
comment
как я могу разрешить «-» и «.» (минус и точка) в этом методе? - person Himagaran; 16.05.2019

numberField = новый JFormattedTextField (NumberFormat.getInstance());

Руководство по форматированию текстовых полей

person Stefan Kendall    schedule 21.08.2009
comment
Это то, о чем я думал до того, как потерпел неудачу, и поэтому искал здесь решение. К сожалению, JFormattedTextField проверяет ввод только при выходе из поля, а не во время редактирования, поэтому можно ввести недопустимое значение. Если ввод должен быть отслежен, это слишком поздно. - person mh.; 29.04.2011

Вы хотели бы взглянуть на JFormattedTextField.

Форматированные текстовые поля позволяют разработчикам указывать допустимый набор символов, которые можно вводить в текстовое поле

Это подкласс JTextField, поэтому вы можете использовать его следующим образом:

JTextField textField = new JFormattedTextField(NumberFormat.getInstance());
person OscarRyz    schedule 21.08.2009

Я думаю, что это лучшее решение:

JTextField textField = new JFormattedTextField(new MaskFormatter("###")); //
person Michal    schedule 21.10.2009

Вы можете создать красивое текстовое поле в java, которое принимает или разрешает только числовые значения. Вы даже можете установить точность для значений с плавающей запятой... проверьте код в зибокодах

person Chris    schedule 21.04.2010
comment
Заявление о том, что на вопрос есть красивый ответ, само по себе не является хорошим ответом. Ссылка на код не так хороша, как добавление фрагмента кода или, по крайней мере, объяснение того, чем он отличается от других ответов. - person tucuxi; 04.04.2016