Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 266
Johanna Avatar asked Aug 21 '09 18:08

Johanna


People also ask

How do I allow only numbers in JTextField?

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(''); }; });

How do I make textfield only accept numbers?

The standard solution to restrict a user to enter only numeric values is to use <input> elements of type number. It has built-in validation to reject non-numerical values.

How do you accept only numbers in Java?

To only accept numbers, you can do something similar using the Character. isDigit(char) function, but note that you will have to read the input as a String not a double , or get the input as a double and the converting it to String using Double.


2 Answers

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

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 input where the String may not be empty
  • Coordinate input
  • Date input
  • Editor on a JSpinner
  • Map scales
  • Numbers
  • ...

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.

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.

enter image description here

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;   } } 

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.

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 );             }           }         } );       }     }   } } 

The 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 );   } } 

Possible improvements:

  • 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.
like image 131
Robin Avatar answered Oct 06 '22 00:10

Robin


This question was cited as an 'exact duplicate' of another question that has since been closed. The answers to this question were so poor that I was inspired to help out anybody that might find it later, by linking to a much better answer for this use case.

It is an answer to the closed question & can be summed up as..

Use a JSpinner instead.

like image 33
Andrew Thompson Avatar answered Oct 05 '22 23:10

Andrew Thompson