Die hier vorgestellte Methode funktioniert mit Java 1.5 und ist als eine andere Alternative zur
Implementation im Document
(vgl. Wie kann ich ein
Textfeld erstellen, indem nur Zahlen eingegeben werden können?) zu sehen. Beide Varianten
haben Ihre Vor- und Nachteile.
Dieser Artikel beinhaltet hierbei im eigentlichen Sinne zwei Lösungen. Zum einem die Nutzung eines
javax.swing.text.MaskFormatter
als Möglichkeit auf einfache Art das Eingabeformat
vorzugeben. Zum anderen wird der Mechanismus der javax.swing.InputVerifier
erläutert, welcher eine wesentlich feinere Art der Eingabeprüfung zuläßt.
Der MaskFormatter bietet analog zum SimpleDateFormat eine einfache Möglichkeit eine Schablone festzulegen, welcher das Eingabeformat entsprechen muss. Im Gegensatz zu anderen Programmiersprachen (z.B. Dialog System von MicroFocus) wird hierbei die Schablone mitgeliefert.
formatter = new MaskFormatter ("##.##.####"); textField.setFormatterFactory(new DefaultFormatterFactory (formatter));Mit der ersten Zeile legen wir eine Schablone fest, die zwischen die getrennt von ein paar Punkten - wie unschwer zu erkennen ist - das Datumsformat festlegt. Die zweite Zeile weist dem Eingabefeld unseren Formatter zu. Die "reine" Zuweisung geht auch etwas einfacher, die hier dargestellte Form ist jedoch auch bei Erstellung eigener Textkomponenten funktional (siehe abschließendes Beispiel).
Mit Hilfe des MaskFormatter haben wir unseren ersten Schritt getan. Nicht immer reicht dieser jedoch aus.
Unser Beispiel zeigt hier schon einige Grenzen des MaskFormatter auf. So wird der Anwender über inkorekte
Eingaben nur schlecht informiert, indem das Eingabefeld im besten Fall leer bleibt. Die Lösung ist der
javax.swing.InputVerifer
. Dieses Interface hat nur zwei Methoden und die Implementation kann
unserem Eingabefeld mit einer Zeile hinzugefügt werden.
textfield.setInputVerifier(new DateInputVerifier());Die zwei Methoden
public boolean verify (final JComponent input)
und
public boolean shouldYieldFocus (final JComponent input)
müssen dabei implementiert werden.
Die verify
Methode sollte die tatsächliche (sachliche) Prüfung vornehmen, ohne die
Eingabekomponente zu verändern. Mit Hilfe von shouldYieldFocus
können wir die Fokusweitergabe
beeinflussen. Zudem ist hier der ideale Ort, um unseren Anwender über "unsere Auffassung eines korekten
Eingabewertes" zu informieren.
Das abschließende Beispiel ist die Erweiterung eines JFormattedTextField zur Datumseingabe. Es kombiniert dabei die beiden oben genannten Möglichkeiten um eine halbwegs sinnvolle Eingabe des Datums zu ermöglichen. Eine Erweiterung des Verifier, so dass z.B. als Monat keine 13 eingegeben werden kann, kann jedoch auf Basis dieses Beispiels leicht implementiert werden.
Um die Klasse zu verwenden ist lediglich eine Instanz zu erzeugen und optional noch ein Platzhalter
hinzuzufügen. Die beiden dargestellten Eingabefelder unterscheiden sich hierbei lediglich hinsichtlich der
Frage, ob ein Platzhalter definiert wurde.
/** * Copyright © 2006 Bastie - Sebastian Ritter */ package de.bastie.swing; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.logging.Logger; import java.awt.Color; import java.awt.Dimension; import java.awt.FontMetrics; import javax.swing.*; import javax.swing.border.Border; import javax.swing.text.DefaultFormatterFactory; import javax.swing.text.MaskFormatter; /** * Simple TextField to input a date. * * @author Bastie - Sebastian Ritter * @version 1.0 * @since Java 1.5 */ public class DateInputTextField extends JFormattedTextField { public DateInputTextField () { this.setFormatterFactory(new DefaultFormatterFactory (this.getDateMask())); this.setInputVerifier(new DateInputVerifier()); } /** * Preferred size for date mask * @return Dimension */ public Dimension getPreferredSize () { final double height = super.getPreferredSize().getHeight(); final FontMetrics fm = this.getFontMetrics(this.getFont()); int breite = fm.charsWidth (this.getDateMask ().getMask().toCharArray(), 0, this.getDateMask ().getMask().length ()); breite += this.getInsets().left+this.getInsets().right; if (this.getBorder() != null) breite += this.getBorder().getBorderInsets(this).left + this.getBorder().getBorderInsets(this).right; Dimension d = new Dimension(); d.setSize((double) breite, height); return d; } /** * Format of input * @return MaskFormatter */ protected MaskFormatter getDateMask () { MaskFormatter formatter = null; try { if (Locale.getDefault ().getLanguage ().equals (Locale.GERMANY.getLanguage())) { formatter = new MaskFormatter ("##.##.####"); } else { formatter = new MaskFormatter ("####-##-##"); } if (this.getPlaceHolder() != null) { formatter.setPlaceholderCharacter (this.getPlaceHolder()); } } catch (final ParseException ignored) { Logger.getLogger(this.getClass().getName()).throwing (this.getClass().getName(),"getDateMask", ignored); } return formatter; } private Character placeholder = null; /** * Set an Empty Character for delete the Input. If Empty Character is null, * a valid value need to input. * @param c Character */ public void setPlaceholder (final Character c) { this.placeholder = c; } /** * Return the char for delete the input or null if delete not allowed. * @return Character */ public Character getPlaceHolder () { return this.placeholder; } /** * Simple Date Verifier * @author Bastie - Sebastian Ritter * @version 1.0 */ protected static class DateInputVerifier extends InputVerifier { public boolean verify (final JComponent input) { if (input instanceof DateInputTextField) { return this.isAlowedDate((DateInputTextField)input); } else { return true; } } /** * Check the incomming Date * @param input DateInputTextField * @return boolean */ protected boolean isAlowedDate (final DateInputTextField input) { final DateFormat sdf = this.getDateFormat (); try { final Date d = sdf.parse (input.getText()); SwingUtilities.invokeLater(new Runnable () { public void run () { input.setText(sdf.format(d)); } }); return true; } catch (final ParseException notValidOrDelete) { if (input.getPlaceHolder() != null) { String noMaskValue = null; if (Locale.getDefault ().getLanguage ().equals (Locale.GERMANY.getLanguage ())) { noMaskValue = input.getText().replace ('.',input.getPlaceHolder ()); } else { noMaskValue = input.getText().replace ('-',input.getPlaceHolder ()); } for (char c : noMaskValue.toCharArray()) { if (c != input.getPlaceHolder()) return false; } return true; } return false; } } /** * Return i18n DateFormat * @return DateFormat */ protected DateFormat getDateFormat () { if (Locale.getDefault().getLanguage().equals(Locale.GERMANY.getLanguage())) { return new SimpleDateFormat ("dd.MM.yyyy"); } else { return new SimpleDateFormat("yyyy-MM-dd"); } } /** * Change the gui. * @param input the JComponent to verify * @returnall rights reserved © Bastie - Sebastian Ritter @: w³: http://www.Bastie.detrue
when valid,false
when invalid * */ public boolean shouldYieldFocus (final JComponent input) { if (!verify(input)) { input.setForeground(Color.RED); input.setBorder(BorderFactory.createEtchedBorder(Color.RED, new Color (255,50,50))); return false; } else { input.setForeground(Color.BLACK); input.setBorder((Border)UIManager.getLookAndFeelDefaults().get("TextField.border")); return true; } } } }