Swing
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Swing(曹雯君)
Swing是一个为Java设计的图形用户界面(GUI)工具包。
Swing是Java API的一部分。
Swing包括了GUI的元器件,如:文本框,按钮,分隔窗格和表。
Swing用于提供一组“轻量级”(全部是 Java 语言)组件,它们用纯Java写成,所以同样可以跨平台使用。
轻量级元件的缺点则是执行速度较慢,优点就是可以在所有平台上采用统一的行为。
1. 30分钟快速上手
1.1 Swing和AWT的关系与区别
•关系
Swing是一个用于开发JAVA应用程序用户界面的开发工具包。
以抽象窗口包(AWT)为基础使跨平台应用程序可以使用任何可插拔的外观风格。
Swing API的大部分是AWT的补充扩展而不是直接的替代。
Swing用来绘制轻量级组件的核心渲染功能是由Java 2D提供的,这是AWT的一部分。
然而,轻量级和重量级组件在同一个应用中使用会导致Z-order不兼容。
•区别
Swing为基于窗体的GUI应用开发设计,为Java跨平台特性提供了卓越的支持,它完全没有本地代码,不受操作系统的影响,做到了真正的跨平台应用,甚至能够提供本地窗口系统不支持的其他特性。
因此比AWT具有更强的实用性,同时比AWT程序拥有更加精致的外观感受。
AWT只提供基本的组件,使很多设计变得复杂,且无法在不同的平台下保持显示风格的一致性。
例如:如果建立一个按钮(Button)对象,就会有一个按钮对象会请求底层操作系统创建一个真正的按钮。
即在WindowsNT上执行那么创建的就是WindowsNT按钮;在Linux 上执行,那么创建的就是Linux按钮。
因此AWT组件外观会受到底层操作系统的影响。
1.2 Swing操作步骤
1.2.1 导入Swing包
import javax.swing.*;
大部分的Swing程序用到AWT的基础底层结构和事件模型,因此需要导入两个包:import java.awt.*;
import java.awt.event.*;
如果图形界面中包括了事件处理,那么还需要导入事件处理包:
import.javax.swing.event.*;
1.2.2 选择界面风格
Swing允许选择程序的图形界面风格常用的有Java风格,Windows风格等。
下面的代码用于选择图形界面风格,这里选择的是跨平台的Java界面风格。
try{
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassNa me());
}catch(Exception e){
}
1.2.3 设置顶层容器
图形界面至少要有一个顶层Swing容器(详见:2.1 容器)。
顶层Swing容器为其他Swi ng组件在屏幕上绘制和处理事件提供支持。
例如:一个框架(JFrame)包括边界、菜单栏、工具栏、状态栏。
以及中间占主要部分的窗格。
窗格也可以看作是一种面板,但它是框架的一个组成部分。
组件不会直接放到框架上,而是放在若干面板(JPanel)上,这些面板再放到窗格上。
用框架对象的getContentPane()函数来获得窗格,在调用窗格的add()函数放置面板。
// 声明一个名为SwingApplication的框架
JFrame frame = new JFrame("SwingApplication");
// 声明一个空的面板
JPanel panel = new JPanel();
// 将面板添加到指定的框架中,定义添加面板的布局方式
frame.getContentPane().add(panel, BorderLayout.Center);
...
// 调整此窗口的大小,以适合其子组件的首选大小和布局。
frame.pack();
// 设置声明框架可见,否则框架不显示
frame.setVisible(true);
1.2.3 添加组件
组件(详见:2.2 常用组件)添加需要容器的支持,添加方式与面板添加的方式相类似,面板也有用于添加元件或子面板的add()函数。
// 声明一个按钮组件
JButton button = new JButton("button");
// 将按钮组件添加到之前声明的面板上,定义添加组件在面板上的布局方式
panel.add(button, BorderLayout.Center);
1.2.4 为组件添加事件
组件通过添加事件来实现用户与系统之间的交互,通过监听键盘键入事件和鼠标操作事件来进行相应的处理。
// 添加指定的操作监听器,以接收发自此组件的操作事件。
button.addActionListener(...);
// 添加指定的鼠标侦听器,以接收发自此组件的鼠标事件。
button.addMouseListener(...);
// 添加指定的鼠标移动侦听器,以接收发自此组件的鼠标移动事件。
button.addMouseMotionListener(...);
// 添加指定的鼠标滚轮侦听器,以接收发自此组件的鼠标滚轮事件。
容器还接收发自子
组件的鼠标滚轮事件。
button.addMouseWheelListener(...);
// 添加指定的按键侦听器,以接收发自此组件的按键事件。
button.addKeyListener(...);
1.3 Swing 的线程策略
通常 Swing 不是线程安全的。
除非另行说明,否则所有 Swing 组件及相关类都必须在
事件调度线程上访问。
典型的 Swing 应用程序执行处理以响应用户动作所生成的事件。
例如,单击 JButton
通知所有添加到JButton 的 ActionListener。
由于用户动作所生成的所有事件都在调
度线程上指派,所以大部分开发人员不受该限制的影响。
但是,影响存在于构造以及显示 Swing 的应用程序中。
对应用程序的 main 方法或 Ap plet 中方法的调用不在事件调度线程上调用。
因此,构造和显示应用程序或 applet 时,必须注意要将控件转移到事件调度线程。
转移控件和开始处理 Swing 的首选方法是使用 invokeLater。
invokeLater 方法安排Runnable 在事件调度线程上处理。
以下两个示例都同样很好地用于转移控件和启动 Swing 应用程序:
public class MyApp implements Runnable {
public void run() {
// Invoked on the event dispatching thread.
// Construct and show GUI.
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new MyApp(args));
}
}
public class MyApp {
MyApp(String[] args) {
// Invoked on the event dispatching thread. Do any initializati on
// here.
}
public void show() {
// Show the UI.
}
public static void main(final String[] args) {
// Schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MyApp(args).show();
}
});
}
}
2. 技术点说明
2.1 容器
Swing中一些组件继承JComponent类,JComponent类继承于Container类,所以凡是继承此类的组件都可以作为容器使用。
容器从功能上可以分为:
1) 顶层容器:
JFrame——框架,主要用来设计应用程序的图像界面。
JApplet——小应用程序,主要用来设计嵌入式网页中运行的JAVA程序。
JDialog——对话框,通常用来设计具有依赖关系的窗口。
JWindow——窗口。
2) 普通容器:
JPanel——面板,通常只有背景颜色的普通容器。
JScrollPane——滚动窗格,具有滚动条。
JToolBar——工具条,通常将多个组件排成一排或一列。
JSplitPane——分裂窗格,用来装两个组件的容器。
JTabbedPane——选项卡窗格,允许多个组件共享相同的界面空间。
3) 专用容器:
JInternalFrame——内部窗格,可以在一个窗口内显示若干个类似于框架的窗口。
JLayeredPanel——分层窗格,给窗格增加了深度的概念。
JRootPane——根窗格,一般是自动创建的容器。
2.2 常用组件
控件从功能上可以分为:
1) 基本控件,实现人机交互的组件:
JButton——按钮控件
JComboBox——下拉选择框
JList——列表控件
JMenu——菜单控件
JSlider——滚动条控件
JTextField——文本控件,是一个轻量级组件,它允许编辑单行文本。
2) 不可编辑信息的显示控件,向用户显示不可编辑信息的控件:
JLabel——标签,用于短文本字符串或图像或二者的显示区。
JProgressBar——以可视化形式显示某些任务进度的组件。
JToolTip——用来显示 Component 的“提示”。
3) 可编辑信息的显示控件,向用户显示可编辑的格式化信息的控件:JColorChooser——颜色选取器,提供一个用于允许用户操作和选择颜色的控制器窗格。
JFileChooser——文件选择器,为用户选择文件提供了一种简单的机制。
JTable——用来显示和编辑常规二维单元表。
有很多用来自定义其呈现和编辑的工具,同时提供了这些功能的默认设置,从而可以轻松地设置简单表。
JTextArea——显示纯文本的多行区域。
JTree——将分层数据集显示为轮廓的控件。
有关面向任务的文档和使用树的示例。
2.3 布局
2.3.1 Swing布局管理器介绍
提供布局逻辑(一句不同的布局管理器和UI内容)
public staic void addComponentsToPane(Container pane){...}
实例化一个容器,通过它的ContentPane加载布局逻辑内容
private static void createAndShowGUI(){
JFrame frame = new JFrame("...");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); addComponentsToPane(frame.getContentPane());
frame.pack();
frame.setVisible(true);
}
main()程序入口,单独起一个线程,实例化UI。
public static void main(String[] args){
javax.swing.SwingUtilities,invokeLater(new Runnable(){
public void run(){
createAndShowGUI();
}
});
}
2.3.2 常用的布局管理器
常用的布局管理器有:FlowLayout、BorderLayout、BoxLayout、CardLayout、Grid Layout和GridBagLayout。
1) FlowLayout
FlowLayout类是最简单的布局管理器。
按照:从左到右,直到没有多余的空间,然后,转到下一行。
public static void addComponentsToPane(Container pane){
pane.setLayout(new FlowLayout());
pane.add(new JButton("button1"));
pane.add(new JButton("button2"));
...
}
2) BorderLayout
BorderLayout将界面分成上下左右中五大区域:PAGE_START、PAGE_END、LINE_START、LINE_END、CENTER。
public static void addComponentsToPane(Container pane) {
JButton button = new JButton("button1");
pane.add(button, BorderLayout.PAGE_START);
button = new JButton("button2");
button.setPreferredSize(new Dimension(200, 100));
pane.add(button, BorderLayout.CENTER)
}
3) BoxLayout
BoxLayout可以将组件由上至下或由左至右依次加入当前面板。
public static void addComponentsToPane(Container pane) {
JPanel xPanel = new JPanel();
xPanel.setLayout(new BoxLayout(xPanel, BoxLayout.X_AXIS));
xPanel.add(new JButton("button1"));
xPanel.add(new JButton("button2"));
pane.add(xPanel, BorderLayout.PAGE_START);
}
4) CardLayout
卡片布局和其他布局不同,它隐藏了一些组件。
卡片布局就是一组容器或组件,它们一次
仅仅显示一个,组中每一个容器称作卡片。
public static void addComponentsToPane(Container pane){
final JPanel contentPanel = new JPanel();
JPanel controlPanel = new JPanel();
final CardLayout CardLayout cardLayout=new CardLayout();
pane.add(contentPanel, BorderLayout.CENTER);
pane.add(controlPanel, BorderLayout.PAGE_END);
controlPanel.setLayout(new FlowLayout());
JButton[] b = new JButton[10];
for (int i = 0; i < 10; i++) {
b[i] = new JButton("No" + i);
contentPanel.add(b[i]);
}
contentPanel.setLayout(cardLayout);
JButton nextButton = new JButton("next");
nextButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
cardLayout.next(contentPanel);
}
});
}
5) GridLayout
GridLayout建立一个组件表格,并且当组件加入时,会一次从左到右,从上到下填充每个格子,并不能指定填充某一个格子。
public static void addComponentsToPane(Container pane){
JButton[] b = new JButton[10];
pane.setLayout(new GridLayout(3, 3));
for (int i = 0; i < b.length; i++) {
b[i] = new JButton("No" + i);
pane.add(b[i]);
}
}
6) GridBagLayout
GridBagLayout是所有AWT布局中最复杂的,同时也是最强大的。
GridBagLayout同G ridLayout一样,在容器中以网格的形式来管理组件。
但GridBagLayout功能要强大得多。
1. GridBagLayout管理的所由有行和列可以大小不同。
2. GridLayout把每个组件限制在一个单元格。
而GridBagLayout中的组件可以占据任意大小的矩形区域。
public static void addComponentsToPane(Container pane){
JButton button;
pane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
button = new JButton("button1");
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 0;
pane.add(button, c);
button = new JButton("button2");
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 0.5;
c.gridx = 1;
c.gridy = 0;
pane.add(button, c);
...
}
2.4 事件监听
Java Swing组件自动产生各种事件来响应用户行为。
Java将事件封装成事件类,并且为每个事件类定义一个事件监听器。
一个组件注册事件监听器方法,表明该组件要响应指定事件。
也就是说我们可以通过注册
监听器,监听事件源产生的事件,从而在事件处理程序中处理我们所需要处理的用户行为。
2.4.1 注册事件的三种方法
1) 采用一个监听多个if语句来实现
继承ActionListener接口,并且实现actionPerformed方法。
通过getActionComme nd()方法来获取
事件的事件源。
缺点:当程序比较复杂时,需要一大串if语句来实现。
程序的代码比较难阅读和维护。
public class Test_01 extends JFrame implement ActionListener {
Test_01(){
JPanel panel = new JPanel();
JButton button1 = new JButton("按钮一");
JButton button2 = new JButton("按钮二");
panel.add(button1);
panel.add(button2);
this.getContentPane().add(panel);
this.setVisible(true);
button1.addActionListener(this);
button2.addActionListener(this);
}
public void actionPerformed(ActionEvent e){
String source = e.getActionCommend();
if(source.equals("按钮一")){
System.out.println("你按了按钮一");
}
if(source.equals("按钮二")){
System.out.println("你按了按钮二");
}
}
public static void main(String args[]){
new Test_01();
}
}
2) 采用匿名内部类实现
缺点:由于匿名内部类和事件组是一起的。
根据事件组在代码中的位置不同,类的定义以
及处理事件,不便于阅读。
如果事件处理程序比较复杂,内部类中的代码会变得很长。
public class Test_02 extends JFrame implement ActionListener { Test_02(){
JPanel panel = new JPanel();
JButton button1 = new JButton("按钮一");
JButton button2 = new JButton("按钮二");
panel.add(button1);
panel.add(button2);
this.getContentPane().add(panel);
this.setVisible(true);
button1.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e){
System.out.println("你按了按钮一");
}
});
button2.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e){
System.out.println("你按了按钮二");
}
});
}
public static void main(String args[]){
new Test_02();
}
}
3) 采用一般内部类实现
优点:单个事件处理,可以被工具栏、菜单栏等重复使用。
public class Test_03 extends JFrame implement ActionListener { Test_03(){
JPanel panel = new JPanel();
JButton button1 = new JButton("按钮一");
JButton button2 = new JButton("按钮二");
panel.add(button1);
panel.add(button2);
this.getContentPane().add(panel);
this.setVisible(true);
button1.addActionListener(new Button1ActionListener());
button2.addActionListener(new Button2ActionListener());
}
private class Button1ActionListener implement ActionListener{
public void actionPerformed(ActionEvent e){
System.out.println("你按了按钮一");
}
}
private class Button2ActionListener implement ActionListener{
public void actionPerformed(ActionEvent e){
System.out.println("你按了按钮二");
}
}
public static void main(String args[]){
new Test_02();
}
}
2.5 焦点体系
2.5.1 窗口事件和焦点事件
1) FocusEvent
•FocusEvent的应用:
指示 Component 已获得或失去输入焦点的低级别事件。
此低级别事件由 Component (比如 TextField)生成。
事件被传递给每一个 FocusListener 或 FocusAdapter 对象,这些对象使用 Component 的 addFocusListener 方法注册,以接收这类事件。
(FocusAdapter 对象实现 FocusListener 接口。
)当发生该事件时,所有这类侦听器对象都将获得此 FocusEvent。
•FocusEvent的事件类型:
事件类型摘要
FocusEvent.FOCUS_GAINED 控件获得焦点
FocusEvent.FOCUS_LOST 控件失去焦点
2) WindowEvent
•WindowEvent的应用:
指示窗口状态改变的低级别事件。
当打开、关闭、激活、停用、图标化或取消图标化 Win dow 对象时,或者焦点转移到 Window 内或移出 Window 时,由 Window 对象生成此低级别事件。
该事件被传递给每一个使用窗口的 addWindowListener 方法注册以接收这种事件的 WindowListener 或 WindowAdapter 对象。
(WindowAdapter 对象实现 WindowListener 接口。
)发生事件时,所有此类侦听器对象都将获取此 WindowEven t。
•WindowEvent焦点相关的事件类型:
事件类型摘要
WindowEvent.WINDOW_ACTIVATED 窗口激活
WindowEvent.WINDOW_GAINED_FOCUS 窗口获得焦点
WindowEvent.WINDOW_LOST_FOCUS 窗口失去焦点
WindowEvent.WINDOW_DEACTIVATED 窗口非激活
3) 通过FocusEvent.getOppositeComponent和WindowEvent.getOppositeWindow可以获取焦点跳转过程中的对立控件。
2.5.2 键盘事件的全局处理
•KeyEventDispatcher:在键盘消息传递到焦点控件之前预先处理键盘消息,需向 KeyboardFocusManager注册
•KeyEventPostProcessor:在键盘消息传递到焦点控件并由焦点空间处理之后处理键盘消息,需要向 KeyboardFocusManager注册。
•KeyboardFocusManager以事件链的模式处理KeyEventDispatcher和KeyEventPostProcessor,并以自身作为事件链的最后一个处理者。
2.5.3 键盘控制焦点的跳转
•Component提供了两个方法用于控制键盘的焦点跳转。
•setFocusTraversalKeysEnabled(boolean)是否支持键盘控制焦点跳转
参数值摘要
true(默认)支持键盘控制焦点,普通的KeyListener将接收不到焦点控制键。
flase 不支持键盘控制焦点,焦点控制键和其他键一致。
•setFocusTraversalKeys(int, Set)用指定按键控制焦点跳转。
•int 参数指定跳转方向
–KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS跳到上一个焦点控件。
–KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS跳到下一个焦点控件。
–KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS跳到下一个焦点循环体。
–KeyEventPostProcessor.UP_CYCLE_TRAVERSAL_KEYS跳到上一个焦点循环体。
•Set参数是键盘(AWTKeyStroke)事件的集合
–可以多个键盘事件对应一个焦点跳转操作,但不能一个事件对应多个焦点跳转操作。
–不能使用KEY_TYPED类型的键盘事件对应焦点跳转操作,可以使用KEY_PRESSED或KEY_RELEASED。
•Swing控制的默认情况:
•切换到下一个组件: 文本域:CTRL+TAB控制焦点切换其他组件:TAB和CTRL+TAB都可以控制焦点切换
•切换到上一个组件:文本域:CTRL+SHIFT+TAB控制焦点切换其他组件:SHIFT+TAB/CTRL+SHIFT+TAB都可以控制焦点切换
2.5.4 焦点跳转策略
•焦点跳转策略(FocusTraversalPolicy)定义了焦点跳转的顺序。
•Swing实现了两个焦点跳转策略
•SortingFocusTraversalPolicy:通过一个Comparator来决定焦点跳转•LayoutFocusTraversalPolicy:继承于SortingFocusTraversalPolicy,通过控件的大小,位置和方向来决定焦点的跳转。
•默认情况下Swing中的Containers使用LayoutFocusTraversalPolicy
2.5.5 程序控制焦点的跳转
•KeyboardFocusManager提供的接口:
•KeyboardFocusManager.focusNextComponent(Component) •KeyboardFocusManager.focusPreviousComponent(Component) •KeyboardFocusManager.upFocusCycle(Component) •KeyboardFocusManager.downFocusCycle(Component)
•Component提供接口:
•Component.transferFocus()
•Component.transferFocusBackward()
•Component.transferFocusUpCycly()
•Component.transferFocusDownCycle()
•Component自身获得焦点:
•Component.requestFocus():支持跨窗口获取焦点,受系统平台限制•Component.requestFocusWindow:不支持跨窗口获取焦点,不受系统平台限制。