Swing-Rechner - erstes GUI-Programm

127954
Karkoon

Ich habe kürzlich einen Rechner mit Java und Swing erstellt. Ich bin mit den Ergebnissen in Ordnung, aber ich bin neugierig, was die größten Fehler sind, die ich gemacht habe (vorausgesetzt, es gibt dies, weil es mein erstes Programm ist, das GUI erstellt). Ich kann mir keine großartige Möglichkeit vorstellen, um Code in den Funktions-Action-Listener-Klassen nicht zu duplizieren.

Welche anderen guten Regeln habe ich gebrochen?

Pastebin-Link

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Frame extends JFrame {

private double tempNumbers1 = 0;
private double tempNumbers2 = 0;

private byte function = -1;


private JTextField resultJText;

public Frame() {

    JButton[] numberButtons = new JButton[10];

    for ( int i = 9; i >= 0; i--) {
        numberButtons[i] = new JButton(Integer.toString(i));
    }

    JButton enterButton     = new JButton("Enter");
    JButton cButton         = new JButton("C");
    JButton multiplyButton  = new JButton("*");
    JButton divideButton    = new JButton("/");
    JButton addButton       = new JButton("+");
    JButton substractButton = new JButton("-");


    resultJText = new JTextField();
        resultJText.setPreferredSize(new Dimension(160, 20));
        resultJText.setBackground(Color.WHITE);
        resultJText.setEnabled(false);
        resultJText.setHorizontalAlignment(4);
        resultJText.setDisabledTextColor(Color.BLACK);


    JPanel motherPanel = new JPanel();
        motherPanel.setLayout(new BoxLayout(motherPanel, BoxLayout.Y_AXIS));

    JPanel textPanel = new JPanel();
        textPanel.setPreferredSize(new Dimension(160, 20));
        textPanel.add(resultJText);

    JPanel numberButtonsPanel = new JPanel();
        numberButtonsPanel.setPreferredSize(new Dimension(160, 100));

        for(int i = 9; i>=0; i--) {
            numberButtonsPanel.add(numberButtons[i]);
        }

    JPanel functionButtonPanel = new JPanel();
        functionButtonPanel.setPreferredSize(new Dimension(160, 35));
        functionButtonPanel.add(enterButton);
        functionButtonPanel.add(cButton);
        functionButtonPanel.add(multiplyButton);
        functionButtonPanel.add(divideButton);
        functionButtonPanel.add(addButton);
        functionButtonPanel.add(substractButton);

    numberButtonsAction[] numberButtonActions = new numberButtonsAction[10];
    for ( int i = 0; i < 10; i++ ) {
        numberButtonActions[i] = new numberButtonsAction(numberButtons[i]);
        numberButtons[i].addActionListener(numberButtonActions[i]);
    }

    EnterButton enter = new EnterButton();
        enterButton.addActionListener(enter);

    CButton c = new CButton();
        cButton.addActionListener(c);

    MultiplyButton multiply = new MultiplyButton();
        multiplyButton.addActionListener(multiply);

    DivideButton divide = new DivideButton();
        divideButton.addActionListener(divide);

    AddButton add = new AddButton();
        addButton.addActionListener(add);

    SubtractButton subtract = new SubtractButton();
        substractButton.addActionListener(subtract);

    motherPanel.add(textPanel);
    motherPanel.add(numberButtonsPanel);
    motherPanel.add(functionButtonPanel);
    add(motherPanel);

    setTitle("ButtonTest");
    setSize(180, 290);
    setLocationByPlatform(true);
    setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    setVisible(true);

}

private class numberButtonsAction implements ActionListener {

    private String c;

    public numberButtonsAction(JButton a) {
        this.c = a.getText();
    }

    public void actionPerformed(ActionEvent e) {
        if (!resultJText.getText().equals("0.0")) {
            resultJText.setText(resultJText.getText() + c);
        } else {
            resultJText.setText("");
            actionPerformed(e);
        }
    }
}

private class EnterButton implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        tempNumbers2 = Double.parseDouble(resultJText.getText());

        if (function == 0) {
            resultJText.setText(Double.toString((Math.round((tempNumbers1 / tempNumbers2) * 100)) / 100));
        } else if (function == 1) {
            resultJText.setText(Double.toString(tempNumbers1 * tempNumbers2));
        } else if (function == 2) {
            resultJText.setText(Double.toString(tempNumbers2 + tempNumbers1));
        } else if (function == 3) {
            resultJText.setText(Double.toString(tempNumbers1 - tempNumbers2));
        } else {
            resultJText.setText(String.valueOf(tempNumbers1));
        }
            tempNumbers1 = Double.parseDouble(resultJText.getText());
    }


}

private class CButton implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        resultJText.setText("");
        tempNumbers1 = 0;
        tempNumbers2 = 0;

        function = -1;
    }


}

private class DivideButton implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        if (tempNumbers1 == 0) {
            tempNumbers1 = Double.parseDouble(resultJText.getText());
            resultJText.setText("");
        } else {
            tempNumbers2 = Double.parseDouble(resultJText.getText());
            resultJText.setText("");
        }
        function = 0;
    }

}

private class MultiplyButton implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        if (tempNumbers1 == 0) {
            tempNumbers1 = Double.parseDouble(resultJText.getText());
            resultJText.setText("");
        } else {
            tempNumbers2 = Double.parseDouble(resultJText.getText());
            resultJText.setText("");
        }
        function = 1;
    }


}


private class AddButton implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        if (tempNumbers1 == 0) {
            tempNumbers1 = Double.parseDouble(resultJText.getText());
            resultJText.setText("");
        } else {
            tempNumbers2 = Double.parseDouble(resultJText.getText());
            resultJText.setText("");
        }
        function = 2;
    }

}

private class SubtractButton implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        if (tempNumbers1 == 0) {
            tempNumbers1 = Double.parseDouble(resultJText.getText());
            resultJText.setText("");
        } else {
            tempNumbers2 = Double.parseDouble(resultJText.getText());
            resultJText.setText("");
        }
        function = 3;
    }

}
}
Antworten
15

2 Antworten auf die Frage

10
Simon Forsberg

tempNumbersist kein sehr erklärender Variablenname. Lassen Sie die Variable selbst beschreiben, wofür es Temp ist. Für einen einfachen Rechner vielleicht number1und number2würde genügen.

Sie verwenden in Ihrem Konstruktor eine merkwürdige Einrückung. Es ist gut, dass Sie die Felder voneinander trennen, aber Sie müssen keine zusätzliche Einrückung für die Zeilen hinzufügen, in denen Sie Methoden für die Schaltflächen aufrufen.

Die Klasse numberButtonsActionsollte mit N bezeichnet werden, um den Java-Codierungskonventionen zu entsprechen.


Sie haben folgendes ifin Ihrem Code:

if (!resultJText.getText().equals("0.0")) {

Wenn Sie dieses Textfeld jedoch löschen, setzen Sie es nicht auf "0.0", sondern auf eine leere Zeichenfolge:

resultJText.setText("");

Ich bin mir nicht sicher, wie das alles läuft, wenn ich es laufen lasse. Vielleicht möchten Sie den Text als ein Double analysieren (setzen Sie ihn auf, 0.0wenn es keine Zahl ist), überprüfen Sie den Wert und vergleichen Sie ihn anstelle des Textes. Nur eine Idee.


Für die verschiedenen Operationen sollten Sie das Strategiemuster in Betracht ziehen .

Sie können eine Aufzählung anstelle eines Bytes verwenden.

public enum CalcFunction {
    ADDITION, SUBTRACTION, MULTIPLICATION, DIVISION;
}

Wenn Sie Java 8 verwenden würden, könnten Sie die DoubleBinaryOperatorSchnittstelle und die Lambdas verwenden. Sie können auch die gesamten ActionListener-Dinge durch die Verwendung von Java-Methodenverweisen viel flüssiger gestalten. button.addActionListener(this::divideButtonClick);(Ich liebe Java 8!)

Die aktuellen Klassennamen DivideButton, MultiplyButtonetc klingt wie sie extend JButton, aber sie tun es nicht (und sie sollten nicht so, das ist gut). Bessere Namen würden Listeneram Ende hinzugefügt. Wenn Sie Java 8 jedoch nicht verwenden können, können Sie eine allgemeine Klasse wie folgt erstellen:

private class CalculationListener implements ActionListener {

    private final CalcFunction operation;

    public CalculationListener(CalcFunction function) {
        this.operation = function;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (tempNumbers1 == 0) {
            tempNumbers1 = Double.parseDouble(resultJText.getText());
            resultJText.setText("");
        } else {
            tempNumbers2 = Double.parseDouble(resultJText.getText());
            resultJText.setText("");
        }
        function = operation;
    }
}

Und benutze es so:

divideButton.addActionListener(new CalculationListener(CalcFunction.DIVISION));

Derzeit verwenden Sie eine Schleife, um die Schaltflächen zu erstellen:

for ( int i = 9; i >= 0; i--) {
    numberButtons[i] = new JButton(Integer.toString(i));
}

Und dann haben Sie eine Schleife, um die Schaltflächen zum Panel hinzuzufügen:

for(int i = 9; i>=0; i--) {
    numberButtonsPanel.add(numberButtons[i]);
}

Und noch etwas weiter unten, um die Zuhörer zu schaffen:

numberButtonsAction[] numberButtonActions = new numberButtonsAction[10];
for ( int i = 0; i < 10; i++ ) {
    numberButtonActions[i] = new numberButtonsAction(numberButtons[i]);
    numberButtons[i].addActionListener(numberButtonActions[i]);
}

Sie brauchen nicht all diese drei Schleifen, eine genügt. Dies macht auch die von Ihnen verwendeten Arrays überflüssig. Sie müssen nicht in einem Array gespeichert werden.

for ( int i = 9; i >= 0; i--) {
    JButton numberButton = new JButton(Integer.toString(i));
    numberButtonsPanel.add(numberButton);
    numberButton.addActionListener(new NumberButtonsAction(numberButton));
}
9
Marc-Andre

Sie ändern nicht das Verhalten von. JFrameWarum sollten Sie es erweitern? Es ist ein "häufiger" Fehler bei Swing. Sie sollten Ihr extends JFramefür ein privates JFrameFeld in Ihrer Klasse entfernen . Dies ändert fast nichts an Ihrem Code, da Sie alles tun müssen, anstatt adddirekt anzurufen. Sie werden es in Ihrem Feld anrufen. Was sich ändern wird, ist, dass dein Wille nicht JFrameso viel bindet . Waht, wenn Sie eine JDialogstatt einer wollen JFrame? Ein Feld zu haben, erleichtert diese Änderung. (In Ihrem Fall benötigen Sie eine JFrame, die jedoch nicht immer der Fall sein kann).

Es gibt etwas, das mir an Ihrem Code gefällt, keine absolute Position! Sie wissen nicht, wie viel dies für die Pflege einer GUI wertvoll ist! Ich beschäftige mich mit GUI mit absoluter Position, und es ist einfach schrecklich! Lass es so wie es ist. Verwenden LayoutSie das, was Sie brauchen, seien Sie nicht zu restriktiv in Ihrem Design und positionieren Sie NIEMALS (gut wie nie) Ihre Komponenten absolut.


Ich persönlich mag es nicht, wenn Sie den Namen der Klasse in einer Variablen erwähnen:

private JTextField resultJText;

Wenn Sie Ihre Klasse jemals ändern, müssen Sie den Variablennamen ändern. Andernfalls wird die Klasse inkonsistent! Sie könnten resultTextoder einfach, resultaber Sie brauchen wirklich nicht zu erwähnen, dass es eine ist JTextField.

Ich bin in allem völlig einverstanden. JFrame zu erweitern, anstatt es zu verwenden, ist ein Fehler, den ich auch schon oft gemacht habe. Dieses Problem ist mit JavaFX zum Glück mehr oder weniger verschwunden :) Simon Forsberg vor 6 Jahren 2