JavaFX Taschenrechner mit SceneBuilder

melaniemueller

Aktives Mitglied
Hey zusammen,

ich soll einen einfachen Taschenrechner mit JavaFX und dem Scenebuilder erstellen. Über eine Combobox soll die gewünschte Rechenoperation ausgewählt werden können, 2 Felder zum eintragen der Zahlen sowie 2 Buttons mit Berechnen und Beenden.

Nun habe ich einen Taschenrechner optisch erstellt, auch die Methoden und alles verbunden, aber es tut absolut nicht das was es soll und ich hab so meine Probleme mit der Combobox weil ich nicht genau weiß wie es in JavaFX umgesetzt wird.

Hier einmal mein Code vom Controller:

Java:
package taschenrechnerJavaFX;


import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;

public class TaschenrechnerController {
        @FXML private Label ergebnisAnzeige;
        @FXML private ComboBox<String> auswahl;
        @FXML private TextField eingabe2, eingabe1;
        
        @FXML protected void beendenKlick(ActionEvent event) {
            Platform.exit();
        }
        
        
        @FXML protected String berechnen(ActionEvent event) {
            double zahl1, zahl2, ergebnis = 0;
            boolean fehlerFlag = false;
            zahl1 = Double.parseDouble(eingabe1.getText());
            zahl2 = Double.parseDouble(eingabe2.getText());
            String tempAuswahl = auswahl.getSelectionModel().getSelectedItem().toString();
            if (tempAuswahl.equals("Addition"))
                ergebnis = zahl1 + zahl2;
            if(tempAuswahl.equals("Subtraktion"))
                ergebnis = zahl1 - zahl2;
            if(tempAuswahl.equals("Multiplikation"))
                ergebnis = zahl1 * zahl2;
            if(tempAuswahl.equals("Division")) {
                if(zahl2!=0)
                ergebnis = zahl1 / zahl2;
                else
                    fehlerFlag = true;
        }
            
            if(fehlerFlag == false)
                return Double.toString(ergebnis);
            else
                return ("Nicht definiert");
            
            
        }
        
        @FXML void initialize() {
            auswahl.getItems().addAll("Addition", "Subtraktion", "Multiplikation", "Division");
            auswahl.getSelectionModel().selectFirst();
        }   
            
        }

Und so sieht er aus:

Screenshot 2021-10-13 210219.png

Mein Problem:

Sobald auf "Berechnen" geklickt wird, verdoppeln sich die Einträge in der Combobox. Es wiederholt sich alles. Rechnen tut er auch nichts. Ich meine auch ich hab den Grund dafür gefunden wieso er nicht rechnet. Ich habe ein Label ergebnisAnzeige erstellt, aber nirgends mit dem ergebnis verbunden. Nur weiß ich nicht wie ich das in JavaFX machen kann? Ich dachte an setText aber das akzeptiert er nicht. Auch wüsste ich nicht an welcher Stelle. Es wird einfach mal wieder viel zu wenig erklärt zu diesem Thema in meinem Heft...

Wer kann helfen?
 
was kommt raus wenn du nach dem button klick die werte für zahl1 und zahl2 und tempAuswahl und das ergebnis out.println lässt... stehen da die richtigen sachen überhaupt drin
 
Das Kernproblem ist, dass Du da wild alles vermischt - UI Code und "taschenrechner" Code.

So entsteht dann eine Methode, die ein Ergebnis berechnet und zurück gibt die Du dann einfach als Event Handler einträgst.

Trenne es sauber! Dann hast Du eine Methode, die Du für das Event nutzt, die:
- aus den UI Elementen die benötigten Werte heraus holt.
- Die Berechnung selbst anstößt
- das / die Ergebnisse dann zurück schreibt.

Idealer Weise würde man das auch in den Klassen Trennen, aber das ist erst einmal egal.

Wenn Du es dann so ordentlich unterteilst, dann wird es vielleicht auch für Dich deutlich einfacher und verständlicher.
 
was kommt raus wenn du nach dem button klick die werte für zahl1 und zahl2 und tempAuswahl und das ergebnis out.println lässt... stehen da die richtigen sachen überhaupt drin
Ich verstehe leider nicht genau wie du das meinst? Meinst du anstatt return Double.toString(ergebnis); eine print Anweisung?

