# Textfeld Währungszahlen und automatische Aktualisierung



## FranzFerdinand (14. Feb 2015)

Hallöle,

ich möchte ganz gerne einen ganz primitiven Währungsrechner basteln. Einfach nur als Beispiel, ohne dass der jetzt täglich irgendwelche Wechselkurse aus dem Internet zieht, er arbeitet einfach mit primitiven Standardwerten, die ich einmalig festlege.

Aktuell sieht es so aus:

```
package rechner;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Locale;

import javax.swing.JFormattedTextField;
import javax.swing.JOptionPane;
import javax.swing.text.NumberFormatter;

public class Umrechnen {

	private NumberFormat format = NumberFormat.getInstance();
	private NumberFormatter formatter = new NumberFormatter(format);
	
	public Umrechnen() {
		format.setGroupingUsed(false); 
		formatter.setAllowsInvalid(false);
		
		JFormattedTextField eurofeld = new JFormattedTextField(formatter);
		JFormattedTextField dollarfeld = new JFormattedTextField(formatter);
		JFormattedTextField pfundfeld = new JFormattedTextField(formatter);
		JFormattedTextField frankenfeldfeld = new JFormattedTextField(formatter);
		Object[] zahlenfrage = {"<html><b>Bitte gib Dein Startgeld ein.</b></html>", "Euronen", eurofeld, "Dollar", dollarfeld, "Pfund", pfundfeld, "Franken", frankenfeld};
		JOptionPane pane = new JOptionPane(zahlenfrage, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION);
		pane.createDialog(null, "Währungsrechner").setVisible(true);
	}
	
	public static void main(String[] args) {
		new Umrechnen();
	}

}
```

Das ist erst einmal nicht viel, das entspricht erst einmal nur der klassischen Eingabe. Bisher nichts weiter, wie ich dann Zahlen umrechne krieg ich schon noch hin.

Ich scheitere an zwei Dingen, die ich vorher machen möchte:

1) Ich habe mit dem NumberFormat und dem FormattedTextField erreichen können, dass die Textfelder ausschließlich Zahlen annehmen. Aber das entspricht nur ganzen Zahlen. Ich möchte gerne wissen, wie ich dem sagen kann, dass er auch Komma annimmt (meinetwegen auch Punkt) und das im Format für Währung macht. Also dass er stets etwas nach dem Format "*,##" hat.

Ich habe dazu ein wenig gegooglet und bin auf viel Zeuchs mit setCurrency und mit DecimalFormats gestoßen.
Aber ich hab das immer entweder nicht verstanden oder es hat mir die Eigenschaft, dass das Programm generell nur Zahlen annimmt zerschossen.

Kann mir jemand erklären, wie ich das ganze so einbauen kann, wie beschrieben?

2) Etwas fortgeschrittener. Das erste ist erst einmal wichtiger.
Das Programm ist so aufgebaut, dass es nur vier Eingabefelder für die Währungen gibt.
Es gibt keinen Button wo ich draufklicke, nach welche dann ausgerechnet wird.
Ich möchte das so haben, dass er das live umrechnet. Also wenn ich in im Feld für Pfund eine Null wegnehme, dann soll er automatisch und sofort in den anderen drei Feldern entsprechend agieren. 
Das Rechnen selbst ist ja kein Problem. Nur dieser Live-Effekt, den man meines Erachtens mit Keyevents machen kann, der ist mir noch etwas unklar.


Kann mir einer von euch meine beiden Probleme genauer erklären?

Danke schön!

Gruß
Lukas


----------



## jupper (14. Feb 2015)

Zu 1.

Du könntest die Eingabe als String einlesen und in einen float oder double umwandeln. Damit dürfte es gehen.


----------



## jupper (14. Feb 2015)

Zu 2.

Evtl.- könntest du das mit einem KeyListener lösen.

JAVA: 14.2.3 Tastatureingaben


----------



## Harry Kane (14. Feb 2015)

Zu 1: Dann verwende doch einfach ein anderes Format, z. B. new DecimalFormat("#.00").


----------



## FranzFerdinand (14. Feb 2015)

Harry Kane hat gesagt.:


> Zu 1: Dann verwende doch einfach ein anderes Format, z. B. new DecimalFormat("#.00").



Hallöle und Danke für die Antwort,

das hat hingehauen. Ich habe das NumberFormat durch das hier ersetzt:

```
private DecimalFormat d = new DecimalFormat("#.00");
```
Das war jetzt irgendwie einfacher als gedacht, peinlich.
Ich hatte das schon einmal probiert und war daran gescheitert. Damals hatte ich vermehrt im Internet das hier gesehen:

```
new DecimalFormat("'$'#.00");
```
Hat nie funktioniert.

Aber nun gut, es arbeitet!

Vielen Dank!

Das wäre geklärt. 

Zu 2. Ich werde mich wohl einmal damit beschäftigen müssen mit den KeyListenenern. Ich muss herausfinden,wie ich herausfinden kann, in welchem der vier Textfelder etwas eingegeben wurde. Dann klappt das.

Noch ein kleines 3.: Mir fehlt da eine kleine Sache und ich habe im Netz gelesen, das geht wohl nicht. Ich überzeuge mich lieber nochmal davon:

Also ich habe das ja nur als Dialogbox. Also ohne JFrame, Cancel-, Okaybuttons oder sonst was.
Bei JFrame kann ich immer "Exit_on_Close" nutzen. Mir geht es darum, dass Java geöffnet bleibt, wenn ich den Roten Knopf (in Windows das X zum schließen) klicke. Kann ich bei JOptionPane auch irgendwie herausfinden, ob der Nutzer das Fenster geschlossen hat oder geht das nur bei JFrame?

Vielen Dank!

Gruß
Lukas


----------



## fLooojava (15. Feb 2015)

Servus,

Ich leite dich mal auf nen alten Thread um, vielleicht findest du da deine Antwort .

http://www.java-forum.org/java-basics-anfaenger-themen/119484-close-joptionpane.html

Was ich dazu auch noch gefunden habe, was du glaube ich brauchen könntest:
java - Using joptionpane to exit the program - Stack Overflow

EDIT: Es ist zwar nicht genau dass, was du machen möchtest - aber zumindest nen Ansatz. 


Ansonsten meld dich nochmals und frag. 

cheers, flo


----------



## censored (15. Feb 2015)

Zu 2. :
How to Write a Property Change Listener (The Java™ Tutorials > Creating a GUI With JFC/Swing > Writing Event Listeners)


----------



## FranzFerdinand (15. Feb 2015)

Hallöle,

also die Probleme 2 und 3 sind noch offen bei mir.
Zu 3: Das Problem ist, ich habe ein einziges JDialogFenster ohne JFrame, ohne einen Button, ich kann also auch nicht auf Ok-Cancel_Option oder sowas zurückgreifen.

Es ist anscheinend Technisch nicht möglich Java einfach nur zu sagen, mach Java zu, wenn ich Kreuz klicke.

Alternativ könnte man ja auch eine Okay-Cancel-Option machen, die Okay und cancel Buttons irgendwie ausblenden, sodass es so aussieht wie vorher und dann die Tatsache ausnutzen, dass das Schließenkreuz auch als Cancel gilt.

Zu Nummer 2:
Also eigentlich will ich ja nur erreichen, dass ich weiß, in welchem der Felder der Nutzer eine Zahl verändert hat.
So nach dem ganz trivialen Prinzip:
Euro: 1
Dollar: 1,2
Pfund: 0,8
Franken: 1

Wenn ich jetzt bei Euro drin bin und eine Null ranhänge, dann soll das Programm sofort aufschreien, EURO, EURO, EURO, also ich muss wissen, da hat sich was im Eurofeld verändert. Damit ich dann wiederum sofort die Methode zur Umrechnung aufzurufen kann. 

Ich hatte das jetzt mal so probiert:

```
eurofeld.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
            	System.out.println("Euro");
            }
 
            public void keyTyped(KeyEvent e) {
            	System.out.println("Euro");
            }
 
            public void keyPressed(KeyEvent e) {
            	System.out.println("Euro");
            }
        });
```

Ich dachte der würde dann in der Konsole was ausgeben, wenn ich was da drin bin und eine Taste klicke?

Passiert aber rein gar nichts, leider.
Ich muss das noch weiter erforschen. Das kann doch nicht so schwer sein denke ich mir da immer.

Schöne Grüße
Lukas


----------



## Harry Kane (16. Feb 2015)

Dein KeyListener Beispiel funktioniert bei mir.
Du kannst an deinen Dialog auch einen WindowListener hängen und in diesem die Methode windowClosing überschreiben.


----------



## FranzFerdinand (16. Feb 2015)

Wunderschönen Guten Tag,

vielen Dank für Deine Antwort.
Was machst Du anders, dass es bei Dir geht??? 

