Gibt es eine Möglichkeit, nur numerische Werte in einer JTextField
zu akzeptieren? Gibt es dafür eine spezielle Methode?
Da diese Frage sehr oft auftaucht, habe ich mir mehr Mühe gegeben, als ich normalerweise tun würde.
Meine Stimme geht an die JFormattedTextField
. IMO sollte jeder Swing-Entwickler eine verbesserte Version dieser Klasse in seinem Toolkit haben, da es ermöglicht, fast alles, was Sie sich vorstellen können, durch die richtige Auswahl von Format
zu validieren. Beispiele, für die ich es bereits verwendet habe:
String
nicht leer sein darfJSpinner
Es ermöglicht auch eine visuelle Rückmeldung, wenn die Eingabe ungültig ist, was beispielsweise beim InputVerifier
nicht der Fall ist. Es erlaubt dem Benutzer immer noch, irgendetwas einzugeben, aber dieser Wert wird einfach nicht akzeptiert, wenn er ungültig ist, und dieser Wert verlässt niemals die Benutzeroberfläche. Ich denke (aber auch das ist meine Meinung), dass es besser ist, dem Benutzer zu erlauben, ungültige Eingaben einzugeben, die nur automatisch entfernt werden, z. a DocumentFilter
. Ich würde einen Fehler vermuten, wenn ein Zeichen in ein Textfeld eingegeben wird und es nicht angezeigt wird.
Lassen Sie mich dies mit einigem Code veranschaulichen (eigentlich mit einigem Code). Zuerst die kleine Demoanwendung. Diese Anwendung zeigt nur ein JFormattedTextField
für Zahlen. Wenn Sie nur ein anderes Format verwenden, können Sie diese Komponente für völlig andere Überprüfungen wiederverwenden.
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;
}
}
das zeigt nur eine ImprovedFormattedTextField
und eine JButton
, die nur aktiviert werden, wenn die Eingabe gültig ist (aha, iss diese DocumentFilter
-Lösung). Es wird auch ein JTextArea
angezeigt, in dem der Wert jedes Mal gedruckt wird, wenn ein neuer gültiger Wert festgestellt wird. Durch Drücken der Taste wird der Wert angezeigt.
Der Code für ImprovedFormattedTextField
ist unten zusammen mit dem ParseAllFormat
zu finden, von dem er abhängt
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 );
}
}
} );
}
}
}
}
Die Klasse 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 );
}
}
Mögliche Verbesserungen:
setBackground
wird nicht von allen Look-and-Feels respektiert. Manchmal können Sie stattdessen setForeground
verwenden, aber auch das wird nicht von allen L & Fs garantiert respektiert. Für visuelle Rückmeldungen ist es möglicherweise besser, ein Ausrufezeichen neben dem Feld zu verwenden. Nachteil ist, dass dies ein Layout durcheinander bringen kann, wenn Sie plötzlich ein Symbol hinzufügen/entfernenFormat
zu verwenden, die eine Beschreibung/ein Beispiel für eine gültige Eingabe enthält, und diese als QuickInfo in JFormattedTextField
einzufügen.Diese Frage wurde als "exaktes Duplikat" einer anderen, inzwischen geschlossenen Frage zitiert. Die Antworten auf diese Frage waren so schlecht, dass ich inspiriert wurde, jedem zu helfen, der sie später finden könnte, indem er auf eine viel bessere Antwort für diesen Anwendungsfall verweist.
Es ist eine Antwort auf die geschlossene Frage & kann zusammengefasst werden als ..
JSpinner
.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 );
}
}
}
Obwohl es das reine Übel JFormattedTextField
gibt, gibt es keine einfache Möglichkeit, dies nur mit der Swing-Bibliothek zu tun. Am besten implementieren Sie diese Art von Features mit einer DocumentFilter
.
Ein Code, den ich zuvor erstellt habe.Ein bisschen Beschreibung.
Ein einfacher Ansatz besteht darin, JTextField zu subklassifizieren und createDefaultModel () zu überschreiben, indem eine angepasste PlainDocument-Unterklasse zurückgegeben wird. Beispiel - ein Textfeld nur für Ganzzahlen:
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);
}
}
Verarbeiten Sie die Eingabe in insertString () auf beliebige Weise.
Eine schnelle Lösung:
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);
}
}
};
Das Problem bei der obigen Lösung ist, dass der Benutzer die Tasten Löschen, Linkspfeil, Rechtspfeil oder Rücktaste im Textfeld nicht verwenden kann. Ich empfehle Ihnen daher, diese Lösung zu verwenden:
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.
}
}
};
Angesichts der Anzahl der Ansichten, die diese Frage bekommt, habe ich keine der oben genannten Lösungen für mein Problem gefunden. Ich entschied mich für ein benutzerdefiniertes PlainDocument , um meine Anforderungen zu erfüllen .. Diese Lösung gibt auch einen Signalton aus, wenn die maximale Anzahl der verwendeten Zeichen erreicht ist oder der eingefügte Text keine Ganzzahl ist.
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);
}
}
wie folgt impliziert:
JTextField textfield = new JTextField();
textfield.setDocument(new FixedSizeNumberDocument(textfield,5));
Erwägen Sie auch die Verwendung einer InputVerifier
.
Verwenden Sie formatter
, um das Textfeld zu formatieren.
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);
if (JTextField.getText().equals("") || !(Pattern.matches("^[0-9]+$", JTextField.getText()))) {
JOptionPane.showMessageDialog(null, " JTextField Invalide !!!!! ");
}
Probieren Sie dies in dem mit der Taste gedrückten Ereignis für das zugehörige JTextField aus.
private void JTextField(Java.awt.event.KeyEvent evt) {
// TODO add your handling code here:
char enter = evt.getKeyChar();
if(!(Character.isDigit(enter))){
evt.consume();
}
}
Eine sehr einfache Lösung ist die Verwendung eines Aktionslisteners.
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));
}
}
Sie können es auch für Double verwenden:
Double.parseDouble(TextField.getText());
schreibe diesen Code in Schlüssel eingegeben
char c=evt.getKeyChar();
if(!(Character.isDigit(c) || (c==KeyEvent.VK_BACK_SPACE || c==KeyEvent.VK_DELETE)))
{
getToolkit().beep();
evt.consume();
}
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();
}
}
});
Siehe JFormattedTextField .