Trenne es sauber! Dann hast Du eine Methode, die Du für das Event nutzt, die:
- aus den UI Elementen die benötigten Werte heraus holt.
- Die Berechnung selbst anstößt
- das / die Ergebnisse dann zurück schreibt.
Du meinst also ich sollte eine eigene Methode zum Anzeigen des Ergebnis erstellen und eine Methode zum Berechnen (welche ich ja habe) oder?

Ich versuche mal es abzuändern, mal sehen was bei raus kommt 😅
 
Ich verstehe leider nicht genau wie du das meinst? Meinst du anstatt return Double.toString(ergebnis); eine print Anweisung?
Ich denke, damit ist nur gemeint, dass du dir die Werte der Zahlen nach dem Einlesen einmal über System.out.println in die Console ausgeben lassen kannst, um zu prüfen, ob die Zahlen überhaupt richtig eingelesen werden. Also nur für's debugging. 😉

Du meinst also ich sollte eine eigene Methode zum Anzeigen des Ergebnis erstellen und eine Methode zum Berechnen (welche ich ja habe) oder?
Du könntest zum Beispiel für das Berechnen eine eigene Funktion machen, die gar nichts mehr von der UI weiss und nur die Zahlen und die Rechenmethode als Parameter reinbekommt. Diese Methode rufst du dann entsprechend beim Klick auf den Button auf. So wären die mathematischen Sachen komplett von den UI spezifischen Sachen getrennt.
 
Ich hab nun mal die berechnen() Methode unabhängig gemacht von der UI und stattdessen separat eine Methode berechnenKlick angelegt und dort das Ergebnis aus berechnen() übergeben mit setText an das Label der Ergebnisanzeige.

Java:
package taschenrechnerJavaFX;


import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;

public class TaschenrechnerController {
        @FXML private Label ergebnisAnzeige;
        @FXML private ComboBox<String> auswahl;
        @FXML private TextField eingabe2, eingabe1;
        
        @FXML protected void beendenKlick(ActionEvent event) {
            Platform.exit();
        }
        
        @FXML protected void berechnenKlick(ActionEvent event) {
            ergebnisAnzeige.setText(berechnen());
        }
        
        
        @FXML protected String berechnen() {
            double zahl1, zahl2, ergebnis = 0;
            boolean fehlerFlag = false;
            zahl1 = Double.parseDouble(eingabe1.getText());
            zahl2 = Double.parseDouble(eingabe2.getText());
            String tempAuswahl = auswahl.getSelectionModel().getSelectedItem().toString();
            if (tempAuswahl.equals("Addition"))
                ergebnis = zahl1 + zahl2;
            if(tempAuswahl.equals("Subtraktion"))
                ergebnis = zahl1 - zahl2;
            if(tempAuswahl.equals("Multiplikation"))
                ergebnis = zahl1 * zahl2;
            if(tempAuswahl.equals("Division")) {
                if(zahl2!=0)
                ergebnis = zahl1 / zahl2;
                else
                    fehlerFlag = true;
        }
            
            if(fehlerFlag == false)
                return Double.toString(ergebnis);
            else
                return ("Nicht definiert");
            
            
        }
        
        @FXML void initialize() {
            auswahl.getItems().addAll("Addition", "Subtraktion", "Multiplikation", "Division");
            auswahl.getSelectionModel().selectFirst();
        }   
            
        }

Ist wohl denk ich schon mal nicht verkehrt so. Zumindest wird nun das Ergebnis angezeigt. Aber das Problem mit der Combobox bleibt, da bin ich noch nicht weiter. Ich klicke auf berechnen, er zeigt nun auch das Ergebnis an, aber dann erscheinen die Felder nochmal. Jedes Mal wenn ich auf den Button klicke wiederholen sich die Einträge. Da scheint die initialize() Methode nicht ganz zu passen...Die Liste wird einfach immer länger.
 
Also das ist schon deutlich besser. Ich würde noch ein paar Dinge verlagern, aber das spielt erst einmal keine Rolle.

Das @FXML vor berechnen sollte weg, denn die Methode spielt für den FXMLLoader ja keine Rolle.

Das die Liste in der Auswahlbox immer länger wird, kann ich so erst einmal am Code keinen Grund für sehen. Kannst Du evtl. auch einmal dein fxml File zeigen? Hast Du da das initialize noch irgendwo mit angegeben?
 
XML:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.text.Font?>

