Java Swing实现简易计算器

6 篇文章 1 订阅

 欢迎大家浏览我的主页和博客:https://www.ovvv.tophttps://blog.ovvv.top


 

 项目简介

 

 MVC模型

项目类简介(下载在文末):

计算器的窗口, 用户界面

按钮监听和键盘监听,相当于控制器

数据处理, 相当于模型

大体思路,

用户点击按钮或者按键盘, 控制器会监听, 获取到事件返回的值,

并调用数据处理类的handleValue方法, 根据返回的值进入switch来判断要做什么事.

 这里给出窗口的设计代码, 我大量采用了盒子布局

(注: 如果想看窗口不搞事件监听把它去掉就可以了)

import javax.swing.*;
import java.awt.*;

/**
 * @author mobeicanyue
 * Create  2021-12-18 10:35
 * Describe:计算器的窗口
 */

public class CalculatorWindow extends JFrame {
    JTextField[] text = new JTextField[4];

    List resultList = new List(11, false);//列表选择框

    //按钮监听和事件监听
    ButtonAction buttonAction = new ButtonAction(this);
    KeyAction keyAction = new KeyAction(this);

    public void basicInit() { //初始化窗体的基本设置
        setTitle("create by mobeiCanyue                                       Calculator");
        setResizable(false);
        setBounds(200, 200, 700, 340);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
        setIconImage(Toolkit.getDefaultToolkit().getImage(CalculatorWindow.class.getResource("Porsche.png")));
    }

    public CalculatorWindow() {
        basicInit();//初始化

        add(Box.createVerticalStrut(8));

        JPanel topPanel = loadTopPanel(text, keyAction);//加载顶部的那四个显示框,放至topPanel
        add(topPanel);

        add(Box.createVerticalStrut(8));

        final Box bottomBox = Box.createHorizontalBox();//除了顶部,下面的盒子,样式为水平
        bottomBox.add(Box.createHorizontalStrut(7));//左边的间距

        final Font font = new Font("等线", Font.BOLD, 16);

        final JButton[][] buttons = new JButton[4][5];//左边的若干按钮
        Box buttonBox = loadButtonBox(font, buttons, buttonAction, keyAction);//加载左边一大块按钮
        bottomBox.add(buttonBox);

        bottomBox.add(Box.createHorizontalStrut(3)); //左右中间的间隔

        final Box rightBox = Box.createVerticalBox();//bottomBox的右边一大块
        //加入列表选择框
        rightBox.add(resultList);

        rightBox.add(Box.createVerticalStrut(10));//文本域距离下边三个盒子的距离

        final JButton[] buttons2 = new JButton[3];//右下的若干按钮
        Box buttonBox2 = loadButtonBox2(font, buttons2, buttonAction);//加载第二个承载按钮的盒子
        rightBox.add(buttonBox2);

        bottomBox.add(Box.createHorizontalStrut(2));
        bottomBox.add(rightBox);
        bottomBox.add(Box.createHorizontalStrut(7));
        add(bottomBox);
        try {//换主题
            for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        }catch(Exception e) {
            e.printStackTrace();
        }
        setVisible(true);
    }