Zum Listener: Gute Idee. Aber ich stelle mich noch nicht kompetent genug dazu an.
Variante a:

```
pane.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent ev) {
				
			}
		});
```

Das nimmt er nicht. Weil addWindowListener für JOptionPane nicht definiert ist, meint er.

Variante b:

```
public class Umrechnen implements WindowListener {
```

Und dann das hier:

```
@Override
	public void windowOpened(WindowEvent e) {
	}


	@Override
	public void windowClosing(WindowEvent e) {
		System.exit(0);
	}


	@Override
	public void windowClosed(WindowEvent e) {
	}


	@Override
	public void windowIconified(WindowEvent e) {
	}


	@Override
	public void windowDeiconified(WindowEvent e) {
	}


	@Override
	public void windowActivated(WindowEvent e) {	
	}


	@Override
	public void windowDeactivated(WindowEvent e) {
	}
```

Ich dachte da wäre jetzt nur das Closing relevant und dann würde er das schließen. Leider bleibt Java weiterhin im Hintergrund geöffnet. Auch wenn ich das bei "closed" reinschreibe. Oder bei beidem.

Kann mir jemand für eine von beiden Varianten (welche ist besser?) sagen, wie ich das hinbekomme?

Schöne Grüße
Lukas


----------



## Harry Kane (16. Feb 2015)

Du sollst den WindowListener auch nicht an die JOptionPane hängen, sondern an den JDialog, der unter der Voraussetzung daß dein Code aus deinem ersten Post noch gültig ist, von pane.createDialog(null, "Währungsrechner") zurückgegeben wird. Da du auf diesem JDialog nur und direkt setVisible(true) aufrufst, ist er bisher nicht "fassbar". Das zu ändern, ist aber trivial.
Und hier dein Code mit dem funktionierendem KeyListener und WindowListener:

```
public class CurrencyCalculator {

    private NumberFormat format = NumberFormat.getInstance();
    private NumberFormatter formatter = new NumberFormatter(format);

    public CurrencyCalculator() {
        format.setGroupingUsed(false);
        formatter.setAllowsInvalid(false);

        JFormattedTextField eurofeld = new JFormattedTextField(formatter);
        eurofeld.addKeyListener(new KeyAdapter(){
            public void keyPressed(KeyEvent ke){
                System.out.println("Key pressed " + ke.getKeyCode());
            }
            public void keyReleased(KeyEvent ke){
                System.out.println("Key released " + ke.getKeyCode());
            }
            public void keyTyped(KeyEvent ke){
                System.out.println("Key typed " + ke.getKeyCode());
            }
        }
        );

        JFormattedTextField dollarfeld = new JFormattedTextField(formatter);
        JFormattedTextField pfundfeld = new JFormattedTextField(formatter);
        JFormattedTextField frankenfeld = new JFormattedTextField(formatter);
        Object[] zahlenfrage = {"<html><b>Bitte gib Dein Startgeld ein.</b></html>", "Euronen", eurofeld, "Dollar", dollarfeld, "Pfund", pfundfeld, "Franken", frankenfeld};
        JOptionPane pane = new JOptionPane(zahlenfrage, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION);
        JDialog d = pane.createDialog(null, "Währungsrechner");
        d.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent we){
                System.out.println("Window closing ");
                System.exit(0);
            }
            public void windowClosed(WindowEvent we){
                System.out.println("Window closed");
                System.exit(0);
            }
        });
        d.setVisible(true);
    }

    public static void main(String[] args) {
        new CurrencyCalculator();
    }

}
```
Und natürlich nützt es nix, wenn du deine Hauptklasse WindowListener implementieren lässt, sie dann aber nicht als WindowListener verwendest.


----------



## FranzFerdinand (17. Feb 2015)

Wunderschönen guten Tag,

ich danke Dir für Deine Hilfe.
Das hat mich um einiges weitergebracht. Der WindowListener funktioniert perfekt.
Und auch der KeyListener macht, was er möchte.

Eine kleine Korrektur muss ich aber noch vorgeben. Hast Du vielleicht übersehen:

```
JDialog d = pane.createDialog(null, "Währungsrechner");
```
Ich habs mal in dialog umbenannt, d hab ich bereits belegt gehabt:

```
private DecimalFormat d = new DecimalFormat("#.00");
```
Also kam später dazu, siehe mein Beitrag #5. Aber ist nur Formalität.


