Swing简介
-
不同于 AWT 使用重量级组件(每个组件对应有一个本地窗口系统资源),Swing 提供了轻量级组件,它们不是直接映射到本地窗口系统组件上,而是绘制出来的。因此,Swing 组件可以在不同的平台上保持一致的外观和感觉,并且更加灵活。
-
JComponent:Swing 中的所有组件都继承自
JComponent
类,而JComponent
又间接继承自 AWT 的Container
类。这意味着 Swing 可以利用 AWT 的一些基础设施,比如事件模型和布局管理器。 -
增强的功能:Swing 在 AWT 的基础上增加了许多新的功能和组件,如表格(
JTable
)、树(JTree
)、选项卡面板(JTabbedPane
)等高级组件。 -
事件处理模型:Swing 继承并扩展了 AWT 的事件处理模型。虽然两者使用相似的监听器接口来处理用户交互,但 Swing 引入了一些额外的功能,例如更精细的事件类型和支持键盘导航等特性。
一、Box和BoxLayout详解
Box
和BoxLayout
是Swing提供的两种与布局相关的类,它们提供了一种简单的方法来创建线性布局(水平或垂直方向)。
-
BoxLayout:是一个布局管理器,它可以沿容器的水平轴(X轴)或垂直轴(Y轴)布置组件。通过设置不同的轴向,你可以创建出水平或垂直排列的组件布局。
-
Box:是一个特殊的容器,它使用
BoxLayout
作为其布局管理器。Box
类提供了两个静态方法来创建水平或垂直盒子:Box.createHorizontalBox()
:创建一个水平盒子,即内部组件沿水平方向排列。Box.createVerticalBox()
:创建一个垂直盒子,即内部组件沿垂直方向排列。
Box
类还提供了一些方便的方法来添加具有一定功能的不可见组件,比如用于增加间隔的胶水(createGlue()
)和刚性区域(createRigidArea(Dimension d)
),这有助于调整布局中的空白部分和组件之间的距离。
使用BoxLayout
时,可以通过调用容器对象的setLayout(new BoxLayout(container, axis))
方法来设置布局,其中axis
参数应为BoxLayout.X_AXIS
(水平布局)或BoxLayout.Y_AXIS
(垂直布局)。
案例:
import javax.swing.*;
import java.awt.*;
public class BoxLayoutExample {
public static void main(String[] args) {
// 创建并设置主窗口
JFrame frame = new JFrame("Box Layout Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 200);
// 创建一个垂直Box容器
Box verticalBox = Box.createVerticalBox();
// 添加第一个标签
JLabel label1 = new JLabel("这是第一个标签:");
verticalBox.add(label1);
// 添加一个文本框,并在其前后添加一些垂直胶水以增加间隔
JTextField textField = new JTextField();
verticalBox.add(Box.createVerticalStrut(5)); // 在组件前添加间隔
verticalBox.add(textField);
verticalBox.add(Box.createVerticalStrut(10)); // 在组件后添加更大的间隔
// 添加第二个标签
JLabel label2 = new JLabel("这是第二个标签:");
verticalBox.add(label2);
// 添加一个按钮,并在其前后添加一些垂直胶水以增加间隔
JButton button = new JButton("点击我");
verticalBox.add(Box.createVerticalStrut(5));
verticalBox.add(button);
verticalBox.add(Box.createVerticalStrut(10));
// 将垂直Box容器添加到主窗口
frame.add(verticalBox, BorderLayout.CENTER);
// 显示窗口
frame.setVisible(true);
}
}
在 Java 的 GUI 编程中,事件处理机制是实现用户交互的核心部分。AWT(Abstract Window Toolkit)和 Swing 它们都使用基于 事件监听器模型(Event Listener Model) 来处理用户操作,比如点击按钮、输入文本、鼠标移动等。
二、Java GUI 事件处理的基本概念
1. 事件源(Event Source)
指的是产生事件的对象,例如一个按钮(JButton
)、文本框(JTextField
)或窗口(JFrame
)。
2. 事件对象(Event Object)
当事件发生时,系统会创建一个事件对象,其中包含了事件的相关信息。例如:
ActionEvent
:表示动作事件(如按钮被点击)MouseEvent
:表示鼠标事件KeyEvent
:表示键盘事件WindowEvent
:表示窗口事件(打开、关闭等)
3. 监听器接口(Listener Interface)
监听器是一个实现了特定接口的对象,用于接收并处理事件。每种事件类型都有对应的监听器接口,例如:
ActionListener
:用于响应动作事件MouseListener
:用于响应鼠标事件KeyListener
:用于响应键盘事件WindowListener
:用于响应窗口事件
4. 注册监听器
通过调用事件源的方法将监听器注册到事件源上,例如:
button.addActionListener(myActionListener);
三、常见的事件类型和监听器接口
事件类型 | 对应的监听器接口 | 常见方法示例 |
---|---|---|
ActionEvent | ActionListener | actionPerformed() |
MouseEvent | MouseListener | mouseClicked(), mousePressed(), mouseReleased(), mouseEntered(), mouseExited() |
KeyEvent | KeyListener | keyPressed(), keyReleased(), keyTyped() |
WindowEvent | WindowListener | windowClosing(), windowOpened(), windowClosed() 等 |
ItemEvent | ItemListener | itemStateChanged() |
TextEvent | TextListener | textValueChanged() |
四、事件处理的几种方式
方法一:匿名内部类(最常用)
JButton button = new JButton("Click me");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("按钮被点击了!");
}
});
方法二:Lambda 表达式(Java 8+ 推荐)
button.addActionListener(e -> {
System.out.println("按钮被点击了!");
});
方法三:实现监听器接口的类
适用于多个组件共享同一个监听器逻辑的情况。
public class MyActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("事件触发:" + e.getActionCommand());
}
}
// 使用
button.addActionListener(new MyActionListener());
方法四:适配器类(Adapter Class)
对于有多个方法的监听器接口(如 MouseListener
),可以继承其适配器类,只重写感兴趣的方法。
panel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("鼠标点击位置:" + e.getX() + ", " + e.getY());
}
});
五、常见控件与事件绑定示例
1. JButton 与 ActionEvent
JButton button = new JButton("提交");
button.addActionListener(e -> JOptionPane.showMessageDialog(null, "提交成功!"));
2. JTextField 与 KeyEvent
textField.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
System.out.println("你输入的是:" + textField.getText());
}
});
3. JCheckBox 与 ItemEvent
checkBox.addItemListener(e -> {
if (e.getStateChange() == ItemEvent.SELECTED) {
System.out.println("复选框被选中");
} else {
System.out.println("复选框被取消选中");
}
});
4. JFrame 与 WindowEvent
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.out.println("窗口正在关闭...");
System.exit(0);
}
});
六、控件和常用事件对应的监听接口
组件 | 常用事件 | 监听器接口 |
---|---|---|
JButton | ActionEvent | ActionListener |
JMenuItem | ActionEvent | ActionListener |
JTextField | ActionEvent/KeyEvent | ActionListener/KeyListener |
JCheckBox | ItemEvent | ItemListener |
JFrame | WindowEvent | WindowListener |
JPanel | MouseEvent | MouseListener |
JScrollPane | AdjustmentEvent | AdjustmentListener |
以下是一些 Swing 独有(或主要用于 Swing) 的事件监听接口及其用途:
事件监听接口 | 监听对象 | 主要作用 |
---|---|---|
ListSelectionListener | JList , ListSelectionModel | 监听列表中选中项的变化 |
TreeSelectionListener | JTree | 监听树中节点的选择变化 |
TableColumnModelListener | JTable 的列模型 | 监听表格列的变化(如宽度调整、移动等) |
TableModelListener | JTable 的数据模型 | 表格数据发生变化时触发 |
ChangeListener | JSlider , JSpinner , JTabbedPane , ButtonGroup 等 | 值或状态发生变化时触发 |
DocumentListener | JTextField , JTextArea 的 Document 对象 | 文本内容发生插入、删除、更改时触发 |
CaretListener | JTextComponent 子类(如 JTextField , JTextArea ) | 监听文本光标位置变化 |
MenuListener | JMenu | 菜单打开/关闭/取消选择时触发 |
HyperlinkListener | JEditorPane , JTextPane | 监听超链接点击事件 |
InternalFrameListener | JInternalFrame | 内部窗口创建、激活、关闭等事件 |
例子:
import javax.swing.*;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
public class CaretListenerExample {
public static void main(String[] args) {
// 创建主窗口
JFrame frame = new JFrame("CaretListener 示例");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 300);
frame.setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));
// 创建一个文本框
JTextField textField = new JTextField(20);
// 创建一个文本区域
JTextArea textArea = new JTextArea(5, 30);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
// 创建一个标签用于显示光标位置
JLabel caretPositionLabel = new JLabel("光标位置:未开始");
// 添加 CaretListener 到文本框
textField.addCaretListener(new CaretListener() {
@Override
public void caretUpdate(CaretEvent e) {
int pos = textField.getCaretPosition();
caretPositionLabel.setText("文本框中光标位置:" + pos);
}
});
// 添加 CaretListener 到文本区域
textArea.addCaretListener(new CaretListener() {
@Override
public void caretUpdate(CaretEvent e) {
int pos = textArea.getCaretPosition();
caretPositionLabel.setText("文本区域中光标位置:" + pos);
}
});
// 添加组件到窗口中
frame.add(new JLabel("文本框:"));
frame.add(textField);
frame.add(Box.createVerticalStrut(20)); // 添加垂直间距
frame.add(new JLabel("文本区域:"));
frame.add(new JScrollPane(textArea));
frame.add(caretPositionLabel);
// 显示窗口
frame.setLocationRelativeTo(null); // 居中显示
frame.setVisible(true);
}
}