    public JPanel loadTopPanel(JTextField[] text, KeyAction keyAction) {
        final JPanel topPanel = new JPanel();//顶部的四个文本框容器,JPanel默认流布局
        //使用的字体及大小
        final Font font = new Font("", Font.BOLD, 25);
        //上面的四个文本框
        text[0] = new JTextField(9);
        text[1] = new JTextField(2);
        text[2] = new JTextField(9);
        text[3] = new JTextField(13);

        topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.X_AXIS));

        for (JTextField jTextField : text) {
            jTextField.setEditable(false);
            jTextField.setFont(font);
            jTextField.addKeyListener(keyAction);
            jTextField.setHorizontalAlignment(JTextField.CENTER);
        }

        text[0].setPreferredSize(new Dimension(175, 9));
        text[1].setPreferredSize(new Dimension(50, 9));
        text[2].setPreferredSize(new Dimension(175, 9));
        text[3].setPreferredSize(new Dimension(175, 9));

        text[0].setText("0");

        topPanel.add(Box.createHorizontalStrut(7));
        for (int i = 0; i < 3; i++) {
            topPanel.add(text[i]);
        }
        //因为面板要加间距,所以分开加进去
        topPanel.add(Box.createHorizontalStrut(8));
        topPanel.add(text[3]);
        topPanel.add(Box.createHorizontalStrut(5));
        return topPanel;
    }

    public Box loadButtonBox(Font font, JButton[][] buttons, ButtonAction buttonAction, KeyAction keyAction) {
        final Box buttonBox = Box.createVerticalBox();//bottomBox的左边一大块按钮

        final JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new GridLayout(4, 5, 5, 10));
        final String[][] names = {
                {"1", "2", "3", "/", "C"}, {"4", "5", "6", "*", "退格"},
                {"7", "8", "9", "-", "sqrt"}, {"0", "+/-", ".", "+", "="}
        };
        for (int row = 0; row < names.length; row++) {
            for (int col = 0; col < names[0].length; col++) {
                buttons[row][col] = new JButton(names[row][col]);// 创建按钮
                buttons[row][col].setFont(font);
                buttons[row][col].setPreferredSize(new Dimension(65, 50));
                buttons[row][col].addActionListener(buttonAction);
                buttons[row][col].addKeyListener(keyAction);
                buttonPanel.add(buttons[row][col]); // 将按钮添加到按钮面板中
            }
        }
        buttonBox.add(buttonPanel);
        buttonBox.add(Box.createVerticalStrut(5));//下面的边距
        return buttonBox;
    }

    public Box loadButtonBox2(Font font, JButton[] buttons2, ButtonAction buttonAction) {
        final Box buttonBox2 = Box.createVerticalBox();//第二个承载按钮的盒子

        //final Font font = new Font("微软雅黑", Font.BOLD, 14);
        JPanel buttonPanel2 = new JPanel();
        buttonPanel2.setLayout(new GridLayout(1, 3, 8, 8));

        buttons2[0] = new JButton("保存");
        buttons2[1] = new JButton("查看");
        buttons2[2] = new JButton("清除");
        for (JButton button : buttons2) {
            button.setPreferredSize(new Dimension(55, 43));
            buttonPanel2.add(button);
            button.setFont(font);
            button.addActionListener(buttonAction);
        }
        buttonBox2.add(buttonPanel2);

        buttonBox2.add(Box.createVerticalStrut(5));//右盒子距离下边的宽度
        return buttonBox2;
    }
}
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author mobeiCanyue
 * Create  2021-12-24 19:25
 * Describe: 用于处理数据的类,因为按钮事件和键盘事件的处理高度重合,所以分离出来简化代码
 */
public class DataHandle {
    static StringBuilder s1 = new StringBuilder();//左
    static String s2;//右
    static StringBuilder s3 = new StringBuilder();//中

    //"LEFT" "RIGHT" "MID" "RESULT"
    static String status = "LEFT";//初始化窗口默认位置,*******非常重要,看懂这个即可理解本程序的流程控制,通过改变它来决定文本框位置!

    public static void changeValue(String tabValue, StringBuilder s, JTextField jTextField) {
        s.append(tabValue);
        jTextField.setText(s.toString());
    }

    public static void cleanPanel(StringBuilder s1, StringBuilder s3, JTextField[] texts) {
        s1.delete(0, s1.length());
        s3.delete(0, s3.length());
        for (JTextField jTextField : texts) {
            jTextField.setText("");
        }
        texts[0].setText("0");
        status = "LEFT";
    }

    public static float calculate(StringBuilder s1, String s2, StringBuilder s3, JTextField textField) {
        float result = 0;
        switch (s2) {
            case "/":
                result = Float.parseFloat(s1.toString()) / Float.parseFloat(s3.toString());
                textField.setText(String.valueOf(result));
                break;
            case "*":
                result = Float.parseFloat(s1.toString()) * Float.parseFloat(s3.toString());
                textField.setText(String.valueOf(result));
                break;
            case "+":
                result = Float.parseFloat(s1.toString()) + Float.parseFloat(s3.toString());
                textField.setText(String.valueOf(result));
                break;
            case "-":
                result = Float.parseFloat(s1.toString()) - Float.parseFloat(s3.toString());
                textField.setText(String.valueOf(result));
                break;
        }
        return result;
    }

