事件与GUI基础
事件与GUI基础
目标:理解 GUI 的三类元素(容器、标准组件、自定义成分);掌握「建容器→加组件→设外观→注册事件」四步设计法;能区分
java.awt与javax.swing的组件层次;知道Graphics/Font/Color/Image各自的角色。
一、GUI 的三类元素
- 容器:组织其他界面元素的单元——外层窗体(
Frame/JFrame)、中间层面板(Panel/JPanel);容器通过布局管理器决定元素排列; - 标准组件:
Label/JLabel、Button/JButton、TextField、TextArea、CheckBox/JRadioButton、Choice/JComboBox、List/JList等; - 用户自定义成分:用
Graphics画线段、矩形、圆、多边形、弧,或加载图像/动画。
二、设计四步法
① 定义最外层容器(JFrame)并设定布局 → ② 加入中间容器与标准组件 → ③ 设计外观(颜色、字体、对齐、位置)→ ④ 注册事件响应,实现交互。
三、AWT 与 Swing
- AWT(
java.awt):顶层类Component,派生容器(Frame/Panel/Dialog)与标准组件(Label/Button/TextField…); - Swing(
javax.swing):组件以 J 打头,功能更强、更美观(如JButton可同时显示文字与图标),是基于 AWT 的轻量级组件——新程序优先使用 Swing。
交互实验:AWT ↔ Swing 组件画廊(SwingGallery)
| AWT(java.awt) | Swing(javax.swing) | 类别 | 用途 |
|---|---|---|---|
| Frame | JFrame | 容器 | 最外层窗体,程序主窗口 |
| Panel | JPanel | 容器 | 中间层面板,用于分组 |
| Dialog | JDialog | 容器 | 对话框(模态/非模态) |
| Label | JLabel | 标准组件 | 显示文字/图标,不可编辑 |
| Button | JButton | 标准组件 | 按钮;JButton 可同时显示文字与图标 |
| TextField | JTextField / JPasswordField | 标准组件 | 单行文本输入 |
| TextArea | JTextArea | 标准组件 | 多行文本显示/输入 |
| CheckBox | JCheckBox / JRadioButton | 标准组件 | 多选 / 单选 |
| Choice | JComboBox | 标准组件 | 下拉选择 |
| List | JList | 标准组件 | 列表选择 |
公共属性/方法(Component / JComponent)
setSize(w, h)— 设置大小setLocation(x, y) / setBounds(...)— 设置位置(/大小+位置)setFont(Font)— 设置字体(族、风格、大小)setForeground(Color)— 设置前景色(文字/绘制颜色)setBackground(Color)— 设置背景色setVisible(true)— 显示组件/窗口
GUI 设计四步
- 定义最外层容器(JFrame)并设定布局
- 加入中间容器与标准组件
- 设计外观:颜色、字体、对齐、位置
- 注册事件响应(如鼠标点击),实现交互
自定义绘制四件套
Graphics— 「绘图笔」:drawLine/drawRect/drawOval/drawString/drawImage…Font— 字体(族、风格、大小),配合 drawStringColor— 颜色常量或 new Color(r,g,b)Image— 加载图像(getImage)并绘制(drawImage)
四、绘制自定义成分
Graphics:所有绘制的「绘图笔」——drawLine/drawRect/drawOval/drawString/drawImage…;- 为什么需要图形库?在像素栅格上画一条理想直线本身就需要专门算法(如 Bresenham 直线算法)——图形库把这类底层细节封装好,开发者只需调
drawLine(); Font:字体(族、风格、大小),配合drawString;Color:常量或new Color(r,g,b);Image:getImage()加载、drawImage()绘制,setIcon()设置图标。
五、事件处理(事件委托模型)
三要素:事件源(发生事件的组件)、事件(对交互的封装对象)、监听者(实现监听接口、负责处理事件的对象)。生活类比:小朋友摔倒(事件)→ 监护人扶起(处理)——小朋友是事件源,监护人是监听者。
委托模型:事件源用 addActionListener(...) 注册监听器 → 监听器所属类实现监听接口并实现处理方法 → 事件发生时,系统把事件对象交给已注册的监听器处理。以 ActionEvent 为例:Button 被点击 → 系统生成 ActionEvent → 调用监听器的 actionPerformed(e) → 据 e 响应。
交互实验:事件触发链(EventListenerTrace)
模拟「服务大厅」:窗口(事件源)触发"叫号"(事件),监听者响应。点按钮逐步走一遍 ActionEvent 处理链。
常用事件对照
| 事件 | 典型事件源 | 监听者接口 | 关键方法 |
|---|---|---|---|
| ActionEvent | Button、菜单项、回车 | ActionListener | actionPerformed |
| TextEvent | TextField、TextArea | TextListener | textValueChanged |
| ItemEvent | CheckBox、Choice、List | ItemListener | itemStateChanged |
| MouseEvent | 组件的鼠标操作 | MouseListener / MouseMotionListener | mouseClicked / mouseDragged… |
| KeyEvent | 键盘输入 | KeyListener | keyPressed / keyTyped / keyReleased |
| WindowEvent | 窗口开关/激活 | WindowListener | windowClosing… |
服务大厅:监听者与事件源的四种组合
多个窗口(事件源)共用同一个叫号处理器,按 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 个),直接实现必须全部实现;继承系统提供的适配器类只需重写关心的方法——配合匿名类,事件代码大幅简化。
六、布局管理器(自适应排列)
显示设备分辨率各不相同,窗口大小也会随时变化——若把每个组件的位置与尺寸写死,换个屏幕立刻错乱。布局管理器根据容器当前大小自动安排组件:
- FlowLayout:按添加顺序从左到右排,一行放不下就换行,每行水平居中;适合按钮排 / 工具条(如简易计算器);
- BorderLayout:
NORTH/SOUTH/EAST/WEST/CENTER五个区域,add(comp, region)指定区域,CENTER 填满剩余空间——JFrame 内容面板的默认布局; - GridLayout(rows, cols):等宽等高的格子,按行优先顺序依次摆放;适合计算器键盘 / 表单网格;
- 嵌套组合:外层 BorderLayout + 内层
Panel各自设布局(如计算器:显示框在 NORTH、4×4 键位在 CENTER)——复杂界面就是这样一层层拼出来的。
交互实验:布局管理器(LayoutManagerPlayground)
5 个计算器风格按钮放进一个容器,切换布局管理器、拖动宽/高滑杆模拟「窗口大小变化 / 不同分辨率」——组件位置实时重排。
FlowLayout:按添加顺序从左到右,一行放不下换行,每行水平居中(默认间隙 5)。典型用途:按钮排 / 工具条(如简易计算器)。
七、自绘按钮 vs 库按钮(复习接口与内部类)
- 自己实现按钮:用
Graphics画一个「按钮」矩形,自己维护鼠标区域判定与按下/释放状态,定义自定义事件 + 监听器接口(呼应接口回调),用内部类/匿名类实现监听者; - 使用库按钮:改用
Button/JButton,直接addActionListener(...)注册系统事件——省去自绘与状态管理。
两相对比凸显「图形库封装价值」与「事件驱动模型」。
示例(对照课件 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);
}
}
思考题
- Frame/JFrame 与 Panel/JPanel 在容器层次上有何区别?
Component/JComponent有哪些常用属性与方法?- 自己实现「按钮」与使用库按钮,在事件处理方式上有何本质区别?
学习小测验
学习小测验
Swing 组件所在的包是( )
GUI 程序中的事件通常由( )触发
Swing 组件通常继承自( )体系
GUI 设计四步的正确顺序是( )
JButton 相比 AWT Button 的明显优势是( )
在画布上绘制图形时,充当「绘图笔」的类是( )
Swing 中处理按钮点击事件的监听器接口是( )
事件委托模型中,事件源把事件传递给( )
BorderLayout 的五个区域不包括( )
容器使用 FlowLayout,窗口变窄时组件会( )