怎样通过一个简单的计算器案例掌握Java的事件监听机制

wen java案例 54

本文目录导读:

怎样通过一个简单的计算器案例掌握Java的事件监听机制

  1. 最基础版本(理解核心概念)
  2. 使用内部类实现(更好的组织代码)
  3. 完整功能版本(使用Lambda表达式)
  4. 关键概念总结

我来通过一个简单的计算器案例,系统地讲解Java事件监听机制,这个案例会从最基础的版本逐步扩展到更完善的实现。

最基础版本(理解核心概念)

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class CalculatorV1 {
    public static void main(String[] args) {
        // 创建窗口
        JFrame frame = new JFrame("简易计算器 V1");
        frame.setSize(300, 200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 创建组件
        JTextField num1Field = new JTextField(10);
        JTextField num2Field = new JTextField(10);
        JButton addButton = new JButton("相加");
        JLabel resultLabel = new JLabel("结果: ");
        // 设置布局
        JPanel panel = new JPanel();
        panel.add(new JLabel("数字1:"));
        panel.add(num1Field);
        panel.add(new JLabel("数字2:"));
        panel.add(num2Field);
        panel.add(addButton);
        panel.add(resultLabel);
        // 核心:注册事件监听器
        // ActionListener是一个接口,我们通过匿名内部类实现它
        addButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    // 获取输入
                    double num1 = Double.parseDouble(num1Field.getText());
                    double num2 = Double.parseDouble(num2Field.getText());
                    // 进行计算
                    double result = num1 + num2;
                    // 显示结果
                    resultLabel.setText("结果: " + result);
                } catch (NumberFormatException ex) {
                    resultLabel.setText("输入格式错误!");
                }
            }
        });
        frame.add(panel);
        frame.setVisible(true);
    }
}

使用内部类实现(更好的组织代码)

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class CalculatorV2 {
    private JFrame frame;
    private JTextField num1Field;
    private JTextField num2Field;
    private JLabel resultLabel;
    public CalculatorV2() {
        initializeGUI();
        addEventListeners();
    }
    private void initializeGUI() {
        frame = new JFrame("简易计算器 V2");
        frame.setSize(400, 200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        num1Field = new JTextField(10);
        num2Field = new JTextField(10);
        resultLabel = new JLabel("结果: ");
        // 创建多个按钮
        JButton addBtn = new JButton("+");
        JButton subtractBtn = new JButton("-");
        JButton multiplyBtn = new JButton("×");
        JButton divideBtn = new JButton("÷");
        JPanel panel = new JPanel(new GridLayout(3, 1));
        // 输入面板
        JPanel inputPanel = new JPanel();
        inputPanel.add(new JLabel("数字1:"));
        inputPanel.add(num1Field);
        inputPanel.add(new JLabel("数字2:"));
        inputPanel.add(num2Field);
        // 按钮面板
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(addBtn);
        buttonPanel.add(subtractBtn);
        buttonPanel.add(multiplyBtn);
        buttonPanel.add(divideBtn);
        // 结果面板
        JPanel resultPanel = new JPanel();
        resultPanel.add(resultLabel);
        panel.add(inputPanel);
        panel.add(buttonPanel);
        panel.add(resultPanel);
        frame.add(panel);
        // 使用内部类作为事件监听器
        CalculatorActionListener listener = new CalculatorActionListener();
        addBtn.addActionListener(listener);
        subtractBtn.addActionListener(listener);
        multiplyBtn.addActionListener(listener);
        divideBtn.addActionListener(listener);
    }
    // 内部类实现事件监听器 - 更好的代码组织
    private class CalculatorActionListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            try {
                double num1 = Double.parseDouble(num1Field.getText());
                double num2 = Double.parseDouble(num2Field.getText());
                double result = 0;
                // 根据按钮的文本来确定操作
                String command = e.getActionCommand();
                switch(command) {
                    case "+":
                        result = num1 + num2;
                        break;
                    case "-":
                        result = num1 - num2;
                        break;
                    case "×":
                        result = num1 * num2;
                        break;
                    case "÷":
                        if (num2 != 0) {
                            result = num1 / num2;
                        } else {
                            resultLabel.setText("不能除0!");
                            return;
                        }
                        break;
                }
                resultLabel.setText("结果: " + result);
            } catch (NumberFormatException ex) {
                resultLabel.setText("输入格式错误!");
            }
        }
    }
    private void addEventListeners() {
        // 可以添加键盘事件等
    }
    public void show() {
        frame.setVisible(true);
    }
    public static void main(String[] args) {
        new CalculatorV2().show();
    }
}

