Java 面向对象 · 交互课程

事件与GUI基础

事件与GUI基础

目标:理解 GUI 的三类元素(容器、标准组件、自定义成分);掌握「建容器→加组件→设外观→注册事件」四步设计法;能区分 java.awt 与 javax.swing 的组件层次;知道 Graphics/Font/Color/Image 各自的角色。

一、GUI 的三类元素

  1. 容器:组织其他界面元素的单元——外层窗体(Frame/JFrame)、中间层面板(Panel/JPanel);容器通过布局管理器决定元素排列;
  2. 标准组件:Label/JLabel、Button/JButton、TextField、TextArea、CheckBox/JRadioButton、Choice/JComboBox、List/JList 等;
  3. 用户自定义成分:用 Graphics 画线段、矩形、圆、多边形、弧,或加载图像/动画。

二、设计四步法

① 定义最外层容器(JFrame)并设定布局 → ② 加入中间容器与标准组件 → ③ 设计外观(颜色、字体、对齐、位置)→ ④ 注册事件响应,实现交互。

三、AWT 与 Swing

交互实验:AWT ↔ Swing 组件画廊(SwingGallery)

AWT(java.awt)Swing(javax.swing)类别用途
FrameJFrame容器最外层窗体,程序主窗口
PanelJPanel容器中间层面板,用于分组
DialogJDialog容器对话框(模态/非模态)
LabelJLabel标准组件显示文字/图标,不可编辑
ButtonJButton标准组件按钮;JButton 可同时显示文字与图标
TextFieldJTextField / JPasswordField标准组件单行文本输入
TextAreaJTextArea标准组件多行文本显示/输入
CheckBoxJCheckBox / JRadioButton标准组件多选 / 单选
ChoiceJComboBox标准组件下拉选择
ListJList标准组件列表选择

公共属性/方法(Component / JComponent)

  • setSize(w, h) — 设置大小
  • setLocation(x, y) / setBounds(...) — 设置位置(/大小+位置)
  • setFont(Font) — 设置字体(族、风格、大小)
  • setForeground(Color) — 设置前景色(文字/绘制颜色)
  • setBackground(Color) — 设置背景色
  • setVisible(true) — 显示组件/窗口

GUI 设计四步

  1. 定义最外层容器(JFrame)并设定布局
  2. 加入中间容器与标准组件
  3. 设计外观:颜色、字体、对齐、位置
  4. 注册事件响应(如鼠标点击),实现交互

自定义绘制四件套

  • Graphics — 「绘图笔」:drawLine/drawRect/drawOval/drawString/drawImage…
  • Font — 字体(族、风格、大小),配合 drawString
  • Color — 颜色常量或 new Color(r,g,b)
  • Image — 加载图像(getImage)并绘制(drawImage)

四、绘制自定义成分

五、事件处理(事件委托模型)

三要素:事件源(发生事件的组件)、事件(对交互的封装对象)、监听者(实现监听接口、负责处理事件的对象)。生活类比:小朋友摔倒(事件)→ 监护人扶起(处理)——小朋友是事件源,监护人是监听者。

委托模型:事件源用 addActionListener(...) 注册监听器 → 监听器所属类实现监听接口并实现处理方法 → 事件发生时,系统把事件对象交给已注册的监听器处理。以 ActionEvent 为例:Button 被点击 → 系统生成 ActionEvent → 调用监听器的 actionPerformed(e) → 据 e 响应。

交互实验:事件触发链(EventListenerTrace)

模拟「服务大厅」:窗口(事件源)触发"叫号"(事件),监听者响应。点按钮逐步走一遍 ActionEvent 处理链。

1. 用户操作
鼠标点击 JButton —— 交互发生
2. 生成事件对象
系统把这次点击封装为 ActionEvent 对象
3. 查找已注册监听器
事件源按注册表依次调用 addActionListener 登记过的监听器
4. 执行处理方法
监听器的 actionPerformed(ActionEvent e) 被调用,据 e 做出响应

常用事件对照

事件典型事件源监听者接口关键方法
ActionEventButton、菜单项、回车ActionListeneractionPerformed
TextEventTextField、TextAreaTextListenertextValueChanged
ItemEventCheckBox、Choice、ListItemListeneritemStateChanged
MouseEvent组件的鼠标操作MouseListener / MouseMotionListenermouseClicked / mouseDragged…
KeyEvent键盘输入KeyListenerkeyPressed / keyTyped / keyReleased
WindowEvent窗口开关/激活WindowListenerwindowClosing…

服务大厅:监听者与事件源的四种组合

多个窗口(事件源)共用同一个叫号处理器,按 e.getSource() 区分来源 —— 优点:节省代码;代价:逻辑耦合在一个类里。

