第七章 Java的图形用户界面设计
AWT与 Swing
AWT
Java抽象窗口工具集( Abstract Window
Toolkit,简称 AWT)
Swing
Swing属于 JavaTM Foundation Classes( 简称 JFC) 的一部分,JFC包含了一组帮助程序员创建图形用户界面的功能。 AWT功能有限,因此在后来的 JDK版本中,又提供了功能更强的
Swing
AWT类与继承关系
Component
Container
Panel
Applet
Window
Frame Dialog
Button
TextFieldTextArea
TextComponent
Checkbox
MenuComponent
Menu
MenuItemMenuBar
其他组件独立不独立
SWING中主要类的继承关系
Swing与 AWT不同
Swing组件在实现时不包含任何本地
( native) 代码
Swing组件可以不受硬件平台的限制,而具有更多的功能
Swing被称为,轻量级( lightweight),组件,
AWT称为,重量级( heavyweight),组件
,重量级,组件与,轻量级,组件一同使用时,
如果组件区域有重叠,则,重量级,组件总是显示在上面
Swing组件的是具有状态( state) 的组件容器
Java的图形用户界面由组件构成,例如按钮( button),文本输入框( textfield)、
标签( label) 等都是组件,其中有一类特殊的组件称为容器( container),例如框架( frame),面板( panel) 等。
容器是组件的容器,各种组件(包括容器)
可以通过 add()方法添加到容器中顶层容器
顶层( Top level) 容器
所有组件都必须包含在某个容器中,而有些容器是可以嵌套的,在这个嵌套层次的最外层,必须是一个顶层( Top level) 容器
四种顶层容器
JFrame,JApplet,JDialog和 Jwindow
JFrame是一个带有标题行和控制按钮(最小化、恢复
/最大化、关闭)的独立窗口,创建应用程序时需要使用 JFrame。 创建小应用程序时使用 JApplet,它被包含在浏览器窗口中。创建对话框时使用 JDialog。
JWindow是一个不带有标题行和控制按钮的窗口,因此通常很少使用
JFrame创建应用程序
程序 7-1
内容窗格
顶层容器都有一个内容窗格( Content Pane)
顶层容器中除菜单之外的组件都是放在这个内容窗格中将组件放入内容窗格
通过顶层容器的 getContentPane()方法获得其缺省的内容窗格,然后将组件添加到内容窗格中
Container contentPane = frame.getContentPane();
contentPane.add(button,BorderLayout.CENTER);
frame.getContentPane().add(button,BorderLayout.CENTER);
将组件放入内容窗格
创建一个新的内容窗格取代顶层容器缺省的内容窗格。创建一个 JPanel的实例,然后将组件添加到 JPanel实例中,再通过顶层容器的 setContentPane()方法将 JPanel实例设置为新的内容窗格 (程序 7-2)
JPanel contentPane = new JPanel();
contentPane.setLayout(new BorderLayout());
contentPane.add(button,BorderLayout.CENTER);
frame.setContentPane(contentPane);
注意
向顶层容器的内容窗格添加组件时,可以直接调用顶层容器的 add()方法,这与调用内容窗格的 add()方法是等价的(从 J2SE
1.5开始 )
顶层容器缺省内容窗格的布局管理器是
BorderLayout,而 JPanel缺省的布局管理器是 FlowLayout,因此可能需要为 JPanel实例设置一个 BorderLayout布局管理器面板 (JPanel)
面板( JPanel) 是一种用途广泛的容器
与顶层容器不同的是,面板不能独立存在,
必须被添加到其他容器内部
面板可以嵌套,由此可以设计出复杂的图形用户界面程序 7-3
创建一个黄色面板,通过 add()方法在面板中添加了一个按钮,然后将该面板添加到一个 JFrame的实例中,JFrame实例的背景被设置为蓝绿色
import java.awt.*;
import javax.swing.*;
public class FrameWithPanel {
public static void main(String args[]) {
JFrame frame = new JFrame("Frame with Panel");
Container contentPane = frame.getContentPane();
contentPane.setBackground(Color.CYAN);
JPanel panel = new JPanel();
panel.setBackground(Color.yellow);
JButton button = new JButton("Press me");
panel.add(button);
contentPane.add(panel,BorderLayout.SOUTH);
frame.setSize(300,200);
frame.setVisible(true);
}
}
布局
组件的布局,包括位置和大小,通常由布局管理器( Layout Manager) 负责安排
每个容器都有一个缺省的布局管理器
通过容器的 setLayout()方法改变容器的布局管理器
多种布局管理器
java.awt.FlowLayout,java.awt.BorderLayout、
java.awt.GridLayout,java.awt.GridBagLayout、
java.awt.CardLayout,javax.swing.BoxLayout和
javax.swing.SpringLayout
程序 7-4
import java.awt.*;
import javax.swing.*;
public class ExGui {
private JFrame frame;
private JButton b1;
private JButton b2;
public static void main(String args[]){
ExGui that = new ExGui();
that.go();
}
main()方法在这个例子中,main()方法有两个作用。首先,它创建了一个 ExGui
类的实例,在这个实例创建之前,
并没有实际可用的 b1和 b2数据项。
其次,当 ExGui实例创建好以后,
main()又调用了该实例的 go()方法,在这个方法中,程序的实际功能得以实现。
程序 7-4
public void go()
{
frame = new JFrame ("GUI example"); //创建一个 JFrame实例
Container contentPane = frame.getContentPane(); //获取内容窗格
//为内容窗格设置 FlowLayout布局管理器
contentPane.setLayout(new FlowLayout());
b1 = new JButton("Press me"); //创建 JButton实例
b2 = new JButton("Don't press Me");
contentPane.add(b1); //添加按钮
contentPane.add(b2);
frame.pack();
frame.setVisible(true);
}
}
new JFrame(“GUI example”)
这条语句的功能是创建 JFrame类的一个实例。 JFrame 是一个顶层级窗口,它带有标题框(标题由构造方法中的 String
型参数,GUI example” 指定)并且可以改变大小。需要注意的是,在刚刚创建时,JFrame的大小为 0,并且不可见。
frame.getContentPane()
这条语句获取 JFrame实例缺省的内容窗格,此后可以修改它的布局管理器,并添加组件。contentPane.setLayout(new FlowLayout())
这条语句创建了一个 FlowLayout型的布局管理器,并通过调用 setLayout()方法将该布局管理器指定给前面已经获得的
JFrame实例的缺省内容窗格。
newJButton(“PressMe”)
这条语句的功能是创建
javax.swing.JButton类的一个实例,该实例是窗口中的标准按钮( button),
按钮上的标签由构造方法中 String型参数
,Press Me”指定。
frame.pack()
这条语句通知框架 frame设定一个适当的大小,以便能够以,紧缩,的形式包容各个组件。为了做到这一点,frame
需要通知布局管理器,由布局管理器安排每个组件的大小和位置。
frame.setVisible(true)
这条语句的功能是使得框架 frame以及它所包含的组件对用户可见,在此之前,框架和组件虽然已经创建好了,但是并没有显示出来,只有调用了 setVisible(true)方法后,
它们才变为可见。
FlowLayout布局管理器
FlowLayout定义在 java.awt包中,它 对容器中组件进行布局的方式是将组件逐个地安放在容器中的一行上,一行放满后就另起一个新行。在缺省情况下,将组件居中放置在容器的某一行上
FlowLayout布局管理器并不强行设定组件的大小,而是允许组件拥有它们自己所希望的尺寸
每个组件都有一个 getPreferredSize()方法,
布局管理器会调用这一方法取得每个组件希望的大小
FlowLayout构造方法
public FlowLayout()
public FlowLayout(int align)
public FlowLayout(int align,int hgap,int vgap)
align对齐方式的可选项,align的可取值有
FlowLayout.LEFT,FlowLayout.RIGHT和
FlowLayout.CENTER三种形式
hgap和 vgap,可以设定组件的水平间距和垂直间距程序 7-5
程序 7-5
BorderLayout布局管理器
BorderLayout是顶层容器中内容窗格的缺省布局管理器
由 BorderLayout管理的容器被划分成北
( North) 南( South) 西( West) 东( East)
中( Center) 五个区域,分别代表容器的上、
下、左、右和中部,用常量
BorderLayout.NORTH、
BorderLayout.SOUTH,BorderLayout.WEST、
BorderLayout.EAST、
BorderLayout.CENTER表示
BorderLayout定义在 java.awt包中
BorderLayout构造方法
BorderLayout()
构造一个各部分间间距为 0的 BorderLayout实例
BorderLayout(int,int)
构造一个各部分间具有指定间距的 BorderLayout
实例组件加入
frame = new JFrame(“Frame Title”);
button = new JButton(“Press Me”);
frame.getContentPane().add(b,BorderLayout.SOUTH);
frame.getContentPane().add(button);
按钮将被放在框架的中部
在 BorderLayout布局管理器的管理下,组件必须通过
add()方法加入到容器中的指定区域
如果在 add()方法中没有指定将组件放到哪个区域,
那么它将会缺省地被放置在 Center区域
在容器的每个区域,只能加入一个组件,如果向某个区域中加入多个组件,那么只有最后一个组件是有效的
frame,getContentPane().add(new JButton(“buttonA”),BorderLayout.SOUTH);
frame,getContentPane().add(new JButton(“buttonB”),BorderLayout.SOUTH);
frame,getContentPane().add(new JButton(“buttonC”),BorderLayout.SOUTH);
最后只有 buttonC显示在 South区域。
组件加入
如果希望在某个区域显示多个组件,可以首先在该区域放置一个内部容器 —— JPanel组件,然后将所需的多个组件放到 JPanel中,通过内部容器的嵌套构造复杂的布局
四个边界区域,如果 没有使用,大小将变为零,
Center区域将会扩展并占据这个未用区域的位置。
如果均没有使用,Center区域将会占据整个窗口程序 7-6
程序 7-6
当窗口大小改变时,窗口中按钮的相对位置并不会发生变化,但按钮的大小会改变。
GridLayout布局管理器
GridLayout是一种网格式的布局管理器,它将容器空间划分成若干行乘若干列的网格,组件依次放入其中,每个组件占据一格
网格每列的宽 (高 )度都是相同的,这个宽度大致等于容器的宽度除以网格的列 (行 )数
组件被放入容器的次序决定了它所占据的位置。
每行网格从左至右依次填充,一行用完之后转入下一行
当容器的大小改变时,GridLayout所管理的组件的相对位置不会发生变化,但组件的大小会随之改变
GridLayout构造方法
public GridLayout()
创建一个只有一行的网格,网格的列数根据实际需要而定
public GridLayout(int rows,int cols)
rows和 cols两个参数分别指定网格的行数和列数 rows
和 cols中的一个值可以为 0,但是不能两个都是 0。如果为 0,那么网格行 (列 )数将根据实际需要而定
public GridLayout(int rows,int cols,int hgap,int
vgap)
hgap和 vgap分别表示网格间的水平间距和垂直间距程序 7-7
程序 7-7
CardLayout布局管理器
是一种卡片式的布局管理器,它将容器中的组件处理为一系列卡片,每一时刻只显示出其中的一张
在 javax.swing包中定义了 JTabbedPane
类,它的使用效果与 CardLayout类似但更为简单程序 7-8
为 JFrame实例的内容窗格指定了一个
CardLayout类型的布局管理器,然后向其中加入了五张卡片,每张卡片都是 JPanel
类的一个实例,并且具有不同的背景色。
每当在程序窗口按动鼠标,下一张卡片就会显示出来。 程序 7-8
BoxLayout布局管理器
BoxLayout是定义在 javax.swing包中的布局管理器
它将容器中的组件按水平方向排成一行或按垂直方向排成一列
当组件排成一行时,每个组件可以有不同的宽度;当组件排成一列时,每个组件可以有不同的高度
BoxLayout构造方法
BoxLayout(Container target,int axis)
Container型参数 target指明是为哪个容器设置此 BoxLayout布局管理器
int型参数 axis指明组件的排列方向,
BoxLayout.X_AXIS 水平方向排列
BoxLayout.Y_AXIS 垂直方向排列程序 7-9
程序 7-9
Box容器
在 javax.swing包中定义
创建 Box实例的静态方法
public static Box createHorizontalBox()
使用使用水平方向的 BoxLayou
public static Box createVerticalBox()
使用垂直方向的 BoxLayout
创建不可见( invisible) 组件的方法,可以增加可见组件之间的距离
public static Component createHorizontalGlue()
public static Component createVerticalGlue()
public static Component createHorizontalStrut(int width)
public static Component createVerticalStrut(int height)
public static Component createRigidArea(Dimension d)
程序 7-10,7-11
程序 7-10改写 7- 9,使用 Box容器
程序 7-11演示 Glue和 Strut的效果
Glue
Strut
RigidBox 1是没有添加不可见组件时的形式,Box 2、3和 4是分别添加了不可见组件 Glue,Strut和
Rigid之后的形式,从中可以看出,Glue将填满所有剩余水平(或垂直)空间,Strut和
Rigid则具有指定的宽度(或高度)
其他布局管理器
GridBagLayout布局管理器
java.awt中定义
以网格为基础,允许组件使用最适当的大小,
既可以占多行,也可以占多列,各组件可以有不同的高度和宽度
SpringLayout等布局管理器
javax.swing中定义不使用布局管理器
1
调用容器的
setLayout(null)
将布局管理器设置为空
2
调用组件的
setBounds()
方法设置组件的位置和大小不使用布局管理器
setBounds()方法的格式,
setBounds(int x,int y,int width,int height)
前两个 int型参数设置组件的位置,后两个 int型参数设置组件的宽度和高度不使用布局管理器的例子
程序 7-12
事件处理
事件处理模型
用户在程序界面所进行的操作称为用户事件( Event),
如单击鼠标,输入字符等
Java中定义了很多事件类,用于描述不同的用户行为
代表鼠标事件的 MouseEvent类和代表键盘事件的 KeyEvent

