Project introduction
MVC model
Project introduction (download at the end of the text):
Calculator window, user interface
Button monitoring and keyboard monitoring are equivalent to controllers
Data processing, equivalent to model
General idea,
When the user clicks the button or presses the keyboard, the controller will listen and obtain the returned value of the event,
And call the handleValue method of the data processing class to enter the switch according to the returned value to judge what to do
Here is the design code of the window. I use a lot of box layout
(Note: if you want to see the window without event monitoring, just remove it)
import javax.swing.*; import java.awt.*; /** * @author mobeicanyue * Create 2021-12-18 10:35 * Describe:Calculator window */ public class CalculatorWindow extends JFrame { JTextField[] text = new JTextField[4]; List resultList = new List(11, false);//List selection box //Button listening and event listening ButtonAction buttonAction = new ButtonAction(this); KeyAction keyAction = new KeyAction(this); public void basicInit() { //Initialize the basic settings of the form 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();//initialization add(Box.createVerticalStrut(8)); JPanel topPanel = loadTopPanel(text, keyAction);//Load the top four display boxes and place them in topPanel add(topPanel); add(Box.createVerticalStrut(8)); final Box bottomBox = Box.createHorizontalBox();//In addition to the top, the lower box, the style is horizontal bottomBox.add(Box.createHorizontalStrut(7));//Left spacing final Font font = new Font("Isoline", Font.BOLD, 16); final JButton[][] buttons = new JButton[4][5];//Several buttons on the left Box buttonBox = loadButtonBox(font, buttons, buttonAction, keyAction);//Load a large button on the left bottomBox.add(buttonBox); bottomBox.add(Box.createHorizontalStrut(3)); //Space between left and right final Box rightBox = Box.createVerticalBox();//A large piece to the right of the bottom box //Add list selection box rightBox.add(resultList); rightBox.add(Box.createVerticalStrut(10));//The distance from the text field to the three boxes below final JButton[] buttons2 = new JButton[3];//Several buttons on the lower right Box buttonBox2 = loadButtonBox2(font, buttons2, buttonAction);//Load the second box that holds the button rightBox.add(buttonBox2); bottomBox.add(Box.createHorizontalStrut(2)); bottomBox.add(rightBox); bottomBox.add(Box.createHorizontalStrut(7)); add(bottomBox); try {//Change the subject 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();//The top four text box containers, JPanel default flow layout //Font and size used final Font font = new Font("", Font.BOLD, 25); //The four text boxes above 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]); } //Because the panels need to be spaced, they are added separately 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();//A large button on the left of the bottom box final JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new GridLayout(4, 5, 5, 10)); final String[][] names = { {"1", "2", "3", "/", "C"}, {"4", "5", "6", "*", "Backspace"}, {"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]);// Create button 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]); // Add button to button panel } } buttonBox.add(buttonPanel); buttonBox.add(Box.createVerticalStrut(5));//Bottom margin return buttonBox; } public Box loadButtonBox2(Font font, JButton[] buttons2, ButtonAction buttonAction) { final Box buttonBox2 = Box.createVerticalBox();//The second box carrying the button //final Font font = new Font("Microsoft YaHei", Font.BOLD, 14); JPanel buttonPanel2 = new JPanel(); buttonPanel2.setLayout(new GridLayout(1, 3, 8, 8)); buttons2[0] = new JButton("preservation"); buttons2[1] = new JButton("see"); buttons2[2] = new JButton("eliminate"); 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));//Width of the right box from the bottom 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: The class used to process data is separated to simplify the code because the processing of button events and keyboard events highly coincide */ public class DataHandle { static StringBuilder s1 = new StringBuilder();//Left static String s2;//right static StringBuilder s3 = new StringBuilder();//in //"LEFT" "RIGHT" "MID" "RESULT" static String status = "LEFT";//The default position of the initialization window, ****** is very important. You can understand the process control of this program and determine the position of the text box by changing it! 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")) {//If it is 0, it remains positive or negative 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();//Get current time SimpleDateFormat dateFormat = new SimpleDateFormat("'Date' yyyy.MM.dd 'Time' HH.mm.ss"); String fileName = dateFormat.format(date) + ".txt";//file name try (BufferedWriter bw = new BufferedWriter(new FileWriter(fileName))) { String[] items = list.getItems();//Gets a String array of all elements of the list 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("No files selected"); 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); //Clear before doing 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 the state is in the middle, do it if (s2.equals("")) { break; } status = "RIGHT";//Change the status, which is in the number box on the 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 "Backspace": if ("RESULT".equals(status)) { break;//The results come out and can't be disqualified } 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("")) {//If the middle is empty, run to the front status = "LEFT"; back(s1, calculatorWindow.text[0]); break; } s2 = ""; calculatorWindow.text[1].setText(s2); break; } if ("RIGHT".equals(status)) { if (s3.toString().equals("")) {//If the back is empty, run to the middle 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);//Set text box display s2 = tabValue; //Set operator 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 "eliminate": calculatorWindow.resultList.removeAll(); break; case "preservation": if (calculatorWindow.resultList.getItemCount() == 0) { JOptionPane.showMessageDialog(calculatorWindow, "The list is empty! ! ! ", "The list is empty", JOptionPane.ERROR_MESSAGE); } else { saveList(calculatorWindow.resultList); JOptionPane.showMessageDialog(calculatorWindow, "Saved successfully", "Saved successfully", JOptionPane.INFORMATION_MESSAGE); } break; case "see": viewFile(calculatorWindow, calculatorWindow.resultList); break; } } }
Small details:
- I use a string to store the current position of the window. For example, the initial character input is on the left. I keep inputting numbers without changing the state quantity. Then I press the operator. At this time, the state quantity will change to the middle. If I press the string again, it will change to the right (this is very convenient for other operations later)
- Each time you press the button or the keyboard, the value of the button and the current state (in which window the string is entered) will be output on the command line
- If there is a decimal point in the string, you can't enter the string. break directly
- When the result comes out, it will not be backspace. When the window on the left has no value, it will be 0
- To change the value in the calculator box, the usual idea is: enter a value, 1 Take the string from the text box, 2 Append the value to or before the string, 3 Then set it in the text box
- This is what I do. I take out the value in the text box separately. 1 Get the value and add it directly, 2 Then set to the text box In this way, there are fewer steps to add and set the value (of course, pay attention to ensure the synchronization of the two each time), and the StringBuilder is used to greatly improve the efficiency
- Save the history to a file. If it is empty, a warning will be given
The complete code is put on GitHub. You need to take it yourself,
mobeiCanyue/Java_calculator: Calculator implements by Java (github.com)
If you just want to run, you can download only jar files (of course, you have to have a Java environment)