监听接口方法多(MouseListener 5 个、WindowListener 7 个)时,直接实现必须全部实现;继承系统提供的适配器类(MouseAdapter/KeyAdapter/WindowAdapter…)只需重写关心的方法:frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } });

适配器(Adapter):监听接口方法多时(MouseListener 5 个、WindowListener 7 个),直接实现必须全部实现;继承系统提供的适配器类只需重写关心的方法——配合匿名类,事件代码大幅简化。

六、布局管理器(自适应排列)

显示设备分辨率各不相同,窗口大小也会随时变化——若把每个组件的位置与尺寸写死,换个屏幕立刻错乱。布局管理器根据容器当前大小自动安排组件:

交互实验:布局管理器(LayoutManagerPlayground)

5 个计算器风格按钮放进一个容器,切换布局管理器、拖动宽/高滑杆模拟「窗口大小变化 / 不同分辨率」——组件位置实时重排。

7
8
9
+
=

FlowLayout:按添加顺序从左到右,一行放不下换行,每行水平居中(默认间隙 5)。典型用途:按钮排 / 工具条(如简易计算器)。

七、自绘按钮 vs 库按钮(复习接口与内部类)

两相对比凸显「图形库封装价值」与「事件驱动模型」。

示例(对照课件 16:MyComponentsApp 自绘 vs ComponentsApp 用库)

自绘按钮——自己画、自己判区域,自定义事件对象 + 监听器接口(呼应接口回调):

interface BtnListener { void onBtnClick(BtnEvent e); }   // 自定义监听器接口
class BtnEvent { int x, y; boolean pressed; }            // 自定义事件对象

class MyBtn {                                            // 自己实现「按钮」
    int x, y, w, h; String text; boolean raised = true;
    BtnListener listener;                                 // 注册进来的监听者

    void paint(Graphics g) {                              // 用 Graphics 画按钮
        g.setColor(raised ? Color.BLUE : Color.RED);
        g.drawString(text, x + 25, y + 15);
        g.draw3DRect(x, y, w, h, raised);                 // 凸起/按下靠 raised 切换
    }

    void processMouseEvent(BtnEvent e) {                  // 自己判定鼠标是否落在按钮内
        if (e.x < x || e.x > x + w || e.y < y || e.y > y + h) return;
        raised = !e.pressed;                              // 按下/释放切换状态
        listener.onBtnClick(e);                           // 触发自定义事件
    }
}

窗体实现 MouseListener + BtnListener,把系统鼠标事件转成自定义事件派发给组件:

class MyBtnFrame extends Frame implements MouseListener, BtnListener {
    MyBtn btn = new MyBtn("MyButton1", 100, 100, 110, 20);

    MyBtnFrame() {
        addMouseListener(this);      // 注册系统鼠标监听
        btn.listener = this;         // 注册自定义监听器(也可用内部类/匿名类)
        setVisible(true);
    }

    public void mousePressed(MouseEvent e) {
        BtnEvent be = new BtnEvent();
        be.x = e.getX(); be.y = e.getY(); be.pressed = true;
        btn.processMouseEvent(be);   // 派发自定义事件
        repaint();
    }
    // mouseReleased / mouseEntered ... 略

    public void onBtnClick(BtnEvent e) {                 // 监听器处理自定义事件
        System.out.println("自绘按钮被点击:" + e);
    }
}

库按钮——JButton 自带绘制与状态,直接注册系统监听器即可:

class LibBtnFrame extends JFrame {
    LibBtnFrame() {
        JLabel lbl = new JLabel("未点击");
        JButton btn = new JButton("JButton1");
        btn.addActionListener(e -> lbl.setText("JButton 被点击")); // 系统事件,省去自绘与状态管理
        add(lbl, BorderLayout.NORTH);
        add(btn, BorderLayout.CENTER);
        setSize(400, 200);
        setVisible(true);
    }
}

思考题

  1. Frame/JFrame 与 Panel/JPanel 在容器层次上有何区别?
  2. Component/JComponent 有哪些常用属性与方法?
  3. 自己实现「按钮」与使用库按钮,在事件处理方式上有何本质区别?

学习小测验

学习小测验

Q1

Swing 组件所在的包是( )

Q2

GUI 程序中的事件通常由( )触发

Q3

Swing 组件通常继承自( )体系

Q4

GUI 设计四步的正确顺序是( )

Q5

JButton 相比 AWT Button 的明显优势是( )

Q6

在画布上绘制图形时,充当「绘图笔」的类是( )

Q7

Swing 中处理按钮点击事件的监听器接口是( )

Q8

事件委托模型中,事件源把事件传递给( )

Q9

BorderLayout 的五个区域不包括( )

Q10

容器使用 FlowLayout,窗口变窄时组件会( )