Sodelle, die letzte Sache die es dann noch zu tun gibt ist eine andere Sache.
Der KeyListener nimmt nun auch brav wahr, wann ich drücke. Welche Taste ist irrelevant. Ich muss nur wissen, dass gedrückt wurde und in welchem Feld.

Ich hab jetzt beispielsweise den beim Eurofeld:

```
eurofeld.addKeyListener(new KeyAdapter() {
	        public void keyPressed(KeyEvent ke) {
	        	double euro = Double.parseDouble(eurofeld.getText());
	    		dollar.setText(d.format(1.2*euro));
	        }
	    });
```

Ich lese meinen Wert dann sofort ein und schreibe ein berechnetes Ergebnis in das Dollarfeld.

Er wirft mir aber eine Exception aus, er akzeptiert mein Zahlenformat nicht.

```
Exception in thread "AWT-EventQueue-0" java.lang.NumberFormatException: empty String
```
Aber schon in der Zeile, in der ich den Wert euro einlese.

Ich nutze wie gesagt das hier als Dezimalformat:

```
private DecimalFormat d = new DecimalFormat("#.00");
```

Das ist meine abschließende Frage. Was mache ich da falsch?

Schöne Grüße und Danke!
Lukas


----------



## Harry Kane (17. Feb 2015)

FranzFerdinand hat gesagt.:


> Das hat mich um einiges weitergebracht. Der WindowListener funktioniert perfekt.
> Und auch der KeyListener macht, was er *möchte*.


Du meinst wohl, was er *soll*?
Warum es jetzt auf einmal geht, brauchen ich und weitere Leser dieses Threads wohl nicht zu wissen, hm?



FranzFerdinand hat gesagt.:


