Java Swing编程入门指南
1. 布局管理器
在编写Swing程序之前,需要了解布局管理器。布局管理器控制容器内组件的位置。Java提供了多种布局管理器,大部分由AWT(在
java.awt
包中)提供,Swing也添加了一些自己的布局管理器。所有布局管理器都是实现了
LayoutManager
接口的类的实例(有些还实现了
LayoutManager2
接口)。以下是Swing程序员可用的一些布局管理器:
| 布局管理器 | 描述 |
| — | — |
|
FlowLayout
| 简单布局,组件从左到右、从上到下排列(在某些文化设置中从右到左排列)。 |
|
BorderLayout
| 将组件放置在容器的中心或边界。这是内容面板的默认布局。 |
|
GridLayout
| 在网格中布局组件。 |
|
GridBagLayout
| 在灵活的网格中布局不同大小的组件。 |
|
BoxLayout
| 在盒子中垂直或水平布局组件。 |
|
SpringLayout
| 根据一组约束条件布局组件。 |
1.1 BorderLayout和FlowLayout
BorderLayout
是内容面板的默认布局管理器,它定义了五个可以添加组件的位置:中心、北、南、东、西。默认情况下,向内容面板添加组件时,组件会被添加到中心位置。若要将组件添加到其他区域,需要指定其名称。
graph LR
A(内容面板) --> B(中心)
A --> C(北)
A --> D(南)
A --> E(东)
A --> F(西)
FlowLayout
是一种更灵活的布局管理器,它一次布局一行组件,从上到下。当一行满了,就会进入下一行。虽然这种布局方式对组件的放置控制较少,但使用起来非常简单。不过,调整窗口大小时,组件的位置会改变。
2. 第一个简单的Swing程序
Swing程序与之前的基于控制台的程序不同,也与基于AWT的小程序不同。Swing程序不仅使用Swing组件集来处理用户交互,还对线程有特殊要求。理解Swing程序结构的最佳方法是通过一个示例。以下是一个简单的Swing程序:
// A simple Swing program.
import javax.swing.*;
class SwingDemo {
SwingDemo() {
// Create a new JFrame container.
JFrame jfrm = new JFrame("A Simple Swing Application");
// Give the frame an initial size.
jfrm.setSize(275, 100);
// Terminate the program when the user closes the application.
jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create a text-based label.
JLabel jlab = new JLabel(" Swing defines the modern Java GUI.");
// Add the label to the content pane.
jfrm.add(jlab);
// Display the frame.
jfrm.setVisible(true);
}
public static void main(String args[]) {
// Create the frame on the event dispatching thread.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new SwingDemo();
}
});
}
}
2.1 程序编译和运行
编译该程序使用以下命令:
javac SwingDemo.java
运行程序使用以下命令:
java SwingDemo
2.2 代码逐行解析
- 导入包 :
import javax.swing.*;
javax.swing
包包含了Swing定义的组件和模型,所有使用Swing的程序都需要包含这个包。
2.
创建
JFrame
:
JFrame jfrm = new JFrame("A Simple Swing Application.");
创建一个带有标题栏、关闭、最小化、最大化和恢复按钮以及系统菜单的标准顶级窗口。
3.
设置窗口大小
:
jfrm.setSize(275, 100);
setSize()
方法以像素为单位设置窗口的尺寸。
4.
设置默认关闭操作
:
jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
关闭窗口时,整个应用程序将终止。
setDefaultCloseOperation()
方法的通用形式为:
void setDefaultCloseOperation(int what)
what
参数决定了窗口关闭时的行为,除了
JFrame.EXIT_ON_CLOSE
,还有以下选项:
| 常量 | 描述 |
| — | — |
|
JFrame.DISPOSE_ON_CLOSE
| 释放窗口资源 |
|
JFrame.HIDE_ON_CLOSE
| 隐藏窗口 |
|
JFrame.DO_NOTHING_ON_CLOSE
| 不做任何操作 |
5.
创建
JLabel
:
JLabel jlab = new JLabel(" Swing defines the modern Java GUI.");
JLabel
是最简单的Swing组件,它不接受用户输入,只是显示信息。
6.
将标签添加到内容面板
:
jfrm.add(jlab);
默认情况下,内容面板使用
BorderLayout
布局,此版本的
add()
方法将组件添加到中心位置。
7.
显示窗口
:
jfrm.setVisible(true);
setVisible()
方法的通用形式为:
void setVisible(boolean flag)
如果
flag
为
true
,窗口将显示;否则,窗口将隐藏。默认情况下,
JFrame
是不可见的,所以需要调用
setVisible(true)
来显示它。
8.
在事件调度线程上创建
SwingDemo
对象
:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new SwingDemo();
}
});
Swing程序是事件驱动的,为了避免多线程更新组件时出现问题,所有Swing GUI组件必须在事件调度线程上创建和更新。
SwingUtilities
类提供了
invokeLater()
和
invokeAndWait()
方法来实现这一点。
3. 使用JButton
JButton
是最常用的Swing控件之一,它继承自抽象类
AbstractButton
,定义了所有按钮的通用功能。Swing按钮可以包含文本、图像或两者都有,这里只使用基于文本的按钮。
3.1 创建JButton
JButton
提供了多个构造函数,这里使用的是:
JButton(String msg)
msg
指定了按钮内部显示的字符串。
3.2 处理按钮事件
当按下按钮时,会生成一个
ActionEvent
。
JButton
提供了以下方法来添加或移除动作监听器:
void addActionListener(ActionListener al)
void removeActionListener(ActionListener al)
al
指定了一个接收事件通知的对象,该对象必须是实现了
ActionListener
接口的类的实例。
ActionListener
接口只定义了一个方法:
void actionPerformed(ActionEvent ae)
当按钮被按下时,会调用这个方法。可以使用传递给
actionPerformed()
方法的
ActionEvent
对象来获取与按钮按下事件相关的有用信息,例如通过调用
getActionCommand()
方法获取与按钮关联的动作命令字符串。
3.3 示例程序
// Demonstrate a push button and handle action events.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class ButtonDemo implements ActionListener {
JLabel jlab;
ButtonDemo() {
// Create a new JFrame container.
JFrame jfrm = new JFrame("A Button Example");
// Specify FlowLayout for the layout manager.
jfrm.setLayout(new FlowLayout());
// Give the frame an initial size.
jfrm.setSize(220, 90);
// Terminate the program when the user closes the application.
jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Make two buttons.
JButton jbtnUp = new JButton("Up");
JButton jbtnDown = new JButton("Down");
// Add action listeners.
jbtnUp.addActionListener(this);
jbtnDown.addActionListener(this);
// Add the buttons to the content pane.
jfrm.add(jbtnUp);
jfrm.add(jbtnDown);
// Create a label.
jlab = new JLabel("Press a button.");
// Add the label to the frame.
jfrm.add(jlab);
// Display the frame.
jfrm.setVisible(true);
}
// Handle button events.
public void actionPerformed(ActionEvent ae) {
if(ae.getActionCommand().equals("Up"))
jlab.setText("You pressed Up.");
else
jlab.setText("You pressed down. ");
}
public static void main(String args[]) {
// Create the frame on the event dispatching thread.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ButtonDemo();
}
});
}
}
3.4 程序解析
- 导入包 :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
java.awt
包包含了
FlowLayout
类,
java.awt.event
包定义了
ActionListener
接口和
ActionEvent
类。
2.
实现
ActionListener
接口
:
class ButtonDemo implements ActionListener {
ButtonDemo
类实现了
ActionListener
接口,意味着
ButtonDemo
对象可以接收动作事件。
3.
设置布局管理器
:
jfrm.setLayout(new FlowLayout());
将内容面板的布局管理器设置为
FlowLayout
。
4.
创建按钮并添加监听器
:
JButton jbtnUp = new JButton("Up");
JButton jbtnDown = new JButton("Down");
jbtnUp.addActionListener(this);
jbtnDown.addActionListener(this);
创建两个按钮并为它们添加动作监听器。
5.
处理按钮事件
:
public void actionPerformed(ActionEvent ae) {
if(ae.getActionCommand().equals("Up"))
jlab.setText("You pressed Up.");
else
jlab.setText("You pressed down. ");
}
根据按钮的动作命令字符串来更新标签的文本。
4. 布局管理器的深入探讨
4.1 布局管理器的选择
在开发Swing应用程序时,选择合适的布局管理器至关重要。不同的布局管理器适用于不同的场景,以下是一些常见布局管理器的适用场景总结:
| 布局管理器 | 适用场景 |
| — | — |
|
FlowLayout
| 当组件的排列顺序较为简单,且不需要精确控制组件位置时,如简单的表单输入界面。 |
|
BorderLayout
| 适用于将界面划分为中心和四个边界区域的场景,如主窗口包含顶部菜单栏、底部状态栏和中心内容区域。 |
|
GridLayout
| 当需要将组件均匀分布在网格中时,如计算器的按钮布局。 |
|
GridBagLayout
| 对于需要灵活布局不同大小组件的复杂界面,如大型数据录入表单。 |
|
BoxLayout
| 用于垂直或水平排列组件,如导航栏或侧边栏。 |
|
SpringLayout
| 当需要根据约束条件动态调整组件位置时,如自适应窗口大小的界面。 |
4.2 布局管理器的使用示例
以下是使用
GridLayout
布局管理器的示例代码:
import javax.swing.*;
import java.awt.*;
class GridLayoutExample {
GridLayoutExample() {
JFrame jfrm = new JFrame("GridLayout Example");
jfrm.setSize(300, 300);
jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 设置布局管理器为GridLayout,2行3列
jfrm.setLayout(new GridLayout(2, 3));
// 添加组件
for (int i = 0; i < 6; i++) {
JButton button = new JButton("Button " + (i + 1));
jfrm.add(button);
}
jfrm.setVisible(true);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new GridLayoutExample();
}
});
}
}
在这个示例中,我们创建了一个
JFrame
并将其布局管理器设置为
GridLayout
,指定了2行3列的网格。然后添加了6个按钮,这些按钮会均匀分布在网格中。
4.3 布局管理器的嵌套使用
在实际开发中,可能需要嵌套使用不同的布局管理器来实现复杂的界面布局。例如,我们可以在一个使用
BorderLayout
的主窗口中,在中心区域使用
GridLayout
来布局子组件。以下是一个简单的嵌套布局示例:
graph LR
A(JFrame - BorderLayout) --> B(北 - JLabel)
A --> C(中心 - JPanel - GridLayout)
C --> D(按钮1)
C --> E(按钮2)
C --> F(按钮3)
C --> G(按钮4)
A --> H(南 - JLabel)
import javax.swing.*;
import java.awt.*;
class NestedLayoutExample {
NestedLayoutExample() {
JFrame jfrm = new JFrame("Nested Layout Example");
jfrm.setSize(400, 400);
jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 设置主窗口布局为BorderLayout
jfrm.setLayout(new BorderLayout());
// 北区域添加标签
JLabel northLabel = new JLabel("North Label");
jfrm.add(northLabel, BorderLayout.NORTH);
// 中心区域添加JPanel并设置为GridLayout
JPanel centerPanel = new JPanel(new GridLayout(2, 2));
for (int i = 0; i < 4; i++) {
JButton button = new JButton("Button " + (i + 1));
centerPanel.add(button);
}
jfrm.add(centerPanel, BorderLayout.CENTER);
// 南区域添加标签
JLabel southLabel = new JLabel("South Label");
jfrm.add(southLabel, BorderLayout.SOUTH);
jfrm.setVisible(true);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new NestedLayoutExample();
}
});
}
}
在这个示例中,我们创建了一个使用
BorderLayout
的主窗口,在北和南区域分别添加了标签,在中心区域添加了一个使用
GridLayout
的
JPanel
,并在
JPanel
中添加了4个按钮。
5. 事件处理的扩展
5.1 多事件监听器的使用
在实际应用中,一个组件可能需要处理多种类型的事件。例如,一个按钮不仅需要处理点击事件,还可能需要处理鼠标悬停事件。以下是一个示例代码:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
class MultiEventListenerExample {
MultiEventListenerExample() {
JFrame jfrm = new JFrame("Multi Event Listener Example");
jfrm.setSize(300, 200);
jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Click Me");
// 添加动作监听器
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(jfrm, "Button Clicked!");
}
});
// 添加鼠标监听器
button.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
button.setBackground(Color.YELLOW);
}
@Override
public void mouseExited(MouseEvent e) {
button.setBackground(null);
}
});
jfrm.add(button);
jfrm.setVisible(true);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MultiEventListenerExample();
}
});
}
}
在这个示例中,我们为按钮添加了
ActionListener
来处理点击事件,当按钮被点击时会弹出一个消息框。同时,我们还添加了
MouseAdapter
来处理鼠标悬停事件,当鼠标进入按钮区域时,按钮背景颜色变为黄色,鼠标离开时恢复默认颜色。
5.2 事件处理的线程安全
在Swing程序中,事件处理必须保证线程安全。由于Swing组件不是线程安全的,所有对Swing组件的更新操作都应该在事件调度线程上执行。除了使用
SwingUtilities.invokeLater()
和
SwingUtilities.invokeAndWait()
方法外,还可以使用
SwingWorker
类来执行耗时任务,避免阻塞事件调度线程。以下是一个使用
SwingWorker
的示例:
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class SwingWorkerExample {
SwingWorkerExample() {
JFrame jfrm = new JFrame("SwingWorker Example");
jfrm.setSize(300, 200);
jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Start Task");
JLabel label = new JLabel("Task Not Started");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 创建并执行SwingWorker任务
new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
// 模拟耗时任务
Thread.sleep(3000);
return null;
}
@Override
protected void done() {
// 在事件调度线程上更新组件
label.setText("Task Completed");
}
}.execute();
}
});
jfrm.add(button, BorderLayout.NORTH);
jfrm.add(label, BorderLayout.CENTER);
jfrm.setVisible(true);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new SwingWorkerExample();
}
});
}
}
在这个示例中,当点击按钮时,会启动一个
SwingWorker
任务,该任务在后台线程中执行耗时操作(模拟3秒的延迟)。任务完成后,会在事件调度线程上更新标签的文本。
6. 总结
通过本文的介绍,我们了解了Swing编程的基础知识,包括布局管理器的使用、简单Swing程序的创建、按钮事件处理以及事件处理的扩展。在实际开发中,我们可以根据具体需求选择合适的布局管理器和事件处理方式,同时要注意线程安全问题,确保程序的稳定性和性能。希望这些内容能帮助你快速入门Swing编程,开发出功能丰富、界面美观的Java应用程序。
超级会员免费看
105

被折叠的 条评论
为什么被折叠?