<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="198.0" prefWidth="428.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="taschenrechnerJavaFX.TaschenrechnerController">
   <children>
      <Label layoutX="37.0" layoutY="18.0" text="Zahl 1:">
         <font>
            <Font size="18.0" />
         </font>
      </Label>
      <Label layoutX="37.0" layoutY="59.0" text="Zahl 2:">
         <font>
            <Font size="18.0" />
         </font>
      </Label>
      <Label layoutX="37.0" layoutY="112.0" prefWidth="77.0" text="Ergebnis:">
         <font>
            <Font size="18.0" />
         </font>
      </Label>
      <TextField fx:id="eingabe1" layoutX="114.0" layoutY="19.0" prefHeight="25.0" prefWidth="110.0" />
      <TextField fx:id="eingabe2" layoutX="114.0" layoutY="60.0" prefHeight="25.0" prefWidth="110.0" />
      <Label layoutX="253.0" layoutY="19.0" text="Rechenoperationen:">
         <font>
            <Font size="18.0" />
         </font>
      </Label>
      <ComboBox fx:id="auswahl" layoutX="253.0" layoutY="48.0" onAction="#initialize" prefHeight="25.0" prefWidth="159.0" />
      <Button layoutX="35.0" layoutY="153.0" mnemonicParsing="false" onAction="#berechnenKlick" prefHeight="39.0" prefWidth="110.0" text="Berechnen" textFill="#109706">
         <font>
            <Font size="18.0" />
         </font>
      </Button>
      <Button layoutX="253.0" layoutY="153.0" mnemonicParsing="false" onAction="#beendenKlick" prefHeight="25.0" prefWidth="150.0" text="Beenden" textFill="#e11616">
         <font>
            <Font size="18.0" />
         </font>
      </Button>
      <Label fx:id="ergebnisAnzeige" layoutX="114.0" layoutY="117.0" prefHeight="17.0" prefWidth="110.0">
         <font>
            <Font size="18.0" />
         </font>
      </Label>
   </children>
</Pane>

Das ist die FXML Datei. An der hab ich manuell jedoch nichts verändert, außer den Controller manuell eingetragen, da dies automatisch nicht geklappt hatte. Ansonsten hab ich nur im Scenebuilder gearbeitet. initialize ist sonst nirgends angegeben. Es sollte nur am Ende ausgeführt werden, sonst nichts, das war der Plan.
 
<ComboBox fx:id="auswahl" layoutX="253.0" layoutY="48.0" onAction="#initialize" prefHeight="25.0" prefWidth="159.0" />

Nimm da bitte das
onAction="#initialize"
raus!

Bei jeder Aktion der ComboBox wirst so initialize aufgerufen, d.h. 4 Elemente kommen hinzu und das oberste Element wird gewählt. Das willst Du aber nicht. Du willst da ja gar keine Action haben, denn beim Button-Click wird ja das Control einfach ausgelesen.
 
Evtl. nur noch der Vollständigkeit halber:
Ich hätte noch paar Dinge mehr von berechnen in berechnenKlick verschoben: Alle Zugriffe auf UI Elemente gehören zur UI und die reine Berechnung wäre dann das Einzige, das in berechnen bleiben würde.

Also etwas wie:
Java:
        @FXML protected void berechnenKlick(ActionEvent event) {
            double zahl1 = Double.parseDouble(eingabe1.getText());
            double zahl2 = Double.parseDouble(eingabe2.getText());
            String operation = auswahl.getSelectionModel().getSelectedItem().toString();
            ergebnisAnzeige.setText(berechnen(zahl1, zahl2, operation));
        }
        
        
        protected String berechnen(double zahl1, double zahl2, String operation) {
            double ergebnis = 0;
            boolean fehlerFlag = false;
            if (operation.equals("Addition"))
                ergebnis = zahl1 + zahl2;
            if(operation.equals("Subtraktion"))
                ergebnis = zahl1 - zahl2;
            if(operation.equals("Multiplikation"))
                ergebnis = zahl1 * zahl2;
            if(operation.equals("Division")) {
                if(zahl2!=0)
                    ergebnis = zahl1 / zahl2;
                else
                    fehlerFlag = true;
        }
            
            if(fehlerFlag == false)
                return Double.toString(ergebnis);
            else
                return ("Nicht definiert");
        }

Hintergrund ist, dass man eine möglichst gute Trennung von der UI haben möchte. Das aber nur als Hinweis - Deine Lösung ist so schon durchaus in Ordnung.
 
du kannst auch noch so weit gehen dass du 2 klassen hast , 1e ist für das berechnen zuständig , 1e ist für das fxml lesen zuständig

und die 2 klassen verbindest du dann und schon hast du logik von grafik getrennt
 

Zurück
Oben