> Eine kleine Korrektur muss ich aber noch vorgeben. Hast Du vielleicht übersehen:
> 
> ```
> JDialog d = pane.createDialog(null, "Währungsrechner");
> ...


Ich verkneife mir mal einen Kommentar darüber, was ich von deiner Formulierung halte, und besonders deine darin erkennbare Erwartung an Arbeit, die andere für dich machen.



FranzFerdinand hat gesagt.:


> ```
> eurofeld.addKeyListener(new KeyAdapter() {
> public void keyPressed(KeyEvent ke) {
> double euro = Double.parseDouble(eurofeld.getText());
> ...


Dein Fehler ist, daß du die Fehlermeldung nicht wirklich unvoreingenommem liest. Wie du ja richtig bemerkt hast, kann sie mit deinem Zahlenformat eigentlich nix zu tun haben. Offenbar gibt euroFeld.getText() einen leeren String zurück. Nur dann kann die genannte Fehlermeldung produziert werden.
Ich vermute mal, das beim Eingeben eines Zeichens sofort der KeyEvent ausgelöst wird, aber das Zeichen erst später im Model des JFormattedTextFields landet und von dort mit getText() abgefragt werden kann. Du kannst ja mal testen, ob es bei Verwendung der keyTyped-Methode besser funktioniert.


----------



## FranzFerdinand (18. Feb 2015)

Wunderschönen Guten Tag,


```
Du meinst wohl, was er soll?
Warum es jetzt auf einmal geht, brauchen ich und weitere Leser dieses Threads wohl nicht zu wissen, hm?
```
Ja, ich meinte, was er soll. Entschuldige, habe mich unglücklich ausgedrückt.

Wie er geht, siehe unten. 


```
Ich verkneife mir mal einen Kommentar darüber, was ich von deiner Formulierung halte, und besonders deine darin erkennbare Erwartung an Arbeit, die andere für dich machen.
```
Das ist eine unverschämte Unterstellung.:bahnhof:
Ich bin dankbar für alle Menschen die sich die Zeit nehmen auf Themen meiner Person zu antworten und mir zu helfen. Ich möchte dabei auch selbst dazu lernen, wie das ganze abläuft, wenn man mir einfach nur den Code dahinbastelt, dann hätte ich auch nichts davon.
Die Behauptung ich würde hier nur primär unfreundlich Leute für mich arbeiten lassen finde ich unglücklich (nett formuliert). Wenn Du nicht möchtest, musst Du ja auch nicht antworten. 

===
Um auf das andere Problem hier zurückzukommen:
Also mein aktueller Quellcode sieht nun so aus:

```
package rechner;

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.DecimalFormat;

import javax.swing.JDialog;
import javax.swing.JFormattedTextField;
import javax.swing.JOptionPane;
import javax.swing.text.NumberFormatter;

public class Umrechnen {
	
	private DecimalFormat d = new DecimalFormat("#.00");
	private NumberFormatter formatter = new NumberFormatter(d);
	
	public Umrechnen() {
		d.setGroupingUsed(false); 
		formatter.setAllowsInvalid(false);
		
		final JFormattedTextField eurofeld = new JFormattedTextField(formatter);
		final JFormattedTextField dollarfeld = new JFormattedTextField(formatter);
        final JFormattedTextField pfundfeld = new JFormattedTextField(formatter);
        final JFormattedTextField frankenfeld = new JFormattedTextField(formatter);
		
		eurofeld.addKeyListener(new KeyAdapter() {
	        public void keyReleased(KeyEvent ke) {
	        	float euro = Float.parseFloat(eurofeld.getText());
	    		dollarfeld.setText(d.format(1.2*euro));
	        }
	    });
		
		Object[] zahlenfrage = {"<html><b>Bitte gib Dein Startgeld ein.</b></html>", "Euronen", eurofeld, "Dollar", dollarfeld, "Pfund", pfundfeld, "Franken", frankenfeld};
		JOptionPane pane = new JOptionPane(zahlenfrage, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION);
		JDialog dialog = pane.createDialog(null, "Währungsrechner");
		dialog.addWindowListener(new WindowAdapter() {
	        public void windowClosing(WindowEvent we) {
	            System.exit(0);
	        }
	        public void windowClosed(WindowEvent we) {
	            System.exit(0);
	        }
	    });
	    dialog.setVisible(true);
		
	}
	
	public static void main(String[] args) {
		new Umrechnen();
	}

}
```

Der WindowListener läuft wie unten zu sehen durch einfaches hinzufügen zu dialog und das aktivieren der windowClosing- bzw. auch windowClosedOption.

Der KeyListener macht aktuell noch nicht, was er soll.

Ich möchte einmal beschreiben, wie es läuft:
Ich habe alle drei KeyListener probiert.
Bei KeyTyped bekomme ich eine folgende Exception:

```
Exception in thread "AWT-EventQueue-0" java.lang.NumberFormatException: empty String
```

Hier akzeptiert er das Format noch nicht, wie es soll. Möglicherweise auch, weil keyTyped zu schnell auf Tastenanschläge reagiert, bevor alles verarbeitet wird.

Bei KeyPressed erhalte ich genau die gleiche Meldung.

Wenn ich KeyReleased eingebe sieht es schon anders aus:

```
Exception in thread "AWT-EventQueue-0" java.lang.NumberFormatException: For input string: "6,00"
	at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1250)
	at java.lang.Float.parseFloat(Float.java:452)
```

Hier ist er offensichtlich schon ein Stück weiter und hat die Zahl selbst schon. Er nimmt sie aber weiterhin als String wahr.

Der Fehler ist dann in dieser Zeile:

```
float euro = Float.parseFloat(eurofeld.getText());
```

Ich habe es nun schon mit Float und mit Double ausprobiert, an dieser Sache sollte es generell nicht liegen, denke ich.

Ich werde mich morgen mal dazu belesen, wie das mit den Decimalfeldern so diesbezüglich aussieht.

Schöne Grüße
Lukas


----------



## FranzFerdinand (19. Feb 2015)

Hallihallöle,

ich habe die schmierigste Lösung gefunden, die es gibt. Es funktioniert. Aber es ist hässlich, schrecklich unschön.

Ich habe festgestellt, das Komma ist das Problem, weshalb er das nicht macht. Deshalb parse ich Vor- und Nachkommastellen einzeln.


```
String eurostring = eurofeld.getText();
	        	String[] eurostringarr = eurostring.split(",");
	        	double[] eurodoublearr = new double[2];
	        	eurodoublearr[0] = Double.parseDouble(eurostringarr[0]);
	        	eurodoublearr[1] = Double.parseDouble(eurostringarr[1]);
	        	double euro = eurodoublearr[0] + eurodoublearr[1]*0.01;
	        	dollar.setText(d.format(1.2*euro));
```

Also ich splite den eingelesenen String, wandle es einzeln um und addiere es wieder zusammen.

So läuft es. Aber wie gesagt, sehr unschön.

Ich würde darum bitten, dass irgendwer, der eine schönere Lösung anhand meines Quellcodes den Post darüber kennt, mir das bitte mitteilt. 

Dezimalzahlen einlesen brauch ich auch in anderen Projekten immer mal. Das wäre so sehr ungünstig.

Viele schöne Grüße
Lukas


----------