    public static void back(StringBuilder s, JTextField textField) {
        if (s.length() != 0) {
            s.delete(s.length() - 1, s.length());
            textField.setText(s.toString());
        }
    }

    public static void addList(StringBuilder s1, String s2, StringBuilder s3, float result, List results) {
        String temp = s1.toString() + " " + s2 + " " + s3.toString() + " = " + result;
        results.add(temp);
    }

    public static void positive_negative(StringBuilder sb, JTextField textField) {
        if (sb.toString().equals("0")) {//为0则不变正负
            return;
        }
        if (String.valueOf(sb.charAt(0)).equals("-")) {
            sb.deleteCharAt(0);
        } else {
            sb.insert(0, "-");
        }
        textField.setText(sb.toString());
    }

    public static void saveList(List list) {
        Date date = new Date();//获取当前时间
        SimpleDateFormat dateFormat = new SimpleDateFormat("'Date' yyyy.MM.dd 'Time' HH.mm.ss");
        String fileName = dateFormat.format(date) + ".txt";//文件名
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(fileName))) {
            String[] items = list.getItems();//获得列表的所有元素的String数组
            bw.write(dateFormat.format(date) + "\n");
            for (String item : items) {
                bw.write(item + "\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void viewFile(CalculatorWindow calculatorWindow, List list) {

        String fileName = new FileChooseDialog(calculatorWindow).getFile();
        if (fileName == null) {
            System.out.println("未选择文件");
            return;
        }

        try (BufferedReader br = new BufferedReader(new FileReader(fileName))
        ) {
            list.removeAll();
            int len;
            char[] data = new char[20];
            StringBuilder temp = new StringBuilder();
            while ((len = br.read(data)) != -1) {
                temp.append(data, 0, len);
            }
            int head = 0;
            int index = temp.indexOf("\n");
            while (index != temp.length()) {
                String substring = temp.substring(head, index);
                System.out.println("substring = " + substring);
                list.add(substring);
                temp.delete(head, index + 1);
                index = temp.indexOf("\n");
                if (index == -1) {
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void handleValue(String tabValue, CalculatorWindow calculatorWindow) {
        switch (tabValue) {
            case "0":
            case "1":
            case "2":
            case "3":
            case "4":
            case "5":
            case "6":
            case "7":
            case "8":
            case "9":
                if ("RESULT".equals(status)) {
                    System.out.println(status);
                    cleanPanel(s1, s3, calculatorWindow.text); //先清除再做
                    changeValue(tabValue, s1, calculatorWindow.text[0]);
                    break;
                }
                if ("LEFT".equals(status)) {
                    System.out.println(status);
                    changeValue(tabValue, s1, calculatorWindow.text[0]);
                    break;
                }
                if ("MID".equals(status)) {//如果状态在中间就做
                    if (s2.equals("")) {
                        break;
                    }
                    status = "RIGHT";//更改状态,此时在右边数字框
                }
                if ("RIGHT".equals(status)) {
                    System.out.println(status);
                    changeValue(tabValue, s3, calculatorWindow.text[2]);
                    break;
                }
            case ".":
                if ("LEFT".equals(status)) {
                    if (s1.indexOf(".") != -1) {
                        break;
                    }
                    System.out.println(status);
                    changeValue(tabValue, s1, calculatorWindow.text[0]);
                    break;
                }
                if ("RIGHT".equals(status)) {
                    if (s3.indexOf(".") != -1) {
                        break;
                    }
                    System.out.println(status);
                    changeValue(tabValue, s3, calculatorWindow.text[2]);
                    break;
                }

            case "sqrt":
                if ("LEFT".equals(status)) {
                    if (s1.toString().equals("")) {
                        break;
                    }
                    s1 = new StringBuilder(String.valueOf(Math.sqrt(Float.parseFloat(s1.toString()))));
                    calculatorWindow.text[0].setText(s1.toString());
                }
                if ("RIGHT".equals(status)) {
                    if (s3.toString().equals("")) {
                        break;
                    }
                    s3 = new StringBuilder(String.valueOf(Math.sqrt(Float.parseFloat(s3.toString()))));
                    calculatorWindow.text[2].setText(s3.toString());
                }
                break;
            case "退格":
                if ("RESULT".equals(status)) {
                    break;//结果出来了不能退格
                }
                if ("LEFT".equals(status)) {
                    if (s1.length() == 1) {
                        back(s1, calculatorWindow.text[0]);
                        calculatorWindow.text[0].setText("0");
                    } else {
                        back(s1, calculatorWindow.text[0]);
                    }
                    break;
                }
                if ("MID".equals(status)) {
                    if (s2.equals("")) {//如果中间为空就跑到前面去
                        status = "LEFT";
                        back(s1, calculatorWindow.text[0]);
                        break;
                    }
                    s2 = "";
                    calculatorWindow.text[1].setText(s2);
                    break;
                }
                if ("RIGHT".equals(status)) {
                    if (s3.toString().equals("")) {//如果后面为空就跑到中间去
                        status = "MID";
                        s2 = "";
                        calculatorWindow.text[1].setText(s2);
                    }
                    back(s3, calculatorWindow.text[2]);
                    break;
                }

            case "/":
            case "*":
            case "-":
            case "+":
                if (s1.length() == 0 || status.equals("RESULT")) {
                    break;
                }
                calculatorWindow.text[1].setText(tabValue);//设置文本框显示
                s2 = tabValue; //设置运算符
                status = "MID";
                System.out.println(status);
                break;
            case "+/-":
                if (status.equals("LEFT")) {
                    positive_negative(s1, calculatorWindow.text[0]);
                }
                if (status.equals("RIGHT")) {
                    positive_negative(s3, calculatorWindow.text[2]);
                }
                break;
            case "=":
                if (!"RIGHT".equals(status)) {
                    break;
                }
                status = "RESULT";
                System.out.println(status);
                float result = calculate(s1, s2, s3, calculatorWindow.text[3]);
                addList(s1, s2, s3, result, calculatorWindow.resultList);
                break;

            case "C":
                cleanPanel(s1, s3, calculatorWindow.text);
                break;

            case "清除":
                calculatorWindow.resultList.removeAll();
                break;

            case "保存":
                if (calculatorWindow.resultList.getItemCount() == 0) {
                    JOptionPane.showMessageDialog(calculatorWindow, "列表为空! ! ! ", "列表为空", JOptionPane.ERROR_MESSAGE);
                } else {
                    saveList(calculatorWindow.resultList);
                    JOptionPane.showMessageDialog(calculatorWindow, "保存成功", "保存成功", JOptionPane.INFORMATION_MESSAGE);
                }
                break;

            case "查看":
                viewFile(calculatorWindow, calculatorWindow.resultList);
                break;
        }
    }
}

小细节:

  • 我用了一个字符串来存储窗口当前的位置, 比如初始字符输入是在左边, 我不停的输入数字, 不会改变状态量, 然后我按了运算符, 这时候状态量就改到中间, 如果再按字符串, 就会又变到右边(在之后处理别的操作时, 这么写很方便)
  • 每次按按钮或者按键盘, 都会在命令行输出按钮的值以及当前状态(字符串在哪个窗口输入的)
  • 如果字符串存在了小数点, 就不能再进字符串了, 直接break 

  • 结果出来了就不给退格了, 左边的窗口没有值的时候为0

  •  改变计算器框里面的值, 通常的思路是: 输入了一个值, 1.从文本框中取字符串, 2.把值追加到字符串后面或前面, 3.再设置到文本框里面,   
  • 我是这么做的, 我把文本框里的值单独拿出来,1.拿到值就直接追加,2.再设置到文本框. 这样在追加的时候就少了取值再追加再设置的步骤(当然, 要注意每次保证两者的同步), 而且用的是StringBuilder, 大大提高效率

  • 保存历史记录到文件, 如果为空会给警告

完整的代码放在GitHub上了, 大家需要的自取,

mobeiCanyue/Java_calculator: Calculator implements by Java (github.com)

如果是只想跑一下, 可以只下载jar文件(当然你得有Java环境)

Releases · mobeiCanyue/Java_calculator (github.com)

  • 3
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值