首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java Swing 按鈕陣列實作計算機介面完整教學

     編輯:Java 相關知識

前言

Swing 雖屬舊世代 GUI 框架,但輕量、跨平台、無額外依賴,適合教學與中小型桌面工具。本文以「按鈕二維陣列 + GridLayout」快速建構計算機介面,示範事件委派、元件動態生成、版面管理等核心技巧。

完整程式碼

package com.example.swing;

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

public class CalculatorUI extends JFrame {
    private static final String[][] KEYS = {
        {"7", "8", "9", "/"},
        {"4", "5", "6", "*"},
        {"1", "2", "3", "-"},
        {"0", ".", "=", "+"}
    };
    private JTextField display;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (Exception ignored) {}
            new CalculatorUI().setVisible(true);
        });
    }

    public CalculatorUI() {
        super("計算機");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new BorderLayout(10, 10));
        ((JComponent) getContentPane()).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

        // 顯示區
        display = new JTextField();
        display.setHorizontalAlignment(SwingConstants.RIGHT);
        display.setFont(new Font(Font.MONOSPACED, Font.BOLD, 28));
        display.setPreferredSize(new Dimension(300, 60));
        display.setEditable(false);
        add(display, BorderLayout.NORTH);

        // 按鈕面板
        JPanel panel = new JPanel(new GridLayout(KEYS.length, KEYS[0].length, 5, 5));
        for (int r = 0; r < KEYS.length; r++) {
            for (int c = 0; c < KEYS[r].length; c++) {
                String label = KEYS[r][c];
                JButton btn = new JButton(label);
                btn.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 22));
                btn.addActionListener(e -> onKeyPressed(((JButton) e.getSource()).getText()));
                panel.add(btn);
            }
        }
        add(panel, BorderLayout.CENTER);

        pack();
        setLocationRelativeTo(null); // 置中
        setResizable(false);
    }

    private void onKeyPressed(String key) {
        String txt = display.getText();
        switch (key) {
            case "=" -> evaluate();
            case "C" -> display.setText(""); // 若有清除鍵
            default -> display.setText(txt + key);
        }
    }

    private void evaluate() {
        try {
            // 簡易四則運算:使用 ScriptEngine (JS) 或自行解析
            // 這裡示範 ScriptEngine (JDK 內建 Nashorn/Rhino)
            javax.script.ScriptEngine engine = new javax.script.ScriptEngineManager().getEngineByName("js");
            Object result = engine.eval(display.getText());
            display.setText(result.toString());
        } catch (Exception ex) {
            display.setText("Error");
        }
    }
}

關鍵設計點解析

技術說明
String[][] KEYS宣告式定義鍵盤配置,修改版面只需改陣列,不動程式邏輯。
GridLayout(rows, cols, hgap, vgap)自動等寬等高排列按鈕,完美符合計算機網格需求。
SwingUtilities.invokeLater確保 GUI 建構在 Event Dispatch Thread (EDT) 執行,避免執行緒安全問題。
UIManager.setLookAndFeel套用系統原生外觀 (Windows/Mac/Linux),提升使用者體驗。
Lambda 事件處理btn.addActionListener(e -> onKeyPressed(...)) 簡潔替代匿名類別。
ScriptEngine幾行程式碼支援完整運算優先序、括號、函數,免手寫解析器。

進階擴充:真正的計算機邏輯 (狀態機)

上方 ScriptEngine 雖方便,但無法處理「連續運算」(如 1+2=3 → +3=6)。實務上需實作狀態機:

enum State { FIRST_OPERAND, OPERATOR, SECOND_OPERAND, RESULT }
private State state = State.FIRST_OPERAND;
private double operand1 = 0;
private String pendingOp = "";

private void onKeyPressed(String key) {
    if ("0123456789.".contains(key)) {
        if (state == State.RESULT || state == State.OPERATOR) {
            display.setText(key);
            state = State.SECOND_OPERAND;
        } else {
            display.setText(display.getText() + key);
        }
    } else if ("+-*/".contains(key)) {
        if (state == State.SECOND_OPERAND) evaluatePending();
        operand1 = Double.parseDouble(display.getText());
        pendingOp = key;
        state = State.OPERATOR;
    } else if ("=".equals(key)) {
        evaluatePending();
        state = State.RESULT;
    }
}

private void evaluatePending() {
    double operand2 = Double.parseDouble(display.getText());
    double result = switch (pendingOp) {
        case "+" -> operand1 + operand2;
        case "-" -> operand1 - operand2;
        case "*" -> operand1 * operand2;
        case "/" -> operand2 != 0 ? operand1 / operand2 : Double.NaN;
        default -> operand2;
    };
    display.setText(String.valueOf(result));
    operand1 = result;
}

常見問題與解法

  • 按鈕大小不一:GridLayout 強制等大;若需不同大小,改用 GridBagLayout 或巢狀 JPanel。
  • 鍵盤輸入支援:為 JFrame 加入 KeyListener 或使用 KeyBinding (推薦) 映射數字鍵、Enter、Backspace。
  • 高 DPI 模糊:JDK 9+ 加啟動參數 -Dsun.java2d.uiScale.enabled=true;或使用 UIManager.put("swing.boldMetal", Boolean.FALSE)。
  • 打包執行:maven-shade-plugin 打 Fat JAR,`Main-Class` 指向此類別。

現代替代方案

  • JavaFX:FXML 宣告式 UI、CSS 樣式、硬體加速、原生打包 (jlink/jpackage)。
  • Compose for Desktop (Kotlin):宣告式 UI、跨平台 (JVM/Desktop/Web)、熱重載開發。
  • TornadoFX / FXLauncher:JavaFX 衍生框架,簡化開發。

新專案建議直接採用 JavaFX 或 Compose for Desktop,Swing 僅維護現有專案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved