GUI 计算器出现奇怪的 Swing Java 错误?

Weird Swing Java Error in GUI Calculator?

我对 java 有点陌生,尤其是 swing 界面。我试图让它在显示两个数字的结果后输入一个数字,它只会清除它并用你按下的下一个数字替换它。例如,如果您添加 1 + 2 然后它显示 3,如果您按下数字 4,它将清除数字 3 并将其替换为 4。

下一个紧迫的问题是当我使用加法开关将两个数字相加时,它工作正常,除了它会显示第二个数字以及两个相加数字的结果。因此,例如,如果您添加 1 + 2,它将显示“23”,其中二是总和中的最后一个数字,而数字三是总和的结果。有帮助吗?


package javacalc;

import com.sun.source.tree.BreakTree;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JTextField;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class CalcGUI extends JFrame implements ActionListener { 
    final static String PROJ_TITLE = "Calculator";
    final static String PROJ_AUTHORS = String.format("Jaren Browne%nKevin Miller");

    final static int[] PROJ_WINDOW_SIZE = {490, 650};
    final static int[] PROJ_WINDOW_LOCATION = {300, 300};

    GridBagConstraints gbcCalcPanel = new GridBagConstraints();
    GridBagConstraints gbcButtonPanel = new GridBagConstraints();
    JPanel calcPanel = new JPanel();
    JTextField upperDisplay;
    JTextField lowerDisplay;
    
    private int operation = 0;
    private boolean clearScreen = false;
    private double firstNumber = 0;
    private double secondNumber = 0;
    
    DecimalFormat df = new DecimalFormat("0.##############");
    
    @Override
    public void actionPerformed(ActionEvent e) {
        String command = e.getActionCommand();
        
        switch (command) {
            case ".":       // decimal
                updateDisplay(command);
                break;
            case "=":           // equal
                if (!upperDisplay.getText().equals("0")) {
                    secondNumber = Double.parseDouble(upperDisplay.getText());
                    
                    switch (operation) {
                        case 2:             // addition
                            updateDisplay(String.valueOf(df.format(firstNumber + secondNumber)));
                            break;
                        case 3:             // subtraction
                            upperDisplay.setText(String.valueOf(df.format(firstNumber - secondNumber)));
                            break;
                        case 4:             // multiplication
                            upperDisplay.setText(String.valueOf(df.format(firstNumber * secondNumber)));
                            break;
                        case 5:             // division
                            upperDisplay.setText(String.valueOf(df.format(firstNumber / secondNumber)));
                            break;
                        case 6:             // percent ( firstNumber % secondNumber = firstNumber% of secondNumber )
                            upperDisplay.setText(String.valueOf(df.format(((firstNumber / 100) * secondNumber))));
                            break;
                        case 7:
                            if (firstNumber == 0) { firstNumber = 1; }
                            upperDisplay.setText(String.valueOf(df.format(firstNumber * Math.sqrt(secondNumber))));
                            break;
                        case 8:
//                            upperDisplay.setText(String.valueOf(df.format()));
                            break;
                        case 9:
//                            upperDisplay.setText(String.valueOf(df.format()));
                            break;

                    }
                }
                break;
            case "+":           // add
                firstNumber = Double.parseDouble(upperDisplay.getText());
                operation = 2;
                clearScreen = true;
                break;
            case "-":           // subtract
                firstNumber = Double.parseDouble(upperDisplay.getText());
                operation = 3;
                clearScreen = true;
                break;
            case "x":           // multiply
                firstNumber = Double.parseDouble(upperDisplay.getText());
                operation = 4;
                clearScreen = true;
                break;
            case "÷":           // divide
                firstNumber = Double.parseDouble(upperDisplay.getText());
                operation = 5;
                clearScreen = true;
                break;
            case "%":           // percent
                firstNumber = Double.parseDouble(upperDisplay.getText());
                operation = 6;
                clearScreen = true;
                break;
// FIXME - +/- should not leave only '-' char when delete is pressed on last digit
// FIXME - if there is only one digit and it is not 0, and the - is active
// FIXME - it should not remove the last character and only leave -
// FIXME - it should change last remaining character to 0 and unalive the -
            case  "+/-":        // negate
                upperDisplay.setText(upperDisplay.getText().equals("0")||
                                    (upperDisplay.getText().length() == 1 && upperDisplay.getText().equals("0"))?
                                    "0":String.valueOf(df.format(Double.parseDouble(upperDisplay.getText()) * -1)));
                break;
            case "√":           // square root
                firstNumber = Double.parseDouble(upperDisplay.getText());
                operation = 7;
                clearScreen = true;
                break;
            case "xⁿ":          // exponent
                
                break;
            case "ⁿ√":          // exponent root
                
                break;
            case "π":           // pi
                
                break;
            case "sin":         // sine

                break;
            case "cos":         // cosine

                break;
            case "tan":         // tangent

                break;
            case "sin‾¹":       // arcsine
                
                break;
            case "cos‾¹":       // arccosine

                break;
            case "tan‾¹":       // arctan

                break;
            case "<-":          // backspace
                deletePressed();
                break;
            case "CE":          // clear
                firstNumber = 0;
                secondNumber = 0;
                operation = 0;
                clearScreen = false;
                upperDisplay.setText("0");
//                lowerDisplay.setText("0");
                break;
            case "M+":          // mem+

                break;
            case "M-":          // mem-

                break;
            case "MC":          // mem clear

                break;


            default:
                    break;
        }
    }
    
    public CalcGUI() {
        initGUI();

        calcPanel.setLayout(new GridBagLayout());
        
        // add display box
        gbcCalcPanel.gridx = 0;
        gbcCalcPanel.gridy = 0;
        gbcCalcPanel.fill = GridBagConstraints.HORIZONTAL;
        gbcCalcPanel.gridwidth = 1;
        calcPanel.add(upperDisplay = buildDisplay(), gbcCalcPanel);
        
//        // add second display box
//        gbcCalcPanel.gridx = 0;
//        gbcCalcPanel.gridy = 1;
//        gbcCalcPanel.fill = GridBagConstraints.HORIZONTAL;
//        gbcCalcPanel.gridwidth = 1;
//        calcPanel.add(lowerDisplay = buildDisplay(), gbcCalcPanel);
                
        // add advanced calculator buttons
        gbcCalcPanel.gridx = 0;
        gbcCalcPanel.gridy = 2;
        gbcCalcPanel.weighty = 1;
        gbcCalcPanel.fill = GridBagConstraints.BOTH;
        calcPanel.add(buildAdvancedButtonPanel(), gbcCalcPanel);
        
        // add calculator buttons
        gbcCalcPanel.gridx = 0;
        gbcCalcPanel.gridy = 3;
        gbcCalcPanel.weighty = 1;
        gbcCalcPanel.fill = GridBagConstraints.BOTH;        
        calcPanel.add(buildButtonPanel(), gbcCalcPanel);
        
        setContentPane(calcPanel);
        pack();
    }

    // BUILD DISPLAY BOX
    private JTextField buildDisplay() {
        JTextField t = new JTextField("", 16);
        t.setHorizontalAlignment(SwingConstants.RIGHT);
        t.setBackground(Color.DARK_GRAY);
        t.setForeground(Color.GREEN);
        t.setText("0");
        t.setCaretPosition(1);

        try {
            Font calcDisplayFont = Font.createFont(Font.TRUETYPE_FONT, new File("src\font\DS-DIGII.TTF")).deriveFont(60f);
            GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
            ge.registerFont(calcDisplayFont);
            t.setFont(calcDisplayFont);
        } catch (IOException | FontFormatException e) {
            System.out.println(e.getMessage());
            Font calcDisplayFont = new Font("Lucida Console", Font.BOLD, 40);
            t.setFont(calcDisplayFont);
        }

        return t;
    }
    
    // BUILD BUTTON PANEL
    private JPanel buildButtonPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new GridBagLayout());
        panel.setBackground(Color.gray);
        
        String[][] buttonText = {{"MC", "MR", "M-", "M+", "÷"},
                                {"+/-", "7", "8", "9", "x"},
                                {"%", "4", "5", "6", "-"},
                                {"√", "1", "2", "3", "+"},
                                {"CE", "0", ".", "=", "+"}};

        int maxWidth = buttonText[0].length;
        int maxHeight = buttonText.length;
        JButton[][] calcButton = new JButton[maxHeight][maxWidth];
        
        // cycle through button array and build
        for (int yPos = 0; yPos < maxHeight; yPos++) {
            for (int xPos = 0; xPos < maxWidth; xPos++) {
                
                // build button panel, skipping button for extended '+' button
                if (!(yPos == 4 && xPos == 4)) {
                    calcButton[yPos][xPos] = new JButton(buttonText[yPos][xPos]);
                    calcButton[yPos][xPos].setFont(calcButton[yPos][xPos].getFont().deriveFont(30f));
                    calcButton[yPos][xPos].setBackground(Color.black);
                    
//                     test if button pressed is number or operation
//                     if number, send to display
                    if (calcButtonTest(buttonText[yPos][xPos])) {
                        String calcValue = calcButton[yPos][xPos].getText();
                        calcButton[yPos][xPos].addActionListener(e -> updateDisplay(calcValue));
                    } else {
                        calcButton[yPos][xPos].addActionListener(this);

                    }

                    gbcButtonPanel.gridx = xPos;
                    gbcButtonPanel.gridy = yPos;
                    gbcButtonPanel.gridwidth = 1;
                    gbcButtonPanel.weightx = 1;
                    gbcButtonPanel.weighty = 1;
                    Insets buttonSpacing = new Insets(2,2,2,2);
                    gbcButtonPanel.insets = buttonSpacing;
                    
                    // if the button being added is '+' extended it vertically
                    if (yPos == 3 && xPos == 4) {
                        gbcButtonPanel.gridheight = 2;
                        gbcButtonPanel.fill = GridBagConstraints.BOTH;
                    } else {
                        gbcButtonPanel.gridheight = 1;
                        gbcButtonPanel.fill = GridBagConstraints.BOTH;
                    }
                    panel.add(calcButton[yPos][xPos], gbcButtonPanel);
                }
            }
        }

        return panel;
    }
    
    private JPanel buildAdvancedButtonPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new GridBagLayout());
        panel.setBackground(Color.darkGray);
        
        String[][] buttonText = {{"<-", "xⁿ", "sin", "cos", "tan"},
                                {"π", "ⁿ√", "sin‾¹", "cos‾¹", "tan‾¹"}};

        int maxWidth = buttonText[0].length;
        int maxHeight = buttonText.length;
        JButton[][] calcButton = new JButton[maxHeight][maxWidth];
        
        // cycle through button array and build
        for (int yPos = 0; yPos < maxHeight; yPos++) {
            for (int xPos = 0; xPos < maxWidth; xPos++) {
                calcButton[yPos][xPos] = new JButton(buttonText[yPos][xPos]);
                calcButton[yPos][xPos].setFont(calcButton[yPos][xPos].getFont().deriveFont(30f));
                calcButton[yPos][xPos].addActionListener(this);
                
                gbcButtonPanel.gridx = xPos;
                gbcButtonPanel.gridy = yPos;
                gbcButtonPanel.gridwidth = 1;
                gbcButtonPanel.weightx = 1;
                gbcButtonPanel.weighty = 1;
                gbcButtonPanel.gridheight = 1;
                Insets buttonSpacing = new Insets(2,2,2,2);
                gbcButtonPanel.insets = buttonSpacing;
                gbcButtonPanel.fill = GridBagConstraints.BOTH;
                
                panel.add(calcButton[yPos][xPos], gbcButtonPanel);
            }
        }
        return panel;
    }
    
    // INITIALIZE GUI LOOK AND FEEL
    private void initGUI() {
        try {
            // sets the look and feel to that of the OS if possible
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            // tell us a story
            System.out.printf("Unable to load GUI.%n%s%n", e.getMessage());
        } 
        
        // set some window and frame options
        setTitle(PROJ_TITLE);
//        setPreferredSize(new Dimension(PROJ_WINDOW_SIZE[0], PROJ_WINDOW_SIZE[1]));
        setResizable(false);
        setLocation(PROJ_WINDOW_LOCATION[0], PROJ_WINDOW_LOCATION[1]);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
    private boolean calcButtonTest(String s) {
        try {
            Integer.parseInt(s);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }
    
    // UPDATE DISPLAY
    private void updateDisplay(String s) {
        StringBuilder displayString = new StringBuilder();
        System.out.println(s);
        if (clearScreen) {
            upperDisplay.setText("");
            clearScreen = false;
        }
        
        if (Double.parseDouble(s) < 0) {
            System.out.println("neg");
        }
        
        if (upperDisplay.getText().length() + 1 <= 15) {
            
            // DECIMAL HANDLER
            if (s.equals(".")){
                if (!upperDisplay.getText().contains(s)) {
                    if (upperDisplay.getText().equals("0")) {
                        upperDisplay.setText(String.format("0%s", s));
                    } else {
                        upperDisplay.setText(String.format("%s", upperDisplay.getText() + s));
                    }
                }
            } else {
                if (upperDisplay.getText().equals("0")) {
                    upperDisplay.setText(String.format("%s", s));
                } else {
                    upperDisplay.setText(String.format("%s", upperDisplay.getText() + s));
                }
            }
        }
    }

    // DELETE LAST CHARACTER
    private void deletePressed(){
        int l = upperDisplay.getText().length();
        int n = upperDisplay.getText().length() - 1;
        
        if (l > 1) {
            StringBuilder backSpace = new StringBuilder(upperDisplay.getText());
            upperDisplay.setText(backSpace.deleteCharAt(n).toString());
        } else {
            upperDisplay.setText("0");
        }
    } 
    
}

尽管看起来一切都不正常,但其他所有基本运算符都可以正常工作。乘法、除法和减法都可以正常工作。

特别针对您的问题,+ operation 的行为与其他行为不同,解释为您的 switch 陈述中存在差异。

operation 3 及更高版本(在此代码段中实现的那些)的调用都直接调用 upperDisplay.setText(...) 并导致上面的显示输出被覆盖。您的 + 操作代码调用了您的方法 updateDisplay(...),它具有额外的逻辑,不会直接替换上面显示的文本。

如果你更换

case 2:             // addition
    updateDisplay(String.valueOf(df.format(firstNumber + secondNumber)));
    break;

case 2:             // addition
    upperDisplay.setText(String.valueOf(df.format(firstNumber + secondNumber)));
    break;

那么 + 的行为应该与其他行为一致。

但是,当您开始输入另一个计算时(例如在 1 + 2 输出 3 之后,此实现似乎也不会清除您的显示,按 4 将导致您的显示呈现 34 并且 34 最终被解析为您的下一个“第一个”数字而不是 4).

您可能需要考虑在 switch 语句的末尾附加以下内容:


if (!upperDisplay.getText().equals("0")) {
    secondNumber = Double.parseDouble(upperDisplay.getText());

    switch(operation) {
        ...
    }
    clearScreen = true; // <- add this
}

以便您可以无缝过渡到进入下一个计算。