Swing-Textfeld Zeichenbeschränkung/Formatierung

Einen schönen Sonntag allerseits :)

Ich stehe gerade vor folgendem Problem: Ich habe eine Swing-GUI und darin u.a. einige Textfelder. Nun möchte ich die Eingabe auf numerische Ausdrücke beschränken. Ein FormattedField hilft hier leider nicht weiter, da die Eingaben zu umfangreich sind.
So sollen als Eingabe z.B. nur Zahlen erlaubt sein, jedoch sollen leere Textfelder eine Zeichenfolge (z.B. "Anzahl" oder "Menge") anzeigen und ihre Schriftfarbe ändern, wenn sie den Fokus verlieren und leer sind, mit FormattedFields funktioniert das nicht.

Außerdem soll ein Textfeld nicht nur numerische Ausdrücke in Dezimalschreibweise, sondern auch z.B. "1 1/2" entgegennehmen können. Das "1 1/2" nachher intern als 1,5 gewertet wird könnte ich mit ein paar String-Schiebereien bewerkstelligen-aber eine Eingabe wie "1//2" möchte ich gerne unterbinden.

Ich habe bereits verschiedene Lösungsansätze gefunden, jedoch hat mir keiner so richtig weitergeholfen. Ein solcher Ansatz lief über eine Document-Klasse-den konnte ich jedoch leider nicht so recht nachvollziehen. Ein Document sammelt und ordnet doch nur mehrere Strings, wenn ich das richtig verstanden habe.

Hat jemand von euch vielleicht eine Idee?
 
Das mit dem Document ist die korrekte Herangehensweise: Ein Document enthält den Inhalt des Textfeldes. Wenn sich dieser Inhalt ändert, werden Events an alle registrierten DocumentListener versandt.

Beispiele findest du z.B. im JavaSE Tutorial von Oracle oder wie immer bei stackoverflow.
 
Hallo ScharzeBeere

Vielen Dank-das hilt mir doch schonmal viel weiter. Ich glaub ich habs so ungefähr verstanden, werde mich mal mehr mit der Bererbungshierarchie von Document beschäftigen. So eine grafische Darstellung (wie man sie für Swing zu Hauf findet) wäre nicht schlecht.

