有没有办法只接受 JTextField 中的数值?
Posted
技术标签:
【中文标题】有没有办法只接受 JTextField 中的数值?【英文标题】:Is there any way to accept only numeric values in a JTextField? 【发布时间】:2010-11-21 18:04:57 【问题描述】:有没有办法只接受JTextField
中的数值?有什么特殊的方法吗?
【问题讨论】:
相关问题:为什么 JFormattedTextField 是邪恶的? 我知道这是旧的,但它可能实际上有助于link to the question。 (再想一想,由于 09 年以来系统的变化,它可能只是一个断开的链接。) 【参考方案1】:numberField = new JFormattedTextField(NumberFormat.getInstance());
Formatted text field tutorial
【讨论】:
这也是我失败前的想法,因此在这里搜索解决方案。不幸的是,JFormattedTextField 仅在离开字段时验证输入,而不是在编辑期间,因此可以输入无效值。如果必须跟踪输入,那就太晚了。【参考方案2】:你想看看JFormattedTextField
格式化文本字段为开发人员提供了一种方法来指定可在文本字段中输入的有效字符集
这是 JTextField 的一个子类,所以你可以这样使用它:
JTextField textField = new JFormattedTextField(NumberFormat.getInstance());
【讨论】:
【参考方案3】:看JFormattedTextField。
【讨论】:
【参考方案4】:尽管存在纯粹的邪恶JFormattedTextField
,但仅使用 Swing 库并没有简单的方法可以做到这一点。实现此类功能的最佳方式是使用DocumentFilter
。
(这篇文章最初在我现已失效的博客中包含指向代码和描述的链接。)
【讨论】:
也许是一个更直接的使用 Document 过滤输入的例子:java2s.com/Code/Java/Swing-JFC/Textfieldonlyacceptsnumbers.htm 我很好奇@Tom 和@banjollity 的关于JTextField 邪恶的cmets。那么,相关问题:为什么 JFormattedTextField 是邪恶的? 问题链接:***.com/questions/1320117/…【参考方案5】:我认为这是最好的解决方案:
JTextField textField = new JFormattedTextField(new MaskFormatter("###")); //
【讨论】:
【参考方案6】:您可以在 java 中创建一个漂亮的文本字段,它接受或只允许数值。您甚至可以设置浮点值的精度...检查 zybocodes 中的code
【讨论】:
说一个问题有“一个美丽的答案”本身并不是一个好的答案。链接到代码不如添加代码 sn-p,或者至少解释它与其他答案的不同之处。【参考方案7】:此问题被引用为另一个已关闭的问题的“完全重复”。这个问题的答案太差了,以至于我受到启发,希望通过链接到这个用例的更好答案来帮助以后可能找到它的任何人。
这是一个answer to the closed question & 可以总结为..
改用JSpinner
。
【讨论】:
【参考方案8】:另外,考虑使用InputVerifier
。
【讨论】:
【参考方案9】:import javax.swing.*;
import javax.swing.text.*;
public class JNumberTextField extends JTextField
private static final char DOT = '.';
private static final char NEGATIVE = '-';
private static final String BLANK = "";
private static final int DEF_PRECISION = 2;
public static final int NUMERIC = 2;
public static final int DECIMAL = 3;
public static final String FM_NUMERIC = "0123456789";
public static final String FM_DECIMAL = FM_NUMERIC + DOT;
private int maxLength = 0;
private int format = NUMERIC;
private String negativeChars = BLANK;
private String allowedChars = null;
private boolean allowNegative = false;
private int precision = 0;
protected PlainDocument numberFieldFilter;
public JNumberTextField()
this( 10, NUMERIC );
public JNumberTextField( int maxLen )
this( maxLen, NUMERIC );
public JNumberTextField( int maxLen, int format )
setAllowNegative( true );
setMaxLength( maxLen );
setFormat( format );
numberFieldFilter = new JNumberFieldFilter();
super.setDocument( numberFieldFilter );
public void setMaxLength( int maxLen )
if (maxLen > 0)
maxLength = maxLen;
else
maxLength = 0;
public int getMaxLength()
return maxLength;
public void setPrecision( int precision )
if ( format == NUMERIC )
return;
if ( precision >= 0 )
this.precision = precision;
else
this.precision = DEF_PRECISION;
public int getPrecision()
return precision;
public Number getNumber()
Number number = null;
if ( format == NUMERIC )
number = new Integer(getText());
else
number = new Double(getText());
return number;
public void setNumber( Number value )
setText(String.valueOf(value));
public int getInt()
return Integer.parseInt( getText() );
public void setInt( int value )
setText( String.valueOf( value ) );
public float getFloat()
return ( new Float( getText() ) ).floatValue();
public void setFloat(float value)
setText( String.valueOf( value ) );
public double getDouble()
return ( new Double( getText() ) ).doubleValue();
public void setDouble(double value)
setText( String.valueOf(value) );
public int getFormat()
return format;
public void setFormat(int format)
switch ( format )
case NUMERIC:
default:
this.format = NUMERIC;
this.precision = 0;
this.allowedChars = FM_NUMERIC;
break;
case DECIMAL:
this.format = DECIMAL;
this.precision = DEF_PRECISION;
this.allowedChars = FM_DECIMAL;
break;
public void setAllowNegative( boolean value )
allowNegative = value;
if ( value )
negativeChars = "" + NEGATIVE;
else
negativeChars = BLANK;
public boolean isAllowNegative()
return allowNegative;
public void setDocument( Document document )
class JNumberFieldFilter extends PlainDocument
public JNumberFieldFilter()
super();
public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException
String text = getText(0,offset) + str + getText(offset,(getLength() - offset));
if ( str == null || text == null )
return;
for ( int i=0; i<str.length(); i++ )
if ( ( allowedChars + negativeChars ).indexOf( str.charAt(i) ) == -1)
return;
int precisionLength = 0, dotLength = 0, minusLength = 0;
int textLength = text.length();
try
if ( format == NUMERIC )
if ( ! ( ( text.equals( negativeChars ) ) && ( text.length() == 1) ) )
new Long(text);
else if ( format == DECIMAL )
if ( ! ( ( text.equals( negativeChars ) ) && ( text.length() == 1) ) )
new Double(text);
int dotIndex = text.indexOf(DOT);
if( dotIndex != -1 )
dotLength = 1;
precisionLength = textLength - dotIndex - dotLength;
if( precisionLength > precision )
return;
catch(Exception ex)
return;
if ( text.startsWith( "" + NEGATIVE ) )
if ( !allowNegative )
return;
else
minusLength = 1;
if ( maxLength < ( textLength - dotLength - precisionLength - minusLength ) )
return;
super.insertString( offset, str, attr );
【讨论】:
【参考方案10】:快速解决方案:
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.
;
【讨论】:
请注意:并非所有 KeyValue >127 的字符都是不可打印字符。即“§”或“ß”仍然可以在第二个代码-sn-p 中输入。只需将 127 替换为 65535 并检查非 127(删除键)是否应该在那里工作(即 KeyArrows 在这里得到值 65535)。如果 (c > 31 && c -1 因为解决方案损坏。这只会过滤诸如输入“5”之类的内容。您可以在不过滤的情况下复制/粘贴“asdf”。 @BJ Peter DeLaCruz 感谢这个解决方案,它为我工作得很好,为我节省了很多时间谢谢:) (1-),不要玩 KeyEvents。 Swing 有更新更好的 API(如 DocumentFilter),适用于所有情况。如果您将文本“粘贴”到文本字段中,此解决方案将不起作用。【参考方案11】:考虑到这个问题的浏览量,我发现上述解决方案都不适合我的问题。我决定定制一个PlainDocument 来满足我的需求。 当达到使用的最大字符数或插入的文本不是整数时,此解决方案也会发出哔声。
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));
【讨论】:
【参考方案12】:由于这个问题经常再次出现,所以我在这个答案上付出了比我通常会做的更多的努力。
我的投票给JFormattedTextField
。 IMO 每个 Swing 开发人员都应该在他/她的工具包中拥有该类的改进版本,因为它允许通过正确选择 Format
来验证几乎任何你能想到的东西。我已经用过的例子:
String
不能为空的字符串输入
坐标输入
日期输入
JSpinner
上的编辑器
地图比例尺
数字
...
它还允许在输入无效时提供视觉反馈,例如InputVerifier
就不是这种情况。它仍然允许用户输入任何内容,但该值在无效时根本不被接受,并且该值永远不会离开 UI。我认为(但同样,这是我的观点)最好允许用户输入无效输入,例如自动删除它。 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://***.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
上。
【讨论】:
@AndrewThompson 我需要一个。由于这个问题经常出现,我需要一个我将来可以参考的答案。而且由于这个问题已经获得了 +10k 的浏览量,因此添加这样一个答案似乎是合适的地方 可以使用JFormattedTextField
只允许可变长度的字母吗?
@Akshat 在上述解决方案中,你可以结合任何你能想到的java.util.format
实现来使用格式化文本字段【参考方案13】:
一种简单的方法是继承 JTextField 并通过返回自定义的 PlainDocument 子类来覆盖 createDefaultModel()。示例 - 仅用于整数的文本字段:
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() 中处理输入。
【讨论】:
谢谢,但如果 (numbers.contains(str) super.insertString @Lawrence 是的,我有时会想念逻辑。【参考方案14】:将此代码写入键入的键
char c=evt.getKeyChar();
if(!(Character.isDigit(c) || (c==KeyEvent.VK_BACK_SPACE || c==KeyEvent.VK_DELETE)))
getToolkit().beep();
evt.consume();
【讨论】:
(1-),不要玩 KeyEvents。 Swing 有更新更好的 API(如 DocumentFilter),适用于所有情况。如果您将文本“粘贴”到文本字段中,此解决方案将不起作用。 我怎样才能允许'-'和'.' (减号)在这种方法中?【参考方案15】:if (JTextField.getText().equals("") || !(Pattern.matches("^[0-9]+$", JTextField.getText())))
JOptionPane.showMessageDialog(null, " JTextField Invalide !!!!! ");
如果 JTextField.getText().equals("") ==-> 如果 JTextField 为空
if(!(Pattern.matches("^[0-9]+$", JTextField.getText()))) ==-> 如果
TextField 包含其他字符
JOptionPane.showMessageDialog(null, " JTextField 无效 !!!!!
");==-> 所以这条消息会分开
【讨论】:
这可能是答案,但添加足够的描述来支持它。【参考方案16】:在相关 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();
【讨论】:
【参考方案17】:使用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);
【讨论】:
【参考方案18】:一个非常简单的解决方案是使用动作监听器。
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());
【讨论】:
这只会删除最后一个字符。如果用户在中间插入一个字符呢?【参考方案19】:DataTF.addKeyListener(new KeyAdapter()
@Override
public void keyTyped(KeyEvent eve)
String AllowedData="0123456789.";
char enter = eve.getKeyChar();
if (!AllowedData.contains(String.valueOf(enter)))
eve.consume();
);
【讨论】:
以上是关于有没有办法只接受 JTextField 中的数值?的主要内容,如果未能解决你的问题,请参考以下文章
如何限制 UITextField 在 Swift 中只接受数字?
有没有办法在不触发 DocumentListener 的 removeupdate() 的情况下调用 JTextField.setText()?