完整功能版本(使用Lambda表达式)

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BinaryOperator;
public class CalculatorV3 {
    private JFrame frame;
    private JTextField displayField;
    private JLabel resultLabel;
    private String currentOperator = "";
    private double firstNumber = 0;
    private boolean isNewNumber = true;
    // 操作映射 - 使用Lambda表达式
    private Map<String, BinaryOperator<Double>> operations = new HashMap<>();
    public CalculatorV3() {
        // 初始化操作映射 - 这里展示了Lambda表达式的简洁性
        operations.put("+", (a, b) -> a + b);
        operations.put("-", (a, b) -> a - b);
        operations.put("*", (a, b) -> a * b);
        operations.put("/", (a, b) -> b != 0 ? a / b : Double.NaN);
        initializeGUI();
        setupEventListeners();
    }
    private void initializeGUI() {
        frame = new JFrame("科学计算器 V3");
        frame.setSize(300, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        displayField = new JTextField();
        displayField.setEditable(false);
        displayField.setFont(new Font("Arial", Font.BOLD, 20));
        displayField.setHorizontalAlignment(JTextField.RIGHT);
        resultLabel = new JLabel(" ", SwingConstants.RIGHT);
        resultLabel.setFont(new Font("Arial", Font.PLAIN, 14));
        // 创建按钮
        String[][] buttonLabels = {
            {"7", "8", "9", "/"},
            {"4", "5", "6", "*"},
            {"1", "2", "3", "-"},
            {"0", ".", "=", "+"},
            {"C", "CE", "←", "±"}
        };
        JPanel buttonPanel = new JPanel(new GridLayout(5, 4, 2, 2));
        for (String[] row : buttonLabels) {
            for (String label : row) {
                JButton button = new JButton(label);
                button.setFont(new Font("Arial", Font.PLAIN, 16));
                buttonPanel.add(button);
            }
        }
        // 布局
        frame.setLayout(new BorderLayout());
        JPanel topPanel = new JPanel(new GridLayout(2, 1));
        topPanel.add(displayField);
        topPanel.add(resultLabel);
        frame.add(topPanel, BorderLayout.NORTH);
        frame.add(buttonPanel, BorderLayout.CENTER);
    }
    private void setupEventListeners() {
        // 获取所有按钮
        Component[] components = ((JPanel)frame.getContentPane()
            .getComponent(1)).getComponents();
        for (Component comp : components) {
            if (comp instanceof JButton) {
                JButton button = (JButton) comp;
                String text = button.getText();
                // 使用Lambda表达式注册事件监听器
                button.addActionListener(e -> handleButtonClick(e));
            }
        }
        // 也可以使用键盘快捷键
        KeyboardFocusManager.getCurrentKeyboardFocusManager()
            .addKeyEventDispatcher(e -> {
                if (e.getID() == KeyEvent.KEY_PRESSED) {
                    char keyChar = e.getKeyChar();
                    if (Character.isDigit(keyChar) || 
                        "+-*/.=".indexOf(keyChar) >= 0) {
                        handleKeyInput(String.valueOf(keyChar));
                        return true;
                    }
                }
                return false;
            });
    }
    private void handleButtonClick(ActionEvent e) {
        JButton source = (JButton) e.getSource();
        handleInput(source.getText());
    }
    private void handleKeyInput(String input) {
        handleInput(input);
    }
    private void handleInput(String input) {
        switch(input) {
            case "C":
                clearAll();
                break;
            case "CE":
                clearEntry();
                break;
            case "←":
                backspace();
                break;
            case "±":
                toggleSign();
                break;
            case "=":
                calculate();
                break;
            case "+": case "-": case "*": case "/":
                setOperator(input);
                break;
            default: // 数字和小数点
                appendDigit(input);
                break;
        }
    }
    private void appendDigit(String digit) {
        if (isNewNumber) {
            displayField.setText(digit);
            isNewNumber = false;
        } else {
            String current = displayField.getText();
            if (digit.equals(".") && current.contains(".")) {
                return; // 防止多个小数点
            }
            displayField.setText(current + digit);
        }
    }
    private void setOperator(String operator) {
        if (!displayField.getText().isEmpty()) {
            firstNumber = Double.parseDouble(displayField.getText());
            currentOperator = operator;
            isNewNumber = true;
            resultLabel.setText(firstNumber + " " + operator);
        }
    }
    private void calculate() {
        if (!currentOperator.isEmpty() && !displayField.getText().isEmpty()) {
            double secondNumber = Double.parseDouble(displayField.getText());
            // 使用Lambda表达式定义的操作
            BinaryOperator<Double> operation = operations.get(currentOperator);
            if (operation != null) {
                double result = operation.apply(firstNumber, secondNumber);
                if (Double.isNaN(result)) {
                    displayField.setText("错误: 除数不能为0");
                } else {
                    displayField.setText(String.valueOf(result));
                    resultLabel.setText(firstNumber + " " + 
                        currentOperator + " " + secondNumber + " =");
                }
            }
            currentOperator = "";
            isNewNumber = true;
        }
    }
    private void clearAll() {
        displayField.setText("");
        resultLabel.setText("");
        currentOperator = "";
        firstNumber = 0;
        isNewNumber = true;
    }
    private void clearEntry() {
        displayField.setText("");
        isNewNumber = true;
    }
    private void backspace() {
        String text = displayField.getText();
        if (text.length() > 0) {
            displayField.setText(text.substring(0, text.length() - 1));
        }
    }
    private void toggleSign() {
        if (!displayField.getText().isEmpty()) {
            double value = Double.parseDouble(displayField.getText());
            displayField.setText(String.valueOf(-value));
        }
    }
    public void show() {
        frame.setVisible(true);
    }
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全
        SwingUtilities.invokeLater(() -> {
            new CalculatorV3().show();
        });
    }
}

