计算器的窗口, 用户界面
按钮监听和键盘监听,相当于控制器
数据处理, 相当于模型
大体思路,
用户点击按钮或者按键盘, 控制器会监听, 获取到事件返回的值,
并调用数据处理类的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;
}
}
}
小细节:
完整的代码放在GitHub上了, 大家需要的自取,
mobeiCanyue/Java_calculator: Calculator implements by Java (github.com)
如果是只想跑一下, 可以只下载jar文件(当然你得有Java环境)
Releases · mobeiCanyue/Java_calculator (github.com)