Formatierungen von Textfeldern mit Java Swing
Damit der Benutzer einer Dialoganwendung gar nicht erst falsche Werte eingeben kann, gibt
es bei der Dialogerstellung mit Java Swing verschiedene Möglichkeiten die Textfelder zu
formatieren.
Falsche Werte können auf unterschiedliche Weise falsch sein:
- Lexikalisch
-
Unerlaubte Zeichen
Prüfung: während der Eingabe
Beispiel: Ein Buchstabe in einem Zahlenfeld
- Syntatktisch
-
Das Muster der Eingabe ist falsch
Prüfung: nach der Eingabe
Beispiel: Datum 1.1.2.04
- Semantisch
-
Der Wert ist nicht plausibel
Prüfung: bei der Verarbeitung
Beispiel: Geburtsdatum vor 1900
- Pragmatisch
-
Die Eingabe passt nicht zur Umgebung
Prüfung: vor, oder bei der Verarbeitung
Beispiel: Zu einem Projekt was in 2005 durchgeführt wird, gibt ein Benutzer folgendes Datum
ein: 1.2.2004
javax.swing.JFormattedTextField
Neben dem Standard Texteingabefeld javax.swing.JTextField, gibt es auch das vorformatierte
Textfeld JFormattedTextField, welches von ersterem abgeleitet ist.
Dem JFormattedTextField kann man Vorlagen oder Schablonen mitgeben, die mit der Klasse
javax.swing.text.MaskFormatter.MaskFormatter erstellt wurden:
Code:
javax.swing.JFormattedTextField myJFTextField = new javax.swing.JFormattedTextField();
javax.swing.text.MaskFormatter mf;
try {
mf = new javax.swing.text.MaskFormatter ("##,##");
}
catch (java.text.ParseException e) {}
javax.swing.text.DefaultFormatterFactory dff = new javax.swing.text.DefaultFormatterFactory(mf);
myJFTextField.setFormatterFactory(dff);
Zeichen für die Schablone:
* - beliebiges Zeichen
? - alphabetisches Zeichen (Prüfung isLetter()=TRUE)
# - nummerisches Zeichen (Prüfung isDigit()=TRUE)
A - alphanummerisches Zeichen (Prüfung isLetterOrDigit()=TRUE)
U - wie "?" jedoch immer groß
L - wie "?" jedoch immer klein
H - hexadezimales Zeichen
' _ (Apostroph) als Kennzeichen, wenn ein Platzhalter, als Buchstabe verwendet wird
Es gibt auch einige voreingestellte Formate, wie z. B. java.text.DecimalFormat für
Zahlen, Dezimalzahlen, Daten.
Code:
java.text.NumberFormat nummerformat = java.text.NumberFormat.getNumberInstance();
nummerformat.setMaximumFractionDigits(0); //keine Nachkommastellen
nummerformat.setGroupingUsed(false); //Keine gruppierung in 100er Schritten
java.text.DecimalFormat myDF = new java.text.DecimalFormat();
myDF.setMaximumFractionDigits(2); //Nachkommastellen einstellen
myDF.setMinimumFractionDigits(2);
java.text.DateFormat df = java.text.DateFormat.getDateInstance(java.text.DateFormat.SHORT, Locale.German);
// SHORT nummerisch, wie 12.13.52 oder 3:30pm
// MEDIUM länger Jan 12, 1952
// LONG noch länger January 12, 1952 oder 3:30:32pm
// FULL komplettes Datum Tuesday, April 12, 1952 AD oder 3:30:42pm PST.
//javax.swing.JFormattedTextFields mit den Formaten erstellen
javax.swing.jFTF_zahl = new javax.swing.JFormattedTextField(nummerformat);
javax.swing.jFTF_dezimal = new javax.swing.JFormattedTextField(myDF);
javax.swing.jFTF_date = new javax.swing.JFormattedTextField(df);
Bei der Verarbeitung im Programm benötigt man zum einen die Methode parse(String wert), um
an der Eingabe zu sehen, ob sie gültig ist, wenn nicht wirft sie eine
java.text.ParseExcepition. Zum anderen benötigt man die Methode format(double wert), um aus
der errechneten Zahl, wieder einen String zu bilden, der beispielsweise ins Ausgabetextfeld
geschrieben werden kann.
Normales JTextField mit javax.swing.InputVerifier
Eine andere Möglichkeit ist die Nutzung des normalen JTextFields mit InputVerifier. Dabei
schreibt man sich eine eigene Klasse, die von der abstrakten Klasse
javax.swing.InputVerifier abgeleitet ist. Dem JTextField hängt man dann mit der Methode
setInputVerifier(javax.swing.InputVerifier iV) den Verifier an.
Wenn man einen Wert in ein Textfeld geschrieben hat und dieser korrekt ist erwartet man
normalerweise, dass man ins nächste Textfeld springen kann. Mit dem InputVerifier kann man
den Focus bei falscher Eingabe aber auf dem Textfeld belassen, so dass der Benutzer nicht
ins nächste Feld kommt, solange die Eingabe falsch ist.
In der eigenen Klasse überschreibt man folgende Methoden:
- public boolean verify (JComponent input)
-
sollte keine Einflüsse auf die Komponente haben und TRUE zurückgeben, wenn das JTextField
die Bedingung zur Weitergabe des Focus erfüllt und FALSE, wenn nicht
- public boolean shouldYieldFocus (JComponent input)
-
darf die Komponente beeinflussen und ruft verify(JComponent input) auf
Beispiel für einen InputVerifier: SpecialDateInputVerifier
public class SpecialDateInputVerifier extends javax.swing.InputVerifier {
public SpecialDateInputVerifier() {
super();
}
public boolean verify(javax.swing.JComponent input) {
javax.swing.JTextField jTF = (javax.swing.JTextField) input;
String sInput = jTF.getText();
//DateChecker ist eine Klasse, welche Daten auf Korrektheit prüft
return myutil.DateChecker.checkDate (sInput);
}
public boolean shouldYieldFocus(javax.swing.JComponent input) {
if (!verify(input)) {
//Textfeld Vordergrund rot färben
input.setForeground(java.awt.Color.RED);
return false;
}
else {
input.setForeground(java.awt.Color.BLACK);
return true;
}
}
}
Damit eine Komponente, also ein Textfeld überhaupt den Verifier aufruft muss ihre
Eigenschaft verifyInputWhenFocusTarget auf TRUE gesetzt werden (Standardeinstellung).
Ein InputVerifier darf keine Meldung mit JOptionPane anzeigen, denn würde er es tun
durchliefe er eine Endlosschleife.
Formatierung mit Hilfe einer Dokument-Klasse
Die Inhalte von Textfeldern werden in Java-Swing durch Objekte dargestellt,
welche die Schnittstelle java.text.Document implementieren. Standardmäßig ist das
ein Objekt der Klasse javax.swing.text.PlainDocument. Dieses allgemeine Document lässt
alle Tastaturzeichen zu.
Will man nur bestimmte Zeichen zulassen, schreibt man sich eine eigene Klasse, die
von javax.swing.text.PlainDocument abgeleitet ist und überschreibt die Methode
insertString() und eventuell removeString(), dahingehend, dass nur die gewünschten
Änderungen in das Dokument aufgenommen werden.
Beispiel für eine Dokumentenklasse für Dezimalzahlen
public class DecimalDocument extends javax.swing.text.PlainDocument{
public DecimalDocument() { }
public void insertString(int offset, String str, javax.swing.text.AttributeSet a)
throws javax.swing.text.BadLocationException {
//Dezimaltrenner, je nach Land abfragen und einsetzen
char decimalSeparator = (new java.text.DecimalFormatSymbols()).getDecimalSeparator();
//Zeichenkette mit den gültigen Zeichen
String valid = "+-.0123456789" + decimalSeparator;
for (int i=0; i<str.length();i++) {
if (valid.indexOf(str.charAt(i)) == -1) {
//Beep
System.out.println("Falsches Zeichen");
java.awt.Toolkit.getDefaultToolkit().beep();
return;
}
//Wichtig Aufruf der übergeordneten Methode
super.insertString(offset, str, a);
}
}
}
Beispiel für eine insertString-Methode für Namen
public void insertString(int offset, String str, javax.swing.text.AttributeSet a)
throws javax.swing.text.BadLocationException{
//Nur Zeichenketten zulassen, die nur aus Buchstaben bestehen und deren
//1. Buchstabe ein Großbuchstabe ist und deren andere Buchstaben Klein sind
if ((Character.isLowerCase(str.charAt(0))) & (offset ==0)) {
System.out.println("Erster Buchstabe muss Groß sein, bsp: 'Valk, van der'");
System.out.println("Offset: "+offset);
return;
}
String disallowed = "+-:;!\"§$%&/()=?1234567890*/#~<>|";
for (int i=0; i<str.length(); i++){
if (disallowed.indexOf(str.charAt(i)) >= 0){
System.out.println("Falsches Zeichen im Namen");
return;
}
super.insertString(offset,str, a);
}
}