在组件上进行某种 操作,事件处理系统便会生成一个事件类对象
每类事件对应一个的 listener监听程序接口,它规定了接收并处理该类事件的方法的规范
组件必须注册相应的事件处理程序,这种事件处理程序称为事件的监听程序( Listener)
通过类似 addXXXListener(XXXListener)的方法程序 7-14
一个 ActionEvent事件处理的例子
用到一个带单个按钮的框架,按钮组件注册了一个 ButtonHandler对象作为 ActionEvent事件的监听程序,而 ButtonHandler类实现了
ActionListener接口,在该类的
actionPerformed()方法中给出了
ActionEvent事件是如何处理的。当用户单击按钮时,产生 ActionEvent事件,该方法将会被调用。 程序 7-14
程序 7-15
事件的监听程序定义在组件类中
程序 7-15
MyButton类
import javax.swing.*;
import java.awt.event.*;
public class MyButton extends JButton implements ActionListener {
public MyButton(String text) {
super(text);
//注册事件的监听程序
addActionListener(this);
}
//出现 ActionEvent事件时,将结束程序的运行
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}
MyButton组件继承 JButton,同时实现
ActionListener接口,因此 MyButton组件对象也可作事件监听程序通过 addActionListener(this)将自身注册为自己的监听程序事件的种类
java.awt.event包和 javax.swing.event
包中定义了很多事件类
例如 ItemEvent,MouseEvent和 KeyEvent
等,并且第三方内容也可加入其中
每种事件类都有一个对应的接口,接口中声明了一个或多个抽象的事件处理方法,
凡是需要接收并处理事件类对象的类,都需要实现相应的接口常用事件类型及接口事件类别 i n te r fac e 名称 方法
m ous e D ra g g e d(M ous e E ve nt )
m ous e M ove d(M ous e E ve nt )
m ous e P re s s e d(M ous e E ve nt )
m ous e Re l e a s e d(M ous e E ve nt )
m ous e E nt e re d(M ous e E ve nt )
m ous e E x i t e d(M ous e E ve nt )
m ous e Cl i c ke d(M ous e E ve nt )
ke y P re s s e d(K e y E ve nt )
ke y Re l e a s e d(K e y E ve nt )
ke y T y p e d(K e y E ve nt )
foc us G a i ne d(F oc us E ve nt )
foc us L os t (F oc us E ve nt )
c om p one nt M ove d(Com p one nt E ve nt )
c om p one nt H i dde n(Com p one nt E ve nt )
c om p one nt Re s i z e d(Com p one nt E ve nt )
c om p one nt S how n(Com p one nt E ve nt )
A c t i on A c t i onL i s t e ne r a c t i onP e rform e d(A c t i onE ve nt )
It e m It e m L i s t e ne r i t e m S t a t e Cha ng e d(It e m E ve nt )
M ous e M ot i on M ous e M ot i onL i s t e ne r
M ous e But t on M ous e L i s t e ne r
K e y K e y L i s t e ne r
F oc us F oc us L i s t e ne r
Com p one nt Com p one nt L i s t e ne r
常用事件类型及接口事件类别 i n te r fac e 名称 方法
A dj us t e m e nt A dj us t m e nt L i s t e ne r a dj us t m e nt V a l ue Cha ng e d(A dj us t m e nt E ve nt )
w i ndo w Cl os i ng (W i ndo w E ve nt )
w i ndo w O p e ne d(W i ndo w E ve nt )
w i ndo w Ic oni fi e d(W i ndo w E ve nt )
w i ndo w D e i c oni fi e d(W i ndo w E ve nt )
w i ndo w Cl os e d(W i ndo w E ve nt )
w i ndo w A c t i va t e d(W i ndo w E ve nt )
w i ndo w D e a c t i va t e d(W i ndo w E ve nt )
c om p one nt A dde d(Con t a i ne rE ve nt )
c om p one nt Re m ove d(Con t a i ne rE ve nt )
T e x t T e x t l i s t e ne r t e x t V a l ue Cha ng e d(T e x t E ve nt )
a nc e s t orA dde d(A nc e s t orE ve nt e ve nt )
a nc e s t orM ove d(A nc e s t orE ve nt e ve nt )
a nc e s t orRe m ove d(A nc e s t orE ve nt e ve nt )
Ca re Ca re t L i s t e ne r c a re t U p da t e (Ca re t E ve nt )
Cha ng e Cha ng e L i s t e ne r s t a t e Cha ng e d(Cha ng e E ve nt )
W i ndo w W i ndo w L i s t e ne r
Cont a i ne r Cont a i ne rL i s t e ne r
A nc e s t or A nc e s t orL i s t e ne r
常用事件类型及接口事件类别 i n te r fac e 名称 方法
c ha ng e dU p da t e (D oc um e nt E ve nt )
i ns e rt U p da t e (D oc um e nt E ve nt )
re m ove U p da t e (D oc um e nt E ve nt )
U ndo a bl e E di t U ndo a bl e E di t L i s t e ne r und oa bl e E di t H a p p e ne d(U ndo a bl e E di t E ve nt )
L i s t S e l e c t i on L i s t S e l e c t i onL i s t e ne r va l ue Cha ng e d(L i s t S e l e c t i onE ve nt )
T a bl e M ode l T a bl e M ode l L i s t e ne r t a bl e Cha ng e d(T a bl e M ode l E ve nt )
t re e Col l a p s e d(T re e E x p a ns i onE ve nt )
t re e E x p a nde d(T re e E x p a ns i onE ve nt )
t re e W i l l Col l a p s e (T re e E x p a ns i onE ve nt )
t re e W i l l E x p a nd(T re e E x p a ns i onE ve nt )
t re e N ode s Cha ng e d(T re e M ode l E ve nt )
t re e N ode s Ins e rt e d(T re e M ode l E ve nt )
t re e N ode s Re m ove d(T re e M ode l E ve nt )
t re e N ode s S t ruc t ure Cha ng e d(T re e M ode l E ve nt )
T re e S e l e c t i on T re e S e l e c t i onL i s t e ne r va l ue Cha ng e d(T re e S e l e c t i onE ve nt )
D oc um e nt D oc um e nt L i s t e ne r
T re e E x p a ns i on T re e E x p a ns i onL i s t e ne r
T re e W i l l E x p a nd T re e W i l l E x p a ndL i s t e ne r
T re e M ode l T re e M ode l L i s t e ne r
例子
程序将检测鼠标的拖动(即按住鼠标键并同时移动鼠标的操作)以及鼠标进入和离开窗口的情况
TwoListener类同时实现 MouseMotionListener
和 MouseListener两个接口
监听多类事件
获取事件的细节
程序 7-16
f.addMouseListener(this);
f.addMouseMotionListener(this);
多监听程序
事件监听模式允许为一个组件注册多个监听程序
允许根据需要多次调用 addListener方法为某个组件的同一事件注册多个不同的监听程序,当事件发生时,
所有相关的监听程序都会被调用
当事件发生时,单个事件的多个监听程序的调用顺序是不确定的
如果在某个程序中,各个监听程序的调用顺序很重要,
那么它们之间就不是不相关的,在这种情况下,你就不能再为同一事件注册多个监听程序,而是只注册唯一一个监听程序,然后再在该监听程序中调用所需的其他方法事件适配器
为了进行事件处理,我们需要创建实现
Listener接口的类,而某些 Listener接口中,声明了很多抽象方法,为了实现这些接口,我们需要一一实现这些方法
为了编程方便,Java为一些声明了多个方法的 Listener接口提供了相对应的适配器
( Adapter) 类,在适配器类中实现了相应接口中的全部方法,只是方法的内容为空
MouseListener接口
public interface MouseListener extends EventListener {
public void mouseClicked(MouseEvent e);
public void mousePressed(MouseEvent e);
public void mouseReleased(MouseEvent e);
public void mouseEntered(MouseEvent e);
public void mouseExited(MouseEvent e);
}
适配器 MouseAdapter:
public abstract class MouseAdapter implements MouseListener {
public void mouseClicked(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
在创建新类时,
就可以不实现接口,而是只继承某个适当的适配器,并且重载关心的事件处理方法使用适配器的例子
import java.awt.*;
import java.awt.event.*;
import java.awt.event.*;
public class MouseClickHandler extends MouseAdapter {
// 我们只关心对单击鼠标事件的处理,因此在这里继承
// MouseAdapter,以避免编写其他不需要的事件处理方法
public void mouseClicked(MouseEvent e) {
// 进行有关的处理
}
}
接口及适配器接口名称 适配器名称
Co m p one nt L i s t e ne r Co m p one nt A da p t e r
Co nt a i ne rL i s t e ne r Co nt a i ne rA da p t e r
F oc us L i s t e ne r F oc us A da p t e r
K e y L i s t e ne r K e y A da p t e r
M ous e L i s t e ne r M ous e A da p t e r
M ous e M ot i onL i s t e ne r M ous e M ot i onA da p t e r
M ous e In p ut L i s t e ne r M ous e In p ut A da p t e r
W i ndo w L i s t e ne r W i ndo w A da p t e r