Swing Validator

Hatschi

Aktives Mitglied
Wie kann ich ein (J)TextField validieren und zwar nach jedem eingegebenen Buchstaben?
Einen InputVerifier kann ich ja nicht nehmen, der kontrolliert ja die Daten erst, nachdem alles fertig eingeben wurde und einen KeyListener kann ich auch nicht nehmen, sonst müsste ich ja alle Kombinationen (wie zB. ALT + 2) berücksichtigen.
Was ich machen will: Ein Textfeld in das ich nur Zahlen eingeben darf (inklusive vorangestelltes Minus). Wenn man eine falsche Taste drückt, soll einfach das Zeichen nicht angezeigt werden.
 

kay73

Bekanntes Mitglied
Da hast Du ein schönes ätzendes Problem. Verwende einen DocumentListener:
Java:
import java.util.regex.Pattern;

import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;

class Validator implements DocumentListener {
	private JTextComponent textComponent;

	private String validContent = "";
	
	protected boolean isValidContent(final String content) {
		return Pattern.matches("\\-?[0-9]+", content);
	}

	private final void validate() {
		final Document doc = textComponent.getDocument();
		final DocumentListener l = this;
		try {
			final String currentContent = doc.getText(0, doc.getLength()).trim();
			if (isValidContent(currentContent)) {
				validContent = currentContent;
			} else {
				SwingUtilities.invokeLater(new Runnable() {
					@Override
					public void run() {
						doc.removeDocumentListener(l);
						textComponent.setText(validContent);
						doc.addDocumentListener(l);
					}
				});
			}
		} catch (final BadLocationException e) {
			e.printStackTrace();
		}
	}

	public Validator(final JTextComponent textComponent) {
		this.textComponent = textComponent;
		textComponent.getDocument().addDocumentListener(this);
	}

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

	@Override
	public void insertUpdate(DocumentEvent e) {
			validate();	
	}

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

public class ValidInputDemo {

	public static void main(String[] args) {
		final JFrame frame = new JFrame();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(300,80);
		
		final JTextField numberTextField = new JTextField(80);
		new Validator(numberTextField);
		
		frame.getContentPane().add(numberTextField);		
		frame.setVisible(true);
	}
}
Mit dem "Abhören" des Keyboards und der Maus kommst Du auf die Dauer nicht weit, man könnte auch aus der Zwischenablage einfügen usw. Ich dachte erst, man muss mit InputMethodListenern arbeiten. Problem: Funktioniert einfach nicht; Google liefert etliche Treffer mit Fragen von Leuten, bei denen das auch nicht klappt. Es wird daher vorgeschlagen, mit DocumentListenern zu arbeiten, denn damit hat man eine Art ganzheitlichen Ansatz, der unabhängig von Eingabemethoden funktioniert.

Tricky wird es, wenn man bei der Validiermethode Text in die JTextComponent einfügt: Man triggert u. U. eine Endloskaskade aus Events und der Code zum Neusetzen des textes muss im Swing Worker Thread laufen.

Ein Glitch ist noch, dass beim Einfügen illegaler Zeichen der Cursor ans Ende der Eingabe hüpft; da lässt sich vielleicht noch was mit dem CaretListemer machen.
 
Zuletzt bearbeitet:

eRaaaa

Top Contributor
Einfacher geht das wohl über einen DocumentFilter:

Java:
		final JTextField textField = new JTextField(20);
		((AbstractDocument) textField.getDocument()).setDocumentFilter(new DocumentFilter() {
					@Override
					public void replace(FilterBypass fb, int offset,
							int length, String text, AttributeSet attrs)
							throws BadLocationException {
						if (text.matches("^-?\\d+")|| (textField.getText().length() == 0 && text.equals("-"))) {
							super.replace(fb, offset, length, text, attrs);
						} else {
							Toolkit.getDefaultToolkit().beep();
						}
					}
				});

wahrscheinlich geht die if-Abfrage auch etwas eleganter/besser/leichter, tut aber was es soll...

EDIT: tut es doch nicht :D
Vllt besser so etwas?
Java:
						String newText = new StringBuilder(textField.getText()).insert(offset, text).toString();
						if (newText.matches("^-?\\d*")) {
							super.replace(fb, offset, length, text, attrs);
						} else {
							Toolkit.getDefaultToolkit().beep();
						}
:autsch:
 
Zuletzt bearbeitet:

mohrenkopf

Mitglied
Ich habe mal sowas für eine GUI gebaut.

Ist prinzipiell ein JTextField für Integer, einfach anstatt eines JTextField im GUI-Editor reinziehen.

Du kannst festlegen, dass nur Werte zwischen 1 und 100 gültig sein sollen. Sobald der Benutzer rummtippt ändert sich die Textfarbe zwischen schwarz (gültig) und grau(ungültig)

rückgabewert ist ein Integer-Objekt (oder null, falls nicht im gültigen bereich), so lässt sich in einer Methode schnell überprüfen, ob die Werte gültig sind.

Java:
public class AKTextFieldLimitedInteger extends AKTextFieldInteger
{
    private int limitMinValue=0;
    private int limitMaxValue=100;

    public AKTextFieldLimitedInteger()
    {
        updateToolTipText();
    }

    private void updateToolTipText()
    {
        this.setToolTipText("Only integer values between "+limitMinValue+" and "+limitMaxValue);
    }

    public int getLimitMaxValue()
    {
        return limitMaxValue;
    }

    public void setLimitMaxValue(int limitMaxValue)
    {
        this.limitMaxValue = limitMaxValue;
        updateToolTipText();
    }

    public int getLimitMinValue()
    {
        return limitMinValue;
    }

    public void setLimitMinValue(int limitMinValue)
    {
        this.limitMinValue = limitMinValue;
        updateToolTipText();
    }
    
    private boolean isInRange(int value)
    {
        return ((limitMinValue<=value) && (value<=limitMaxValue));
    }

    @Override
    public Integer getInteger()
    {
        Integer i = super.getInteger();

        if((i!=null) && (isInRange(i)))
        {
            return i;
        }
        else
        {
            return null;
        }


    }

}

Java:
public class AKTextFieldInteger extends AbstractNumberTextField
{
    public AKTextFieldInteger()
    {
        super();
        this.setToolTipText("Only Integer values!");
     }

    @Override
     protected void contentChanged()
     { 
         this.setColorToValid( getInteger()!=null );
     }

    /**
     * Liefert direkt Integer des eingegebenen Wertes
     * null falls ungültige Zahl
     * @return
     */
    public Integer getInteger()
    {
        try{
            Integer i = Integer.valueOf(this.getText());
            return i;
        }
        catch(NumberFormatException e)
        {
            return null;
        }
    }

}


benutzt wird das so:

Java:
jIrgendeinButtongedrückActionPerformedoderwasweißich
{
   Integer wert1= akLimitedTextFieldInteger().getInteger();

    if(wert1==null) return; //Wert ungültig
    
    //wert gültig: weiter

}

OK, Thema war erledigt, aber vielleicht sucht ja mal jemand eine Copy&Paste-Lösung...
 

Ähnliche Java Themen

Neue Themen


Oben