本文介绍了有没有办法只接受JTextField中的数值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有办法只接受 JTextField 中的数值?对此有什么特别的方法吗?

Is there any way to accept only numeric values in a JTextField? Is there any special method for this?

推荐答案

由于这个问题经常出现,我在这个答案中付出了更多的努力我通常会这样做。

As this question re-appears quite often, I put some more effort in this answer then I would usually do.

我的投票转到 JFormattedTextField 。 IMO每个Swing开发人员都应该在他/她的工具包中拥有该类的改进版本,因为它允许通过正确选择 Format 来验证您能想到的几乎任何内容。我已经使用它的示例:

My vote goes to the JFormattedTextField. IMO each Swing developer should have an improved version of that class in his/her toolkit as it allows to validate almost anything you can think of by the correct choice of Format. Examples for which I already used it:


  • 字符串输入 String 可能不会是空的

  • 坐标输入

  • 日期输入

  • JSpinner上的编辑器

  • 地图比例

  • 数字

  • ...

  • String input where the String may not be empty
  • Coordinate input
  • Date input
  • Editor on a JSpinner
  • Map scales
  • Numbers
  • ...

当输入无效时,它还允许视觉反馈,例如 InputVerifier 。它仍然允许用户输入任何内容,但是当无效时该值根本不被接受,并且该值永远不会离开UI。我认为(但同样,这是我的观点),最好允许用户键入无效输入,只需将其自动删除,例如a DocumentFilter 。我怀疑在文本字段中输入一个字符并且它没有出现时会出现错误。

It also allows for visual feedback when the input is invalid which is for example not the case with the InputVerifier. It still allows to user to input anything, but that value is simply not accepted when not valid and that value never leaves the UI. I think (but again, that is my opinion) that it is better to allow the user to type invalid input that just removing that automatically with e.g. a DocumentFilter. I would suspect a bug when a type a character in a text field and it does not appear.

让我用一些代码(实际上是一些代码)来说明这一点。首先是小型演示应用程序。此应用程序只显示数字的 JFormattedTextField 。只使用其他格式允许重用该组件进行完全不同的验证。

Let me illustrate this with some code (quite some code actually). First the small demo application. This application just shows a JFormattedTextField for numbers. Just using another format allows to reuse that component for completely different validations.

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 只有在输入有效时才启用(aha,吃那个 DocumentFilter 解决方案)。它还显示 JTextArea ,其中每次遇到新的有效值时都会打印该值。按下按钮显示值。

which just shows an ImprovedFormattedTextField and a JButton which is only enabled when the input is valid (aha, eat that DocumentFilter solution). It also shows a JTextArea in which the value is printed each time a new valid value is encountered. Pressing the button shows the value.

下面可以找到 ImprovedFormattedTextField 的代码以及 ParseAllFormat 取决于哪个

The code for the ImprovedFormattedTextField can be found below, together with the ParseAllFormat on which it depends

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 class:

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& Fs都能遵守。因此,对于视觉反馈,最好使用放置在字段旁边的感叹号。缺点是,如果您突然添加/删除图标,这可能会弄乱布局

  • 反馈仅表示输入有效/无效。没有任何东西可以表明预期的格式是什么。一种可能的解决方案是使用 Format 的自创扩展,其中包含有效输入的描述/示例,并将其作为工具提示放在 JFormattedTextField上

  • the setBackground is not respected by all Look-and-Feels. Sometimes you can use the setForeground instead, but even that is not guaranteed to be respected by all L&Fs. So for visual feedback it might be better to use an exclamation mark placed next to the field. Drawback is that this might mess up a layout if you suddenly add/remove an icon
  • the feedback only indicates that the input is valid/invalid. There is nothing that indicates what the expected format is. A possible solution is to use a self-created extension of Format which includes a description/example of valid input, and put that as tooltip on the JFormattedTextField.

这篇关于有没有办法只接受JTextField中的数值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-31 10:31