关键概念总结

事件监听机制的三个核心组件:

// 1. 事件源 (Event Source) - 谁触发事件
JButton button = new JButton("点击我");
// 2. 事件对象 (Event Object) - 事件信息
// ActionEvent包含了事件的相关信息
// 3. 事件监听器 (Event Listener) - 如何处理事件
// 实现特定接口的对象
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // 事件处理代码
    }
});

五种监听器实现方式:

// 方式1: 匿名内部类 (最常用)
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("按钮被点击");
    }
});
// 方式2: Lambda表达式 (Java 8+)
button.addActionListener(e -> System.out.println("按钮被点击"));
// 方式3: 方法引用
button.addActionListener(this::handleClick);
// 方式4: 内部类
class MyListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        handleClick(e);
    }
}
// 方式5: 外部类
button.addActionListener(new ExternalActionListener());

事件处理的最佳实践:

// 1. 线程安全 - 使用SwingUtilities
SwingUtilities.invokeLater(() -> {
    // UI更新代码
});
// 2. 避免长时间操作在事件线程
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
    @Override
    protected Void doInBackground() {
        // 耗时操作
        return null;
    }
    @Override
    protected void done() {
        // 更新UI
    }
};
worker.execute();
// 3. 使用适配器类减少代码
window.addWindowListener(new WindowAdapter() {
    @Override
    public void windowClosing(WindowEvent e) {
        // 只重写需要的方法
    }
});

这个计算器案例从最简单的版本开始,逐步增加了功能并展示了不同的事件处理方式,通过实践这些代码,你可以深入理解Java事件监听机制的工作原理和应用方式。

抱歉,评论功能暂时关闭!