Ein paar Fragen zu Syntax-Tricksereien hätte ich noch:
Code:
public class DocumentEventDemo ... {     [I]...//where initialization occurs:[/I]     textField = new JTextField(20);     textField.addActionListener(new MyTextActionListener());     textField.getDocument().addDocumentListener(new MyDocumentListener());     textField.getDocument().putProperty("name", "Text Field");      textArea = new JTextArea();     textArea.getDocument().addDocumentListener(new MyDocumentListener());     textArea.getDocument().putProperty("name", "Text Area");     ...  class MyDocumentListener implements DocumentListener {     String newline = "\n";       public void insertUpdate(DocumentEvent e) {         updateLog(e, "inserted into");     }     public void removeUpdate(DocumentEvent e) {         updateLog(e, "removed from");     }     public void changedUpdate(DocumentEvent e) {         //Plain text components do not fire these events     }      public void updateLog(DocumentEvent e, String action) {         Document doc = (Document)e.getDocument();         int changeLength = e.getLength();         [COLOR=DarkOrange]displayArea.append[/COLOR](             changeLength + " character" +             ((changeLength == 1) ? " " : "s ") +             action + doc.getProperty("name") + "." + newline +             "  Text length = " + doc.getLength() + newline);     } }
Wo kommt denn das Objekt displayArea her? Ich sehe nirgendwo, daß da der Konstruktor aufgerufen wurde. Dieser Codeschnipsel ist aus dem Oracle-Link von dir.


Dann habe ich noch einen weiteren Codeschnipsel gefunden (Textfeld bekommt einen Zeichenfilter), hier irritiert mich jedoch die for-Schleife:

Code:
[URL="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+jtextfield"][COLOR=#003399]JTextField[/COLOR][/URL] f [COLOR=#339933]=[/COLOR] [COLOR=#000000][B]new[/B][/COLOR] [URL="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+jtextfield"][COLOR=#003399]JTextField[/COLOR][/URL][COLOR=#009900]([/COLOR][COLOR=#009900])[/COLOR][COLOR=#339933];[/COLOR]
         [COLOR=#000000][B]final[/B][/COLOR] ArrayList[COLOR=#339933]<[/COLOR]Character[COLOR=#339933]>[/COLOR] forbiddenChars [COLOR=#339933]=[/COLOR] [COLOR=#000000][B]new[/B][/COLOR] ArrayList[COLOR=#339933]<[/COLOR]Character[COLOR=#339933]>[/COLOR][COLOR=#009900]([/COLOR][URL="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+arrays"][COLOR=#003399]Arrays[/COLOR][/URL].[COLOR=#006633]asList[/COLOR][COLOR=#009900]([/COLOR][COLOR=#0000ff]'g'[/COLOR],[COLOR=#0000ff]'k'[/COLOR],[COLOR=#0000ff]' '[/COLOR][COLOR=#009900])[/COLOR][COLOR=#009900])[/COLOR][COLOR=#339933];[/COLOR] [COLOR=#666666][I]//Liste mit nicht erlaubten Zeichen[/I][/COLOR]
         f.[COLOR=#006633]setDocument[/COLOR][COLOR=#009900]([/COLOR][COLOR=#000000][B]new[/B][/COLOR] [URL="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+plaindocument"][COLOR=#003399]PlainDocument[/COLOR][/URL][COLOR=#009900]([/COLOR][COLOR=#009900])[/COLOR] [COLOR=#009900]{[/COLOR] [COLOR=#666666][I]//Erzeugt eine neue anonyme Subklasse von PlainDocument(default-implementation von Document) [/I][/COLOR]
             @Override
             [COLOR=#000000][B]public[/B][/COLOR] [COLOR=#000066][B]void[/B][/COLOR] insertString[COLOR=#009900]([/COLOR][COLOR=#000066][B]int[/B][/COLOR] offs, [URL="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string"][COLOR=#003399]String[/COLOR][/URL] str, [URL="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+attributeset"][COLOR=#003399]AttributeSet[/COLOR][/URL] a[COLOR=#009900])[/COLOR] [COLOR=#666666][I]//überschreibt die Funktion von PlainDocument[/I][/COLOR]
                     [COLOR=#000000][B]throws[/B][/COLOR] [URL="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+badlocationexception"][COLOR=#003399]BadLocationException[/COLOR][/URL] [COLOR=#009900]{[/COLOR]
                 [COLOR=#000000][B]for[/B][/COLOR] [COLOR=#009900]([/COLOR][URL="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+character"][COLOR=#003399]Character[/COLOR][/URL] c [COLOR=#339933]:[/COLOR] forbiddenChars[COLOR=#009900])[/COLOR] [COLOR=#009900]{[/COLOR]
                     str.[COLOR=#006633]replace[/COLOR][COLOR=#009900]([/COLOR][URL="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string"][COLOR=#003399]String[/COLOR][/URL].[COLOR=#006633]valueOf[/COLOR][COLOR=#009900]([/COLOR]c[COLOR=#009900])[/COLOR], [COLOR=#0000ff]""[/COLOR][COLOR=#009900])[/COLOR][COLOR=#339933];[/COLOR] [COLOR=#666666][I]//Löscht alle nicht erlaubten Zeichen[/I][/COLOR]
                 [COLOR=#009900]}[/COLOR]
                 [COLOR=#000000][B]super[/B][/COLOR].[COLOR=#006633]insertString[/COLOR][COLOR=#009900]([/COLOR]offs, str, a[COLOR=#009900])[/COLOR][COLOR=#339933];[/COLOR] [COLOR=#666666][I]//Ruft die Funktion von PlainDocument auf, die sich dann um das tatsächliche inserten kümmert[/I][/COLOR]
             [COLOR=#009900]}[/COLOR]
         [COLOR=#009900]}[/COLOR][COLOR=#009900])[/COLOR][COLOR=#339933];[/COLOR]
Ich kenne es zwar auch, daß man im Kopf der for-Schleife allerhand bis alles weglassen kann oder innerhalb der Schleife implementieren kann, aber irgendeine Form einer Count-Variable braucht man doch, sofern es keine Endlosschleife ist oder irgendwann per if(...){break;} abgebrochen wird. Hier wird jedoch weder etwas hochgezählt, noch erkenne ich irgendeine Abbruchbedingung. Rattert for() denn eine Feldvariable automatisch ab, wenn ich sie in den Schleifenkopf setze? Ich habe das bisher noch nie gesehen.


Edit:
Kann ich den Listener nachträglich auch mit weiteren Eigenschaften versehen? Ich denke da an sowas wie "Enthält bereits ein Leerzeichen ja/nein" um, Muster besser zu erkennen die bestimmte Zeichen wie ',' nur einmal oder nur an bestimmter Stelle zulassen.
Oder muß das eine eigene Klasse werden die von documentListener erbt?
 
Zuletzt bearbeitet:
Wo kommt denn das Objekt displayArea her? Ich sehe nirgendwo, daß da der Konstruktor aufgerufen wurde. Dieser Codeschnipsel ist aus dem Oracle-Link von dir.

Hier findest du den gesamten Code des Beispiels. Dort findest du dann auch die displayArea-Variable:

Code:
displayArea = new JTextArea();
displayArea.setEditable(false);
JScrollPane displayScrollPane = new JScrollPane(displayArea);
displayScrollPane.setPreferredSize(new Dimension(200, 75));

Dann habe ich noch einen weiteren Codeschnipsel gefunden (Textfeld bekommt einen Zeichenfilter), hier irritiert mich jedoch die for-Schleife:

[...]

Ich kenne es zwar auch, daß man im Kopf der for-Schleife allerhand bis alles weglassen kann oder innerhalb der Schleife implementieren kann, aber irgendeine Form einer Count-Variable braucht man doch, sofern es keine Endlosschleife ist oder irgendwann per if(...){break;} abgebrochen wird.

Auch, wenn sie in Java nicht explizit so genannt wird, so handelt es sich hierbei um eine einfache foreach-Schleife, in der jedes Element eines Sets, einer Liste oder eines Arrays durchlaufen wird. Dieses Element wird dann in der Variablen vor dem Doppelpunkt gespeichert. Die Quellstruktur wird nach dem Doppelpunkt angegeben. Z.B. würde

Code:
String[] personen = new String[]{"Nadine","Dieter","Sonja","Peter"};
int i=0;
for(String person : personen) {
System.out.println("Hallo "+person+". Du bist die "+i+"-te Person.");
i++;
}

alle Personen aus dem Array personen grüßen. Diese Form der for-Schleife verwendest du überlicherweise dann, wenn du keine Zählvariable brauchst, sondern nur auf die einzelnen Elemente einer Struktur zugreifst. Wenn es keine Elemente mehr gibt, so bricht die Schleife ab. Die Zählvariable kannst du aber, wie oben zu sehen, leicht nachrüsten.

Vorsicht: Die Reihenfolge ist z.B. bei Sets nicht immer die gleiche ;)

Kann ich den Listener nachträglich auch mit weiteren Eigenschaften versehen? Ich denke da an sowas wie "Enthält bereits ein Leerzeichen ja/nein" um, Muster besser zu erkennen die bestimmte Zeichen wie ',' nur einmal oder nur an bestimmter Stelle zulassen.
Oder muß das eine eigene Klasse werden die von documentListener erbt?

Genau genommen erbst ("extends") du nicht von DocumentListener, sondern du implementierst das Interface DocumentListener ("implements"). In Bezug auf deine Frage ist diese Unterscheidung recht wichtig, denn Interfaces kann man nicht instanziieren, sondern müssen immer erst durch Klassen implementiert werden, die man dann wiederum instanziieren kann. Das funktioniert allerdings auch implizit, z.B.

DocumentListener dl = new DocumentListener() {

@Override
public void insertUpdate(DocumentEvent e) {
// ...
}

@Override
public void removeUpdate(DocumentEvent e) {
// ...
}

@Override
public void changedUpdate(DocumentEvent e) {
// ...
}

};

Um auf deine Frage zurückzukommen: Natürlich kannst du beliebige Listener registrieren, die unterschiedliche Checks durchführen. Mein Tipp: Einfach ausprobieren ;)
 
Zuletzt bearbeitet:
Interfaces...da klingelt doch was...hab ich doch gestern erst gelesen was man damit macht.

Prima...recht herzlichen Dank für deine Ausführungen. Jetzt ist mir auch klar warum ich mit den Codeschnipseln dauernd kaum was anfangen konnte. :)
 
Ein Problem habe ich noch:

Ich habe jetzt meine Klasse definiert:
Code:
public class Zahlenfeld extends javax.swing.JTextField implements java.awt.event.FocusListener, javax.swing.event.DocumentListener {
    private String Titel;
    private Color Titelfarbe;
    
    private void ZahlenfeldFocusGained(java.awt.event.FocusEvent evt) {                                             
        if(Zahlenfeld.getText().equals(Titel)){
            Zahlenfeld.setForeground(Color.black);
            Zahlenfeld.setText("");
        }
    }                                            
    private void ZahlenfeldFocusLost(java.awt.event.FocusEvent evt) {                                           
        if(Zahlenfeld.getText().equals("")||Zahlenfeld.getText().equals(Titel)){
            Zahlenfeld.setForeground(Color.Titelfarbe);
            Zahlenfeld.setText(Titel);
        }
    }
    public Zahlenfeld(String Titel, Color Titelfarbe){
        this.Titel=Titel;
        this.Titelfarbe=Titelfarbe;
    }
}

Weiter unten habe ich zwei Methoden, die das FocusListener-Event "GetFocus" und "LostFocus" auswerten. Nun meldet Netbeans mir aber "non-static method "getText() can not be referenced from a static context", bei setForeground() das Gleiche.

Jetzt ist aber weder meine Klasse noch irgendeine Methode, die ich geschrieben habe, als static deklariert. Warum tut Netbeans das?

Die Klasse soll mir als Vorlage für eine Textbox dienen, die eine verbesserte Benutzerführung ermöglicht. Wenn die Textbox leer ist soll sie selbstständig anzeigen, was das Programm als Eingabe erwartet.
 
Der Übersichtlichkeit halber solltest du dich an folgende Konventionen halten:
1. Variablen und Methoden immer mit einem Kleinbuchstaben beginnen
2. Klassen immer mit einem Großbuchstaben beginnen

Der Fehler liegt darin, dass du "Zahlenfeld.getText()" in einem statischen Kontext aufrufst. Allerdings ist diese Funktion nicht als "static" markiert. Willst du die Funktion in einem nicht-statischen-Kontext aufrufen, d.h. in Abhängigkeit zur Instanz der eigenen Klasse, so musst du "this.getText()" schreiben. Eine genaue Erklärung zum Schlüsselwort "static" findest du z.B. hier.
 
Zuletzt bearbeitet:
Der Übersichtlichkeit halber solltest du dich an folgende Konventionen halten:
1. Variablen und Methoden immer mit einem Kleinbuchstaben beginnen
2. Klassen immer mit einem Großbuchstaben beginnen

Danke...das werde ich beherzigen.

Das mit dem statischen Kontext hab ich schon verstanden, und auch was static mit der Klase (bzw. Methode/Eigenschaft) macht ist mir auch klar. Mir war nur nicht klar gewesen, warum Netbeans einen statischen Kontext erkannt hat da ich ja nichts als statisch markiert habe.
Aber nach einigem Nachdenken ist es dann doch logisch.

Ich danke dir sehr für deine Hilfe bis hierher. :)
Mal sehen, vielleicht veröffentliche ich mein Programm hier wenn ich es zufriedenstellend vollendet habe.

Ein paar Fragen habe ich aber noch:
Ich möchte mein Textfeld jetzt gern im GUI-DEsigner von Netbeans einbinden. Wie man neue Elemente z.B. aus einer .jar einbindet weiß ich und hab ich auch schon gemacht. Wenn ich "von Projekt einbinden" nutzen will findet er meine Zahlenfeld-Klasse nicht.
Hast du ne Idee, wie ich das beheben kann?
 
Habs doch hingekriegt...ich hatte keinen Konstruktor ohne Parameter vorgesehen. Matisse will .java-Dateien nicht. Ich habe (eher aus Zufall) grad in Netbeans im Rechtsklick-Menü einen kleinen versteckten Eintrag hinter "Extras" "Add to Palette" gefunden...und das hat (nach Behebung des konstruktor-Problems) funktioniert. :)

Kann mir vielleicht jemand sagen, warum ein parameterloser Konstruktor unbedingt nötig ist? Ok, ich kann ja jede beliebige Eigenschaft über eine Set/Get-Methode ändern, aber wo ist der Sinn dieser Einschränkung? Sind denn Objekte, die zwingend eine Eigenschaft zu ihrer "Geburtszeit" vorraussetzen, so undenkbar?
 
Zurück
Oben