建立GUIs
本模块讲述图形用户界面的建立及布局。它介绍了抽象视窗工具包(AWT),一种建立GUIs的类包。
AWT提供用于所有Java applets及应用程序中的基本GUI组件,还为应用程序提供与机器的界面。这将保证一台计算机上出现的东西与另一台上的相一致。
在学AWT之前,简单回顾一下对象层次。记住,超类是可以扩展的,它们的属性是可继承的。而且,类可以被抽象化,这就是说,它们是可被分成子类的模板,子类用于类的具体实现。
显示在屏幕上的每个GUI组件都是抽象类组件的子类。也就是说,每个从组件类扩展来的图形对象都与允许它们运行的大量方法和实例变量共享。
Container是Component的一个抽象子类,它允许其它的组件被嵌套在里面。这些组件也可以是允许其它组件被嵌套在里面的容器,于是就创建了一个完整的层次结构。在屏幕上布置GUI组件,容器是很有用的。Panel是Container的最简单的类。Container的另一个子类是Window。
Java.awt包
Java.awt包包含生成WIDGETS和GUI组件的类。该包的基本情况如下图所示。黑体字的类表明该模块的要点。
<wrapblock><shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_s1026" style="MARGIN-TOP: 10.9pt; Z-INDEX: 1; MARGIN-LEFT: 2.1pt; WIDTH: 356.7pt; POSITION: absolute; HEIGHT: 351.4pt" o:allowincell="f" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image001.png"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>
建立图形用户界面
Container
Container有两个主要类型:Window和Panel
Window是Java.awt.Window.的对象。Window是显示屏上独立的本机窗口,它独立于其它容器。
Window有两种形式:Frame(框架)和Dialog(对话框)。Frame和Dialog是Window的子类。Frame是一个带有标题和缩放角的窗口。对话没有菜单条。尽管它能移动,但它不能缩放。
Panel是Java.awt.Panel的对象。Panel包含在另一个容器中,或是在Web浏览器的窗口中。Panel确定一个四边形,其它组件可以放入其中。Panel必须放在Window之中(或Window的子类中)以便能显示出来。
滚动块也是Window的一个子类。它在模块10“AWT组件集”里讨论。
定位组件
容器里的组件的位置和大小是由布局管理器决定的。容器对布局管理器的特定实例保持一个引用。当容器需要定位一个组件时,它将调用布局管理器来做。当决定一个组件的大小时,同样如此。布局管理器完全控制容器内的所有组件。它负责计算并定义上下文中对象在实际屏幕中所需的大小。
组件大小
因为布局管理器负责容器里的组件的位置和大小,因此不需要总是自己去设定组件的大小或位置。如果想这样做(使用setLocation(),setSize()或setBounds()方法中的任何一种),布局管理器将覆盖你的决定。
如果必须控制组件的大小或位置,而使用标准布局管理器做不到,那就可能通过将下述方法调用发送到容器中来中止布局管理器:
setLayout(null);
做完这一步,必须对所有的组件使用setLocation(),setSize()或setBounds(),来将它们定位在容器中。请注意,由于窗口系统和字体大小之间的不同,这种办法将导致从属于平台的布局。更好的途径是创建布局管理器的新子类。
Frames
Frames是Window的一个子类。它是带有标题和缩放角的窗口。它继承于Java.awt.Container,因此,可以用add()方式来给框架添加组件。框架的缺省布局管理器就是Border Layout。它可以用setLayout()方式来改变。
框架类中的构造程序 Frame(String)用由String规定的标题来创建一个新的不可见的框架对象。当它还处于不可见状态时,将所有组件添加到框架中。
1. import java.awt.*;
2. public class MyFrame extends Frame {
3. public static void main (String args[]) {
6. MyFrame fr = new MyFrame("Hello Out There!");
7. fr.setSize(500,500);
4. fr.setBackground(Color.blue);
5. fr.setVisible(true);
6. }
7. public MyFrame (String str) {
8. super(str);
9. }
10. }
上述程序创建了下述框架,它有一个具体的标题、大小及背景颜色。
<wrapblock><shape id="_x0000_s1027" style="MARGIN-TOP: 11.9pt; Z-INDEX: 2; MARGIN-LEFT: 0px; WIDTH: 366pt; POSITION: absolute; HEIGHT: 123pt" o:allowincell="f" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image003.gif"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>
Panels
象Frames一样,Panels提供空间来连接任何GUI组件,包括其它面板。每个面板都可以有它自己的布管理程序。
一旦一个面板对象被创建,为了能看得见,它必须添加到窗口或框架对象上。用Container类中的add()方式可以做到这一点。
下面的程序创建了一个小的黄色面板,并将它加到一个框架对象上:
1. import java.awt.*;
2. public class FrameWithPanel extends Frame {
3.
4. // Constructor
5. public FrameWithPanel (String str) {
6. super (str);
7. }
8.
9. public static void main (String args[]) {
10. FrameWithPanel fr =
11. new FrameWithPanel ("Frame with Panel");
12. Panel pan = new Panel();
13.
14. fr.setSize(200,200);
15. fr.setBackground(Color.blue);
16. fr.setLayout(null); //override default layout mgr
17. pan.setSize (100,100);
18. pan.setBackground(Color.yellow);
19.
20. fr.add(pan);
21. fr.setVisible(true);
22. }
23. ....
24.
<wrapblock><shape id="_x0000_s1028" style="MARGIN-TOP: 10pt; Z-INDEX: 3; MARGIN-LEFT: 0px; WIDTH: 365.25pt; POSITION: absolute; HEIGHT: 166.5pt" o:allowincell="f" type="#_x0000_t75"><font face="Times New Roman"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image004.gif"></imagedata><wrap type="topAndBottom"></wrap></font></shape></wrapblock>
容器布局(Container Layout)
容器中组件的布局通常由布局管理器控制。每个Container(比如一个Panel或一个Frame)都有一个与它相关的缺省布局管理器,它可以通过调用setLayout()来改变。
布局管理器负责决定布局方针以及其容器的每一个子组件的大小。
布局管理器
下面的布局管理器包含在Java编程语言中:
- Flow Layout—Panel和Applets的缺省布局管理器
- Border Layout—Window、Dialog及Frame的缺省管理程序
- Grid Layout
- Card Layout
- GridBag Layout
GridBag布局管理器在本模块中不深入讨论。
Flow Layout的一个简单例子
这个样板代码阐述了几个要点,将在下一节讨论。
1. import java.awt.*;
2.
3. public class ExGui {
4. private Frame f;
5. private Button b1;
6. private Button b2;
7.
8. public static void main(String args[]) {
9. ExGui guiWindow = new ExGui();
10. guiWindow.go();
11. }
12.
13. public void go() {
14. f = new Frame("GUI example");
15. f.setLayout(new FlowLayout());
16. b1 = new Button("Press Me");
17. b2 = new Button("Don't Press Me");
18. f.add(b1);
19. f.add(b2);
20. f.pack();
21. f.setVisible(true);
22. }
23. }
main()方法
本例中第8行main()方法有两个作用。首先,它创建了ExGui对象的一个实例。回想一下,直到一个实例存在,还没有被称做f,b1和b2的真实数据项可以使用。第二,当数据空间被创建时,main()在该实例的上下文中调用实例方法go()。在go()中,真正的运行才开始。
new Frame (“GUI Example”)
这个方法创建Java.awt.Frame类的一个实例。根据本地协议,在Java编程语言中,Frame是顶级窗口,带有标题条—在这种情况下,标题条由构造程序参数“GUI Example”定义—缩放柄,以及其它修饰。
f.setLayout (new FlowLayout())
这个方法创建Flow布局管理器的一个实例,并将它安装在框架中。对于每个Frame、Border布局来说,都有一个布局管理器,但本例中没有使用。Flow布局管理器在AWT中是最简单的,它在某种程度上象一个页面中的单词被安排成一行一行的那样来定位组件。请注意,Flow布局缺省地将每一行居中。
new Button(“Press Me”)
这个方法创建Java.awt.Button类的一个实例。按钮是从本地窗口工具包中取出的一个标准按钮。按钮标签是由构造程序的字符串参数定义的。
f.add(b1)
这个方法告诉框架f(它是一个容器),它将包容组件b1。b1的大小和位置受从这一点向前的Frame布局管理器的控制。
f.pack()
这个方法告诉框架来设定大小,能恰好密封它所包含的组件。为了确定框架要用多大,f.pack()询问布局管理器,在框架中哪个负责所有组件的大小和位置。
f.setVisible(true)
这个方法使框架以及其所有的内容变成用户看得见的东西。
Flow布局管理器
与其它布局管理器不一样,Flow布局管理器不限制它所管理的组件的大小,而是允许它们有自己的最佳大小。
如果想将组件设定缺省居中的话,Flow布局构造程序参数允许将组件左对齐或右对齐。
如果想在组件之间创建一个更大的最小间隔,可以规定一个界限。
当用户对由Flow布局管理的区域进行缩放时,布局就发生变化。如:
<wrapblock><shape id="_x0000_s1029" style="MARGIN-TOP: 0px; Z-INDEX: 4; MARGIN-LEFT: 0px; WIDTH: 379.5pt; POSITION: absolute; HEIGHT: 109.5pt" o:allowincell="f" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image005.gif"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>
下面的例子就是如何用类容器的setLayout()方法来创建Flow布局对象并安装它们。
对齐的值必须是FlowLayout.LEFT, FlowLayout.RIGHT,或 FlowLayout.CENTER。例如:
setLayout(new FlowLayout(FlowLayout.RIGHT, 20, 40));
下述程序构造并安装一个新Flow布局,它带有规定好的对齐方式以及一个缺省的5单位的水平和垂直间隙。对齐的值必须是FlowLayout.LEFT, FlowLayout.RIGHT,或 FlowLayout.CENTER。
setLayout(new FlowLayout(int align);
下述程序构造并安装一个新Flow布局,它带有规定好的居中对齐方式和一个缺省的5单位的水平和垂直间隙。
这个模块代码将几个按钮添加到框架中的一个Flow布局中:
1. import java.awt.*;
2.
3. public class MyFlow {
4. private Frame f;
5. private Button button1, button2, button3;
6.
7. public static void main (String args[]) {
8. MyFlow mflow = new MyFlow ();
9. mflow.go();
10. }
11.
12. public void go() {
13. f = new Frame ("Flow Layout");
14. f.setLayout(new FlowLayout());
15. button1 = new Button("Ok");
16. button2 = new Button("Open");
17. button3 = new Button("Close");
18. f.add(button1);
19. f.add(button2);
20. f.add(button3);
21. f.setSize (100,100);
22. f.setVisible(true);
23. }
24. }
Border布局管理器
Border布局管理器为在一个Panel或Window中放置组件提供一个更复杂的方案。Border布局管理器包括五个明显的区域:东、南、西、北、中。
北占据面板的上方,东占据面板的右侧,等等。中间区域是在东、南、西、北都填满后剩下的区域。当窗口垂直延伸时,东、西、中区域也延伸;而当窗口水平延伸时,东、西、中区域也延伸。
Border布局管理器是用于Dialog和Frame的缺省布局管理器。
<wrapblock><shape id="_x0000_s1030" style="MARGIN-TOP: 6.4pt; Z-INDEX: 5; MARGIN-LEFT: 36.15pt; WIDTH: 306.15pt; POSITION: absolute; HEIGHT: 144.15pt" o:allowincell="f" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image006.png"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>
下面的代码对前例进行了修改,表示出了Border布局管理器的特性。可以用从Container类继承的setLayout()方法来将布局设定为Border布局。
1. import java.awt.*;
2.
3. public class ExGui2 {
4. private Frame f;
5. private Button bn, bs, bw, be, bc;
6.
7. public static void main(String args[]) {
8. ExGui2 guiWindow2 = new ExGui2();
9. guiWindow2.go();
10. }
11.
12. public void go() {
13. f = new Frame("Border Layout");
14. bn = new Button("B1");
15. bs = new Button("B2");
16. be = new Button("B3");
17. bw = new Button("B4");
18. bc = new Button("B5");
19.
20. f.add(bn, BorderLayout.NORTH);
21. f.add(bs, BorderLayout.SOUTH);
22. f.add(be, BorderLayout.EAST);
23. f.add(bw, BorderLayout.WEST);
24. f.add(bc, BorderLayout.CENTER);
25.
26. f.setSize (200, 200);
27. f.setVisible(true);
28. }
29. }
下面这一行:
构造并安装一个新Border布局,在组件之间没有间隙。
这一行
构造并安装一个Border布局,在由hgap和 vgap 规定的组件之间有规定的间隙。
在布局管理器中组件必须被添加到指定的区域,而且还看不见。区域名称拼写要正确,尤其是在选择不使用常量(如add(button,”Center”))而使用add(button,BorderLayout.CENTER)时。拼写与大写很关键。
可以使用Border布局管理器来产生布局,且带有在缩放时在一个方向、另一方向或双方向上都延伸的元素。
注—如果窗口水平缩放,南、北、中区域变化;如果窗口垂直缩放,东、西、中区域变化; |
如果离开一个Border布局未使用的区域,,好象它的大小为0。中央区域即使在不含组件的情况下仍然呈现为背景。
可以仅将单个组件添加到Border布局管理器五个区域的每一个当中。如果添加不止一个,只有最后一个看得见。后面的模块将演示如何用中间容器来允许不止一个组件被放在单个Border布局管理器区域的空间里。
注—布局管理器给予南、北组件最佳高度,并强迫它们与容器一样宽。但对于东、西组件,给予最佳宽度,而高度受到限制。
|
Grid布局管理器
Grid布局管理器为放置组件提供了灵活性。用许多行和栏来创建管理程序。然后组件就填充到由管理程序规定的单元中。比如,由语句new GridLayout(3,2)创建的有三行两栏的Grid布局能产生如下六个单元:
<wrapblock><shape id="_x0000_s1061" style="MARGIN-TOP: 16.25pt; Z-INDEX: 36; LEFT: 0px; MARGIN-LEFT: 42.05pt; WIDTH: 361.5pt; POSITION: absolute; HEIGHT: 111pt; TEXT-ALIGN: left" type="#_x0000_t75"><font face="Times New Roman"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image010.gif"></imagedata><wrap type="topAndBottom"></wrap></font></shape></wrapblock>
因为有Border布局管理器,组件相应的位置不随区域的缩放而改变。只是组件的大小改变。
Grid布局管理器总是忽略组件的最佳大小。所有单元的宽度是相同的,是根据单元数对可用宽度进行平分而定的。同样地,所有单元的高度是相同的,是根据行数对可用高度进行平分而定的。
将组件添加到网格中的命令决定它们占有的单元。单元的行数是从左到右填充,就象文本一样,而页是从上到下由行填充。
行
setLayout(new GridLayout());
创建并安装一个Grid布局,每行中的每个组件有一个栏缺省。
行
创建并安装一个带有规定好行数和栏数的Grid布局。对布局中所有组件所给的大小一样。
下一行:
setLayout(new GridLayout(int rows, int cols, int hgap, int vgap);
创建并安装一个带有规定好行数和栏数的网格布局。布局中所有组件所给的大小一样。hgap和vgap规定组件间各自的间隙。水平间隙放在左右两边及栏与栏之间。垂直间隙放在顶部、底部及每行之间。
注—行和栏中的一个,不是两个同时,可以为0。这就是说,任何数量的对象都可以放在一个行或一个栏中。
|
应用程序代码如下:
1. import java.awt.*;
2. public class GridEx {
3. private Frame f;
4. private Button b1, b2, b3, b4, b5, b6;
5.
6. public static void main(String args[]) {
7. GridEx grid = new GridEx();
8. grid.go();
9. }
10.
11. public void go() {
12. f = new Frame("Grid example");
13.
14. f.setLayout (new GridLayout (3, 2));
15. b1 = new Button("1");
16. b2 = new Button("2");
17. b3 = new Button("3");
18. b4 = new Button("4");
19. b5 = new Button("5");
20. b6 = new Button("6");
21.
22. f.add(b1);
23. f.add(b2);
24. f.add(b3);
25. f.add(b4);
26. f.add(b5);
27. f.add(b6);
28.
29. f.pack();
30. f.setVisible(true);
31. }
32. }
布局管理器
Card布局管理器能将界面看作一系列的卡,其中的一个在任何时候都可见。用add()方法来将卡添加到Card布局中。Card布局管理器的show()方法应请求转换到一个新卡中。下例就是一个带有5张卡的框架。
<wrapblock><shape id="_x0000_s1031" style="MARGIN-TOP: 0px; Z-INDEX: 6; MARGIN-LEFT: 17.1pt; WIDTH: 330.15pt; POSITION: absolute; HEIGHT: 152.55pt" o:allowincell="f" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image011.png"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>
鼠标点击左面板将视图转换到右面板,等等。
用来创建上图框架的代码段如下所示:
1. import java.awt.*;
2. import java.awt.event.*;
3.
4. public class CardTest implements MouseListener {
5.
6. Panel p1, p2, p3, p4, p5;
7. Label l1, l2, l3, l4, l5;
8.
9. // Declare a CardLayout object,
10. // to call its methods
11. CardLayout myCard;
12. Frame f;
13.
14. public static void main (String args[]) {
15. CardTest ct = new CardTest ();
16. ct.init();
17. }
18.
19. public void init () {
20. f = new Frame ("Card Test");
21. myCard = new CardLayout();
22. f.setLayout(myCard);
23.
24. // create the panels that I want
25. // to use as cards
26. p1 = new Panel();
27. p2 = new Panel();
28. p3 = new Panel();
29. p4 = new Panel();
30. p5 = new Panel();
31. // create a label to attach to each panel, and
32. // change the color of each panel, so they are
33. // easily distinguishable
34.
35. l1 = new Label("This is the first Panel");
36. p1.setBackground(Color.yellow);
37. p1.add(l1);
38.
39. l2 = new Label("This is the second Panel");
40. p2.setBackground(Color.green);
41. p2.add(l2);
42.
43. l3 = new Label("This is the third Panel");
44. p3.setBackground(Color.magenta);
45. p3.add(l3);
46.
47. l4 = new Label("This is the fourth Panel");
48. p4.setBackground(Color.white);
49. p4.add(l4);
50.
51. l5 = new Label("This is the fifth Panel");
52. p5.setBackground(Color.cyan);
53. p5.add(l5);
54.
55. // Set up the event handling here ....
√
56. // add each panel to my CardLayout
57. f.add(p1, "First");
58. f.add(p2, "Second");
59. f.add(p3, "Third");
60. f.add(p4, "Fourth");
61. f.add(p5, "Fifth");
62.
63. // display the first panel
64. myCard.show(f, "First");
65.
66. f.setSize (200, 200);
67. f.setVisible(true);
68. }
GridBag布局管理器
除了Flow、Border、Grid和Card布局管理器外,核心Java.awt也提供GridBag布局管理器。
GridBag布局管理器在网格的基础上提供复杂的布局,但它允许单个组件在一个单元中而不是填满整个单元那样地占用它们的最佳大小。网格包布局管理器也允许单个组件扩展成不止一个单元。
创建面板及复杂布局
下面的程序使用一个面板,允许在一个Border布局的北部地区放置两个按钮。这种嵌套法对复杂布局来说是基本的。请注意,就框架本身而论,面板被看作象另一个组件。
1. import java.awt.*;
2.
3. public class ExGui3 {
4.
5. private Frame f;
6. private Panel p;
7. private Button bw, bc;
8. private Button bfile, bhelp;.
9.
10. public static void main(String args[]) {
11. ExGui3 gui = new ExGui3();
12. gui.go();
13. }
14. public void go() {
15. f = new Frame("GUI example 3");
16. bw = new Button("West");
17. bc = new Button("Work space region");
18. f.add(bw, BorderLayout.WEST );
19. f.add(bc, BorderLayout.CENTER );
20. p = new Panel();
21. bfile = new Button("File");
22. bhelp = new Button("Help");
23. p.add(bfile);
24. p.add(bhelp);
25. f.add(p, BorderLayout.NORTH );
26. f.pack();
27. f.setVisible(true);
28. }
29. }
<wrapblock><shape id="_x0000_s1032" style="MARGIN-TOP: 30.15pt; Z-INDEX: 7; MARGIN-LEFT: 96.15pt; WIDTH: 142.5pt; POSITION: absolute; HEIGHT: 86.25pt" o:allowincell="f" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image013.gif"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>
当这个例子运行时,其结果如下所示:
<wrapblock><shape id="_x0000_s1033" style="MARGIN-TOP: 23.65pt; Z-INDEX: 8; MARGIN-LEFT: 66.15pt; WIDTH: 192.75pt; POSITION: absolute; HEIGHT: 140.25pt" o:allowincell="f" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image014.gif"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>
如果缩放窗口,它看起来如下:
请注意,现在,Border布局的北部地区有效地保持着两个按钮。事实上,它保持的只是单个面板,但这个面板含有这两个按钮。面板的大小和位置是由Border布局管理器决定的,一个面板的最佳大小是由该面板中组件的最佳大小来决定的。面板中按钮的大小和位置受Flow布局的控制,该布局是由缺省与面板相关联的。
AWT事件模型
本模块讨论了事件驱动的图形用户界面(GUI)的用户输入机制。
什么是事件?
如果用户在用户界面层执行了一个动作(鼠标点击和按键),这将导致一个事件的发生。事件是描述发生了什么的对象。存在各种不同类型的事件类用来描述各种类型的用户交互。
事件源
事件源是一个事件的产生者。例如,在Button组件上点击鼠标会产生以这个Button 为源的一个ActionEvent. 这个ActionEvent实例是一个对象,它包含关于刚才所发生的那个事件的信息的对象。这些信息包括:
- getActionCommand-返回与动作相关联的命令名称。
- GetModifiers-返回在执行动作时持有的修饰符。
事件处理器
事件处理器就是一个接收事件、解释事件并处理用户交互的方法。
JDK1.0的事件模型与JDK1.2的事件模型比较
在JDK1.1中,事件接收和处理的方法发生了重要的改变。本节将比较以前的事件模型(JDK1.0)和当前的事件模型(JDK1.1和JDK1.2)。
JDK1.0采用的是层次事件模型,而JDK1.1以及更高的版本采用的是委托事件模型。
层次模型(JDK1.0)
层次模型是基于容器的。 事件先发送到组件,然后沿容器层次向上传播。没有被组件处理的事件会自动地继续传播到组件的容器。
JDK1.0的事件模型与JDK1.2的事件模型比较
例如,在下图中,Button对象(包含在一个Frame上的Panel中)上的鼠标点击首先向Button发送一个动作事件。如果它没有被Button处理,这个事件会被送往Panel,如果它在那儿仍然没有被处理,这个事件会被送往Frame。
<shapetype id="_x0000_t202" path="m,l,21600r21600,l21600,xe" o:spt="202" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><path o:connecttype="rect" gradientshapeok="t"></path></shapetype><shape id="_x0000_s1038" style="MARGIN-TOP: 7.8pt; Z-INDEX: 13; MARGIN-LEFT: 42pt; WIDTH: 294pt; POSITION: absolute; HEIGHT: 117pt" type="#_x0000_t202"></shape>
层次模型(JDK1.0) 优点 - 简单,而且非常适合面向对象的编程环境。 缺点 - 事件只能由产生这个事件的组件或包含这个组件的容器处理。 - 为了处理事件,你必须定义接收这个事件的组件的子类,或者在基容器创建handleEvent()方法。 |
<wrapblock><shape id="_x0000_s1036" style="MARGIN-TOP: 15.6pt; Z-INDEX: 11; MARGIN-LEFT: 21pt; WIDTH: 321.75pt; POSITION: absolute; HEIGHT: 159pt" type="#_x0000_t75"><font size="3"><font face="Times New Roman"><imagedata o:title="mod09_awt-6" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image015.gif"></imagedata><wrap type="topAndBottom"></wrap></font></font></shape></wrapblock>
层次模型(JDK1.0)(续)
这种模型有一个显著的优点:
- 它简单,而且非常适合面向对象的编程环境;说到底,所有的组件都继承了java.awt.Component类,而handleEvent()就在java.awt.Component类中。
然而,这种模型也存在缺点:
- 事件只能由产生这个事件的组件或包含这个组件的容器处理。这个限制违反了面向对象编程的一个基本原则:功能应该包含在最合适的类中。而最适合处理事件的类往往并不是源组件的容器层次中的成员。
- 大量的CPU周期浪费在处理不相关的事件上。任何对于程序来说不相关或者并不重要的事件会沿容器层次一路传播,直到最后被抛弃。不存在一种简单的方法来过滤事件。
- 为了处理事件,你必须定义接收这个事件的组件的子类,或者在基容器创建一个庞大的handleEvent()方法。
委托事件模型是在JDK1.1中引入的。在这个模型中,事件被送往产生这个事件的组件,然而,注册一个或多个称为监听者的类取决于每一个组件,这些类包含事件处理器,用来接收和处理这个事件。采用这种方法,事件处理器可以
<wrapblock><shape id="_x0000_s1037" style="MARGIN-TOP: -24.5pt; Z-INDEX: 12; MARGIN-LEFT: 12.05pt; WIDTH: 330.75pt; POSITION: absolute; HEIGHT: 141.05pt" type="#_x0000_t75"><font face="Times New Roman"><imagedata o:title="mod09_awt-8" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image016.png"></imagedata><wrap type="topAndBottom"></wrap></font></shape></wrapblock>
安排在与源组件分离的对象中。监听者就是实现了Listener接口的类。
事件是只向注册的监听者报告的对象。每个事件都有一个对应的监听者接口,规定哪些方法必须在适合接收那种类型的事件的类中定义。实现了定义那些方法的接口的类可以注册为一个监听者。
<chsdate w:st="on" year="1899" month="12" day="30" islunardate="False" isrocdate="False"><strong style="mso-bidi-font-weight: normal"><span lang="EN-US">9.4.2</span></strong></chsdate> 委托模型
从没有注册的监听者的组件中发出的事件不会被传播。
例如,这是一个只含有单个Button的简单Frame。
1.import java.awt.*;
2.
3.public class TestButton {
4.public static void main(String args[]) {
5.Frame f = new Frame("Test");
6.Button b = new Button("Press Me!");
7.b.addActionListener(new ButtonHandler());
8.f.add(b,"Center");
9.f.pack();
10.f.setVisible(true);
11.}
12.}
ButtonHandler类是一个处理器类,事件将被委托给这个类。
1.import java.awt.event.*;
2.
3.public class ButtonHandler implements ActionListener {
4.public void actionPerformed(ActionEvent e) {
5.System.out.println("Action occurred");
6.System.out.println("Button's label is : '
7.+ e.getActionCommand());
8.}
9.}
这两个范例的特征如下:
- Button类有一个addActionListner(ActionListener)方法。
- AddActionListner接口定义了一个方法actionPerformed,用来接收一个ActionEvent。
- 创建一个Button对象时,这个对象可以通过使用addActionListener方法注册为ActionEvents的监听者。调用这个方法时带有一个实现了ActionListener接口的类的参数。
委托模型(JDK1.1或更高版本)(续)
- 在Button对象上用鼠标进行点击时,将发送一个ActionEvent事件。这个ActionEvent事件会被使用addActionListener()方法进行注册的所有ActionListener的actionPerformed()方法接收。
- ActionEvent类的getActionCommand()方法返回与动作相关联的命令名称。以按钮的点击动作为例,将返回Button的标签。
这种方法有若干优点:
- 事件不会被意外地处理。在层次模型中,一个事件可能传播到容器,并在非预期的层次被处理。
- 有可能创建并使用适配器(adapter)类对事件动作进行分类。
- 委托模型有利于把工作分布到各个类中。
- 新的事件模型提供对JavaBeans的支持。
这种方法也有一个缺点:
- 尽管当前的JDK支持委托模型和JDK1.0事件模型,但不能混合使用JDK1.0和JDK1.1。
图形用户界面的行为
事件类型
<wrapblock><shape id="_x0000_s1039" style="MARGIN-TOP: 70.2pt; Z-INDEX: 14; MARGIN-LEFT: 0px; WIDTH: 320.1pt; POSITION: absolute; HEIGHT: 156pt" o:allowincell="f" type="#_x0000_t75"><font size="3"><imagedata o:title="mod09_awt-10" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image018.png"></imagedata><wrap type="topAndBottom"></wrap></font></shape></wrapblock>
我们已经介绍了在单一类型事件上下文中从组件接收事件的通用机制。事件类的层次结构图如下所示。许多事件类在java.awt.event包中,也有一些事件类在API的其他地方。
对于每类事件,都有一个接口,这个接口必须由想接收这个事件的类的对象实现。这个接口还要求定义一个或多个方法。当发生特定的事件时,就会调用这些方法。表9-1列出了这些(事件)类型,并给出了每个类型对应的接口名称,以及所要求定义的方法。这些方法的名称是易于记忆的,名称表示了会引起这个方法被调用的源或条件。
方法类型和接口
Category | Interface Name | Methods |
Action | ActionListener | actionPerformed(ActionEvent) |
Item | ItemListener | itemStateChanged(ItemEvent) |
Mouse motion | MouseMotionListener | mouseDragged(MouseEvent) |
|
| mouseMoved(MouseEvent) |
Mouse button | MouseListener | mousePressed(MouseEvent) |
|
| mouseReleased(MouseEvent) |
|
| mouseEntered(MouseEvent) |
|
| mouseExited(MouseEvent) |
|
| mouseClicked(MouseEvent) |
Key | KeyListener | keyPressed(KeyEvent) |
|
| keyReleased(KeyEvent) |
|
| keyTyped(KeyEvent) |
Focus | FocusListener | focusGained(FocusEvent) |
|
| focusLost(FocusEvent) |
Adjustment | AdjustmentListener | adjustmentValueChanged (AdjustmentEvent) |
Component | ComponentListener | componentMoved(ComponentEvent) |
|
| componentHidden(ComponentEvent) |
|
| componentResized(ComponentEvent) |
|
| componentShown(ComponentEvent) |
<wrapblock><shape id="_x0000_s1040" style="MARGIN-TOP: 23.4pt; Z-INDEX: 15; LEFT: 0px; MARGIN-LEFT: 5.25pt; WIDTH: 351.75pt; POSITION: absolute; HEIGHT: 206.1pt; TEXT-ALIGN: left" o:allowincell="f" type="#_x0000_t75"><font size="3"><font face="Times New Roman"><imagedata cropbottom="36664f" o:title="mod09_awt-11" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image020.png"></imagedata><wrap type="topAndBottom"></wrap></font></font></shape></wrapblock>
方法类型和接口(续)
复杂的范例
本节将考察一个更复杂的Java软件范例。它将跟踪鼠标被按下时,鼠标的移动情况(鼠标拖动)。这个范例还将监测当鼠标没有按下时,鼠标的移动情况(鼠标移动)。
当鼠标按下或没有按下时,移动鼠标产生的事件会被实现了MouseMotionListener接口的类的对象检取。这个接口要求定义两个方法,mouseDragged()和mouseMoved()。即使你只对鼠标拖动感兴趣,也必须提供这两个方法。然而,mouseMoved()的体可以是空的。
要检取其他鼠标事件,包括鼠标点击,必须定义MouseListener接口。这个接口包括若干个事件,即:mouseEntered, mouseExited, mousePressed, mouseReleased和 mouseClicked。
发生鼠标或键盘事件时,有关鼠标的位置和所按下的键的信息可以从事件中得到。代码如下:
1.import java.awt.*;
2.import java.awt.event.*;
3.
4.public class TwoListen implements
5.MouseMotionListener, MouseListener {
6.private Frame f;
7.private TextField tf;
8.
9.public static void main(String args[]) {
10.TwoListen two = new TwoListen();
11.two.go();
12.}
13.
复杂的范例(续)
1.public void go() {
2.f = new Frame("Two listeners example");
3.f.add (new Label ("Click and drag the mouse"),
4."BorderLayout.NORTH");
5.tf = new TextField (30);
6.f.add (tf, "BorderLayout.SOUTH");
7.
8.f.addMouseMotionListener(this);
9.f.addMouseListener (this);
10.f.setSize(300, 200);
11.f.setVisible(true);
12.}
13.
14.// These are MouseMotionListener events
15.public void mouseDragged (MouseEvent e) {
16.String s =
17."Mouse dragging: X = " + e.getX() +
18." Y = " + e.getY();
19.tf.setText (s);
20.}
21.
22.public void mouseMoved (MouseEvent e) {
23.}
24.
25.// These are MouseListener events
26.public void mouseClicked (MouseEvent e) {
27.}
28.
29.public void mouseEntered (MouseEvent e) {
30.String s = "The mouse entered";
31.tf.setText (s);
32.}
复杂的范例(续)
1.public void mouseExited (MouseEvent e) {
2.String s = "The mouse has left the building";
3.tf.setText (s);
4.}
5.
6.public void mousePressed (MouseEvent e) {
7.}
8.
9.public void mouseReleased (MouseEvent e) {
10.}
11.}
这个范例中的一些地方值得注意。它们将在以下的几节中讨论。
定义多重接口
这个类由第4行中的如下代码声明:
implements MouseMotionListener, MouseListener
声明多个接口时,可以用逗号隔开。
监听多个源
如果你在第11行和第12行中,调用如下方法:
两种类型的事件都会引起TwoListen类中的方法被调用。一个对象可以“监听”任意数量的事件源;它的类只需要实现所要求的接口。
f.addMouseListener(this);
f.addMouseMotionListener(this);
复杂的范例(续)
获取关于事件的细节
调用处理器方法(如mouseDragged())时所带的事件参数可能会包含关于源事件的重要信息。为了确定可以获取关于某类事件的哪些细节时,你应当查看java.awt.event包中合适的类文档。
多监听者
AWT事件监听框架事实上允许同一个组件带有多个监听者。一般地,如果你想写一个程序,它基于一个事件而执行多个动作,把那些行为编写到处理器的方法里即可。然而,有时一个程序的设计要求同一程序的多个不相关的部分对于同一事件作出反应。这种情况是有可能的,例如,将一个上下文敏感的帮助系统加到一个已存在的程序中。
监听者机制允许你调用addXXXlistener方法任意多次,而且,你可以根据你的设计需要指定任意多个不同的监听者。事件发生时,所有被注册的监听者的处理器都会被调用。
事件Adapters(适配器)
你定义的Listener可以继承Adapter类,而且只需重写你所需要的方法。例如:
实现每个Listener接口的所有方法的工作量是非常大的,尤其是MouseListener接口和ComponentListener接口。
以MouseListener接口为例,它定义了如下方法:
- mouseClicked(MouseEvent)
- mouseEntered(MouseEvent)
- mouseExited(MouseEvent)
- mousePressed(MouseEvent)
- mouseReleased(MouseEvent)
为了方便起见,Java语言提供了Adapters类,用来实现含有多个方法的类。这些Adapters类中的方法是空的。
你可以继承Adapters类,而且只需重写你所需要的方法。例如:
1.import java.awt.*;
2.import java.awt.event.*;
3.
4.public class MouseClickHandler extends MouseAdapter {
5.
6.// We just need the mouseClick handler, so we use
7.// the Adapter to avoid having to write all the
8.// event handler methods
9.public void mouseClicked (MouseEvent e) {
10.// Do something with the mouse click...
11.}
12.}
匿名类
可以在一个表达式的域中,包含整个类的定义。这种方法定义了一个所谓的匿名类并且立即创建了实例。匿名类通常和AWT事件处理一起使用。例如:
1.import java.awt.*;
2.import java.awt.event.*;
3.public class AnonTest {
4.private Frame f;
5.private TextField tf;
6.
7.public static void main(String args[])
8.{
9.AnonTest obj = new AnonTest();
10.obj.go();
11.}
12.
匿名类(续)
1.public void go() {
2.f = new Frame("Anonymous classes example");
3.f.add(new Label("Click and drag the " +
4." mouse",BorderLayout.NORTH);
5.tf = new TextField (30);
6.f.add(tf,BorderLayout.SOUTH);
7.
8.f.addMouseMotionListener ( new
9.MouseMotionAdapter() {
10.public void mouseDragged (MouseEvent e) {
11.String s =
12."Mouse dragging: X = " + e.getX() +
13." Y = " + e.getY();
14.tf.setText (s);
15.}
16.} ); // <- note the closing parenthesis
17.
18.f.addMouseListener (new MouseClickHandler());
19.f.setSize(300, 200);
20.f.setVisible(true);
21.}
22.}
AWT组件库
JDK提供了能创建图形用户界面的许多组件。本模块考察这些AWT组件,以及非组件的AWT类,例如Color、Font和图形用户界面的打印。
AWT的特点
AWT提供了各种标准特性。本模块将介绍你可以使用的组件,并且概述了你需要知道的一些特殊情形。
首先将描述AWT的各个组件。它们用来创建用户界面。你需要知道所有图形用户界面组件,这样你就可以在创建你自己的界面时选择合适的组件。
AWT组件提供了控制界面外观的机制,包括用于文本显示的颜色和字体。
此外,AWT还支持打印。这个功能是在JDK1.1版中引入的。
按钮(Button)
你已经比较熟悉Button组件了。这个组件提供了“按下并动作”的基本用户界面。可以构造一个带文本标签的按钮,用来告诉用户它的作用。
Button b = new Button("Sample");
b.addActionListener(this);
add(b);
<wrapblock><shape id="_x0000_s1047" style="MARGIN-TOP: 0px; Z-INDEX: 22; MARGIN-LEFT: 63pt; WIDTH: 185.25pt; POSITION: absolute; HEIGHT: 76.5pt" type="#_x0000_t75"><imagedata o:title="mod10_awtcomp-5" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image022.gif"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>
任何实现了被注册为监听者的ActionListener接口的类,它的actionPerformed()方法将在一个按钮被鼠标点击“按下”时被调用。
public void actionPerformed(ActionEvent ae) {
System.out.println("Button press received.");
System.out.println("Button's action command is: " + ae.getActionCommand());
}
按钮被按下时调用的getActionCommand()方法在缺省情况下将返回标签字符串。用按钮的setActionCommand()方法改变动作命令和标签。
Button b = new Button("Sample");
b.setActionCommand("Action Command Was Here!");
b.addActionListener(this);
add(b);
注-SampleButton和ActionCommandButton的完整源代码可以在course_examples目录下找到。
复选框(Checkbox)
Checkbox组件提供一种简单的“开/关”输入设备,它旁边有一个文本标签。
Frame f = new Frame("Checkbox")
Checkbox one = new Checkbox("One", true);
Checkbox two = new Checkbox("Two", false);
Checkbox three = new Checkbox("Three", true);
one.addItemListener(this);
two.addItemListener(this);
three.addItemListener(this);
f.add(one);
f.add(two);
<wrapblock><shape id="_x0000_s1048" style="MARGIN-TOP: 22.65pt; Z-INDEX: 23; MARGIN-LEFT: 58.2pt; WIDTH: 206.25pt; POSITION: absolute; HEIGHT: 78.9pt" o:allowincell="f" type="#_x0000_t75"><imagedata o:title="mod10_awtcomp-7" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image024.png" croptop="6102f"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>
f.add(three);
选取或不选取(取消)一个复选框的事件将被送往ItemListener接口。所传递的ItemEvent包含getStatechange()方法,它根据实际情况返回ItemEvent.DESELECTED或ItemEvent.SELECTED。getItem()方法将受到影响的复选框作为一个表示这个复选框标签的String对象返回。
class Handler implements ItemListener {
public void itemStateChanged(ItemEvent ev) {
String state = "deselected";
if (ev.getStateChange() == ItemEvent.SELECTED){
state = "selected";
}
System.out.println(ev.getItem() + " " + state);
}
}
复选框组-单选框(Checkbox group-Radio Button)
复选框组提供了将多个复选框作为互斥的一个集合的方法,因此在任何时刻,这个集合中只有一个复选框的值是true。值为true的复选框就是当前被选中的复选框。你可以使用带有一个额外的CheckboxGroup参数的构造函数来创建一组中的每个复选框。正是这个CheckBoxGroup对象将各个复选框连接成一组。如果你这么做的话,那么复选框的外观会发生改变,而且所有和一个复选框组相关联的复选框将表现出“单选框”的行为。
Frame f = new Frame("Checkbox Group");
CheckboxGroup cbg = new CheckboxGroup();
Checkbox one = new Checkbox("One", false, cbg);
Checkbox two = new Checkbox("Two", false, cbg);
Checkbox three = new Checkbox("Three", true, cbg);
one.addItemListener(this);
two.addItemListener(this);
three.addItemListener(this);
f.add(one);
f.add(two);
<wrapblock><shape id="_x0000_s1049" style="MARGIN-TOP: 17.7pt; Z-INDEX: 24; LEFT: 0px; MARGIN-LEFT: 63.3pt; WIDTH: 188.25pt; POSITION: absolute; HEIGHT: 92.25pt; TEXT-ALIGN: left" o:allowincell="f" type="#_x0000_t75"><imagedata o:title="mod10_awtcomp-8" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image026.gif"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>
f.add(three);
下拉列表(Choice)
下拉列表组件提供了一个简单的“从列表中选取一个”类型的输入。例如:
Frame f = new Frame("Choice");
Choice c = new Choice();
c.add("First");
c.add("Second");
c.add("Third");
c.addItemListener(this);
<wrapblock><shape id="_x0000_s1050" style="MARGIN-TOP: 21pt; Z-INDEX: 25; LEFT: 0px; MARGIN-LEFT: 116.7pt; WIDTH: 87pt; POSITION: absolute; HEIGHT: 102.75pt; TEXT-ALIGN: left" o:allowincell="f" type="#_x0000_t75"><imagedata o:title="mod10_awtcomp-9" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image027.gif"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>
f.add(c);
点击下拉列表组件时,它会显示一个列表,列表中包含了所有加入其中的条目。注意所加入的条目是String对象。
<wrapblock><shape id="_x0000_s1051" style="MARGIN-TOP: 1.95pt; Z-INDEX: 26; MARGIN-LEFT: 108.9pt; WIDTH: 87pt; POSITION: absolute; HEIGHT: 102.15pt" o:allowincell="f" type="#_x0000_t75"><font size="3"><font face="Times New Roman"><imagedata o:title="mod10_awtcomp-10" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image028.png" croptop="8682f"></imagedata><wrap type="topAndBottom"></wrap></font></font></shape></wrapblock>
ItemListener接口用来观察下拉列表组件的变化,其细节和复选框的相同。
画布(Canvas)
画布提供了一个空白(背景色)的空间。除非你用setSize()显式地定义它的大小,否则它的大小就是0×0。
画布的空间可以用来绘图、显示文本、接收键盘或鼠标的输入。后面的模块将告诉你如何有效地在AWT中绘图。
<wrapblock><shape id="_x0000_s1052" style="MARGIN-TOP: 37.05pt; Z-INDEX: 27; MARGIN-LEFT: 89.55pt; WIDTH: 122.25pt; POSITION: absolute; HEIGHT: 127.2pt" o:allowincell="f" type="#_x0000_t75"><imagedata o:title="mod10_awtcomp-11" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image030.png" croptop="3787f"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>
通常,画布用来提供一个一般的绘图空间或者为客户组件提供工作区域。
画布可以“监听”所有适用于一个普通组件的事件。特别地,你还可能想增加KeyListener、MouseMotionListener和MouseListener对象,以允许某种方式对用户输入作出反应。
注-如要在画布中接收键盘事件,必须调用画布的requestFocus()方法。如果缺少这个步骤,一般是不能将击键“导向”画布的。键盘事件会送往另一个组件,或整个地丢失了。
下面是画布的一个范例。每击一次键,这个程序就改变一次画布的颜色。
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class MyCanvas extends Canvas
implements KeyListener {
int index;
Color colors[] = {Color.red, Color.green, Color.blue };
public void paint(Graphics g) {
g.setColor(colors[index]);
g.fillRect(0,0,getSize().width,getSize().height);
}
public static void main(String args[]) {
Frame f = new Frame("Canvas");
MyCanvas mc = new MyCanvas();
f.add(mc,BorderLayout.CENTER);
f.setSize(150, 150);
mc.requestFocus();
mc.addKeyListener(mc);
f.setVisible(true);
}
public void keyTyped(KeyEvent ev) {
index++;
if (index == colors.length) {
index =0;
}
repaint();
}
public void keyPressed(KeyEvent ev) {
}
public void keyReleased(KeyEvent ev) {
}
}
标签(Label)
一个标签对象显示一行静态文本。程序可以改变文本,但用户不能改变。标签没有任何特殊的边框和装饰。
Label l = new Label( " Hello " );
<wrapblock><shape id="_x0000_s1053" style="MARGIN-TOP: 16.35pt; Z-INDEX: 28; LEFT: 0px; MARGIN-LEFT: 101.1pt; WIDTH: 122.25pt; POSITION: absolute; HEIGHT: 46.35pt; TEXT-ALIGN: left" o:allowincell="f" type="#_x0000_t75"><imagedata o:title="mod10_awtcomp-12" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image034.png" croptop="14269f"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>
add(l);
标签通常不处理事件,但也可以按照和画布相同的方式来处理事件。也就是说,只有调用了requestFocus()方法后,才能可靠地检取击键事件。
文本域(Textfield)
文本域是一个单行的文本输入设备。例如:
TextField f = new TextField("Single line" " , 30);
f.addActionListener(this);
add(f);
<wrapblock><shape id="_x0000_s1054" style="MARGIN-TOP: 8.1pt; Z-INDEX: 29; MARGIN-LEFT: 49.8pt; WIDTH: 215.85pt; POSITION: absolute; HEIGHT: 54.75pt" o:allowincell="f" type="#_x0000_t75"><imagedata o:title="mod10_awtcomp-13" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image036.png" croptop="12379f" cropleft="14283f"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>
因为只允许有一行,所以当按下Enter或Return键时,ActionListener可以通过actionPerformed()知道这个事件。如果需要,还可以增加其他的组件监听者。
除了注册一个ActionListener,你还可以注册一个TextListener来接收关于个别击键的通知。它的回调方法是textValueChanged(TextEvent)。
文本区(TextArea)
文本区是一个多行多列的文本输入设备。你可以用setEditable(boolean)将它设置成只读的。文本区将显示水平和垂直的滚动条。
下面这个范例创建了一个4行×30字符的文本,最初它含有“Hello!”。
TextArea t = new TextArea( " Hello! " , 4, 30);
t.addTextListener(this);
<wrapblock><shape id="_x0000_s1055" style="MARGIN-TOP: 21.45pt; Z-INDEX: 30; LEFT: 0px; MARGIN-LEFT: 31.95pt; WIDTH: 315.6pt; POSITION: absolute; HEIGHT: 94.35pt; TEXT-ALIGN: left" o:allowincell="f" type="#_x0000_t75"><imagedata cropbottom="-359f" o:title="mod10_awtcomp-14" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image038.png" croptop="9426f" cropleft="7600f"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>
add(t);
你用addTexListener指定的监听者将以和文本域相同的方式接收到关于击键的通知。
你可以给文本区增加一般的组件监听者,然而,由于文本是多行的,按下Enter键将导致把另一个字符送入缓冲。如果你需要识别“输入的结束”,你可以在文本区旁放置一个“应用”或“确认”按钮,以便用户指明“输入的结束”。
文本组件(Text Components)
文本区和文本域的文档都分为两个部分。如果你查找一个称为文本组件的类,你会找到若干个文本区和文本域共有的方法。例如,文本区和文本域都是文本组件的子类。
你已经知道文本区和文本域类的构造函数允许你指定显示所用的列数。记住所显示的组件大小是由布局管理器决定的,因此这些设置可能被忽略。进而,列数是按所用字体的平均宽度计算的。如果使用一种空间比例字体,实际显示的字符数可能相差很大。
由于文本组件实现了TextListener,诸如文本域、文本区及其它子类都有对击键事件的内置支持。
列表(list)
一个列表将各个文本选项显示在一个区域中,这样就可以在同时看到若干个条目。列表可以滚动,并支持单选和多选两种模式。例如:
List l = new List(4, true);
1.add("Hello");
1.add("there");
1.add("how");
1.add("are");
<shape id="_x0000_i1025" style="WIDTH: 119.25pt; HEIGHT: 93.75pt" type="#_x0000_t75" fillcolor="window"><imagedata o:title="mod10_awtcomp-16" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image040.gif"></imagedata></shape>
构造函数的数值参数定义了按可见列计算的列表高度。这个值也可能被布局管理器覆盖。一个值为true的布尔型参数表明这个列表允许用户作多个选择。
<shape id="_x0000_i1026" style="WIDTH: 118.5pt; HEIGHT: 98.25pt" type="#_x0000_t75" o:ole="" fillcolor="window"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image041.gif"></imagedata></shape>
选取或取消一个条目时,AWT将一个ItemEvent的实例送往列表。用户双击滚动列表中的一个条目时,单选模式和多选模式的列表都会产生一个ActionEvent。根据每个平台的约定来决定列表中的条目是否被选取。对于UNIX/Motif环境,单击会加亮列表中的一个条目,只有双击才会触发列表事件(选取条目)。
对话框(Dialog)
<wrapblock><shape id="_x0000_s1056" style="MARGIN-TOP: 52.35pt; Z-INDEX: 31; MARGIN-LEFT: 98.55pt; WIDTH: 118.5pt; POSITION: absolute; HEIGHT: 52.95pt" o:allowincell="f" type="#_x0000_t75"><font size="3"><font face="Times New Roman"><imagedata o:title="mod10_awtcomp-18" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image042.png" croptop="9111f"></imagedata><wrap type="topAndBottom"></wrap></font></font></shape></wrapblock>
对话框组件与一个框架相关联。它是一个带有一些装饰的自由窗口。它与框架的区别在于所提供的一些装饰,而且你可以生成一个“模式”对话框,它在被关闭前将存储所有窗口的输入。
对话框可以是无模式和模式的。对于无模式对话框,用户可以同时与框架和对话框交互。“模式”对话框在被关闭前将阻塞包括框架在内的其他所有应用程序的输入。
由于对话框是窗口的子类,所以它的缺省布局管理器是Border Layout。
Dialog d = new Dialog(f, "Dialog", false);
d.add(new Label("Hello, I'm a Dialog",Border.Layout.CENTER));
d.pack();
对话框在创建时通常是不可见的。通常在对按下按钮等用户输入作出反应时,才显示对话框。
public void actionPerformed(ActionEvent ev) {
d.setVisible(true);
}
要隐藏对话框,你必须调用setVisible(false)。典型的做法是对它添加一个WindowListener,并等待对那个监听者的windowClosing()调用。这和处理一个框架的关闭是平行的。
文件对话框(File Dialog)
文件对话框是文件选择设备的一个实现。它有自己的自由窗口,以及窗口元素,并且允许用户浏览文件系统,以及为以后的操作选择一个特定的文件。例如:
FileDialog d = new FileDialog(parentFrame, "FileDialog");
d.setVisible(true);// block here until OK selected
String fname = d.getFile();
<shape id="_x0000_i1027" style="WIDTH: 337.5pt; HEIGHT: 315pt" type="#_x0000_t75" fillcolor="window"><imagedata o:title="mod10_awtcomp-19" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image044.gif"></imagedata></shape>
通常并不需要处理FileDialog的事件。调用setVisible(true)将阻塞事件,直至用户选择OK,这时会请求用户选择的文件名。这个信息将作为一个String返回。
滚动面板(Scroll Pane)
滚动面板提供了一种不能作为自由窗口的通用容器。它应当总是和一个容器相关联(例如,框架)。它提供了到一个更大的区域的视窗以及操纵这个视窗的滚动条。例如:
Frame f = new Frame("ScrollPane");
Panel p = new Panel();
ScrollPane sp = new ScrollPane();
p.setLayout(new GridLayout(3, 4));
sp.add(p);
f.add(sp, "Center");
f.setSize(200, 200);
<wrapblock><shape id="_x0000_s1057" style="MARGIN-TOP: 16.5pt; Z-INDEX: 32; LEFT: 0px; MARGIN-LEFT: 117.6pt; WIDTH: 159.75pt; POSITION: absolute; HEIGHT: 165.3pt; TEXT-ALIGN: left" o:allowincell="f" type="#_x0000_t75"><imagedata o:title="mod10_awtcomp-20" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image045.png" croptop="5602f"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>
f.serVisible(true);
滚动面板创建和管理滚动条,并持有一个组件。你不能控制它所用的布局管理器。你可以将一个面板加入到滚动面板中,配置面板的布局管理器,并在那个面板中放置你的组件。
通常,你不处理滚动面板上的事件;这些事件通过滚动面板所包含的组件进行处理。
菜 单
菜单与其他组件有一个重要的不同:你不能将菜单添加到一般的容器中,而且不能使用布局管理器对它们进行布局。你只能将菜单加到一个菜单容器中。然而,你可以将一个Jmenuswing组件加到一个Jcontainer中。你可以通过使用setMenuBar()方法将菜单放到一个框架中,从而启动一个菜单“树”。从那个时刻之后,你可以将菜单加到菜单条中,并将菜单或菜单项加到菜单中。
弹出式菜单是一个例外,因为它们可以以浮动窗口形式出现,因此不需要布局。
帮助菜单
菜单条的一个特性是你可以将一个菜单指定为帮助菜单。这可以用setHelpMenu(Menu)来做到。要作为帮助菜单的菜单必须加入到菜单条中;然后它就会以和本地平台的帮助菜单同样的方式被处理。对于X/Motif类型的系统,这涉及将菜单条放置在菜单条的最右边。
菜单条(MenuBar)
一个菜单条组件是一个水平菜单。它只能加入到一个框架中,并成为所有菜单树的根。在一个时刻,一个框架可以显示一个菜单条。然而,你可以根据程序的状态修改菜单条,这样在不同的时刻就可以显示不同的菜单。例如:
Frame f = new Frame("MenuBar");
MenuBar mb = new MenuBar();
f.setMenuBar(mb);
<shape id="_x0000_i1028" style="WIDTH: 122.25pt; HEIGHT: 140.25pt" type="#_x0000_t75" fillcolor="window"><imagedata o:title="mod10_awtcomp-22" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image047.gif"></imagedata></shape>
菜单条不支持监听者。作为普通菜单行为的一部分,在菜单条的区域中发生的预期事件会被自动处理。
菜单
菜单组件提供了一个基本的下拉式菜单。它可以加入到一个菜单条或者另一个菜单中。例如:
MenuBar mb = new MenuBar();
Menu m1 = new Menu("File");
Menu m2 = new Menu("Edit");
Menu m3 = new Menu("Help");
mb.add(m1);
mb.add(m2);
mb.setHelpMenu(m3);
f.setMenuBar(mb);
<shape id="_x0000_i1029" style="WIDTH: 213.75pt; HEIGHT: 141.75pt" type="#_x0000_t75" fillcolor="window"><imagedata o:title="mod10_awtcomp-23" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image048.gif"></imagedata></shape>
注-这里显示的菜单是空的,这正是File菜单的外观。
你可以将一个ActionListener加入到菜单对象,但这种做法是罕见的。正常情况下,菜单用来显示和控制菜单条,这将在后面讨论。
菜单项(MenuItem)
菜单项组件是菜单树的文本“叶”结点。它们通常被加入到菜单中,以构成一个完整的菜单。例如:
Menu m1 = new Menu("File");
MenuItem mi1 = new MenuItem("New");
MenuItem mi2 = new MenuItem("Load");
MenuItem mi3 = newMenuItem ("Save");
MenuItem mi4 = new MenuItem("Quit");
mi1.addActionListener(this);
mi2.addActionListener(this);
mi3.addActionListener(this);
m1.add(mi1);
m1.add(mi2);
m1.addSeparator();
m1.add(mi3);
通常,将一个ActionListener加入到一个菜单项对象中,以提供菜单的行为。
复选菜单项(CheckboxMenuItem)
复选菜单项是一个可复选的菜单项,所以你可以在菜单上有选项(“开”或“关”)。例如:
Menu m1 = new Menu("File");
MenuItem mi1 = new MenuItem("Save");
CheckboxMenuItem mi2 =
new CheckboxMenuItem("Persistent");
mi1.addItemListener(this);
mi2.addItemListener(this);
m1.add(mi1);
m1.add(mi2);
<shape id="_x0000_i1030" style="WIDTH: 197.25pt; HEIGHT: 140.25pt" type="#_x0000_t75" fillcolor="window"><imagedata o:title="mod10_awtcomp-25" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image051.gif"></imagedata></shape>
应当用ItemListener接口来监视复选菜单。因此当复选框状态发生改变时,就会调用itemStateChanged()方法。
弹出式菜单(PopupMenu)
弹出式菜单提供了一种独立的菜单,它可以在任何组件上显示。你可以将菜单条目和菜单加入到弹出式菜单中去。
例如:
Frame f = new Frame("PopupMenu");
Button b = new Button("Press Me");
PopupMenu p = new PopupMenu("Popup");
MenuItem s = new MenuItem("Save");
MenuItem l = new MenuItem("Load");
b.addActionListener(this);
f.add(b,Border.Layout.CENTER);
p.add(s);
p.add(l);
f.add(p);
为了显示弹出式菜单,你必须调用显示方法。显示需要一个组件的引用,作为x和y坐标轴的起点。通常,你要为此使用组件的触发器。在上面这个范例中,触发器是Button b。
弹出式菜单(续)
public void actionPerformed(ActionEvent ev) {
p.show(b, 10, 10); // display popup
// at (10,10) relative to b
}
<shape id="_x0000_i1031" style="WIDTH: 118.5pt; HEIGHT: 120pt" type="#_x0000_t75" fillcolor="window"><imagedata o:title="mod10_awtcomp-26" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image052.gif"></imagedata></shape>
控制外观
你可以控制在AWT组件中所显示的文本的前景背景颜色、背景颜色和字体。
颜色
有两个方法用来设置组件的颜色:
- setForeground()
- getForeground()
这两个方法都带有一个参数,参数是java.awt.Color类的实例。你可以使用常数颜色,如Color.red,Color.blue等。所有预定义的颜色列在Color类的文档中。
此外,你可以创建一个特定的颜色,例如:
int r = 255, g = 255, b = 0;
Color c = new Color(r, g, b);
上述构造函数根据指定的红色、绿色和蓝色的亮度(它们的范围都是0~255)来创建一个颜色。
字体
在组件中显示时所用的字体可以用setFont()方法来设置。这个方法的参数应当是java.awt.Font类的实例。
没有为字体定义常数,但你可以根据字体的名称,风格,磅值来创建一个字体。
Font f = new Font("TimesRoman", Font.PLAIN, 14);
有效的字体名称包括:
- Dialog
- Helvetica
- TimesRoman
- Courier
可以通过调用Toolkit对象的getFontlist()方法来得到完整的列表。GetToolkit()方法是用来在显示toolkit后获得toolkit的。还有另外一种方法,你可以使用缺省的toolkit,它可以通过调用Toolkit.getDefaultToolkit()来获得。
字体风格常数实际上是int值,即:
- Font.BOLD
- Font.ITALIC
- Font.PLAIN
- Font.BOLD+ Font.ITALIC
磅值必须使用int值来指定。
打 印
在JDK1.2中的打印处理方式和屏幕显示是几乎平行的。会获取一种特殊的java.awt.Graphics对象,这样任何送往图形的绘图指令实际上会最终被送往打印机。
JDK1.2打印系统允许使用本地打印机的控制约定。选择一个打印操作时,用户会看到一个打印机选择对话框。然后用户可以设置各种选项,如:纸张大小、打印质量和使用哪个打印机。例如:
Frame f = new Frame("Print test");
Toolkit t = f.getToolkit();
PrintJob job = t.getPrintJob(f, "MyPrintJob", null);
Graphics g = job.getGraphics();
这些代码创建了一个Graphics对象,它将连接到用户选择的打印机。
你可以使用任何Graphics类绘图方法来使用打印机。另一种方法,如下所示,你可以让一个组件在图形上绘制自身。
f.printComponents(g);
print()方法以这种方式让组件绘制自身,但它只和所要求的组件相关联。如果是容器,你可以使用printcomponents()方法使容器和它所包含的全部组件绘制在打印机上。
g.dispose();
job.end();
创建输出页之后,使用dispose()方法将页面提交给打印机作业对象。
完成作业以后,调用打印作业对象的end()方法。这表明打印作业已经完成,并允许打印假脱机系统运行作业,以及释放打印机以供其他作业使用。
Java基础类
JDK1.2提供了Java基础类,其中的一部分就是Swing。Swing是构筑在AWT上层的一些组件的集合(为了保证平台独立性,它是用100%的纯Java编写)。本模块介绍了JFC和Swing图形用户界面的实现。
介 绍
Java基础类是关于GUI组件和服务的完整集合,它大大简化了健壮Java应用程序的开发和实现。
JFC,作为JDK1.2的一个有机部分,主要包含5个API:AWT,Java2D,Accessibility,Drag & Drop,Swing。它提供了帮助开发人员设计复杂应用程序的一整套应用程序开发包。
正如前面那些模块中所讨论的那样,AWT组件为各类Java应用程序提供了多种GUI工具。
Java2D是一图形API,它为Java应用程序提供了一套高级的有关二维(2D)图形图像处理的类。Java2D API扩展了java.awt和java.awt. image类,并提供了丰富的绘图风格,定义复杂图形的机制和精心调节绘制过程的方法和类。这些API使得独立于平台的图形应用程序的开发更加简便。
Accessibility API提供了一套高级工具,用以辅助开发使用非传统输入和输出的应用程序。它提供了一个辅助的技术接口,如:屏幕阅读器,屏幕放大器,听觉文本阅读器(语音处理)等等。
Drag & Drop技术提供了Java和本地应用程序之间的互操作性,用来在Java应用程序和不支持Java技术的应用程序之间交换数据。
JFC模块的重点在Swing。Swing用来进行基于窗口的应用程序开发,它提供了一套丰富的组件和工作框架,以指定GUI如何独立于平台地展现其视觉效果。
Swing介绍
Swing提供了一整套GUI组件,为了保证可移植性,它是完全用Java语言编写的。
可插的外观和感觉
可插的外观和感觉使得开发人员可以构建这样的应用程序:它们可以在任何平台上执行,而且看上去就象是专门为那个特定的平台而开发的。一个在Windows环境中执行的程序,似乎是专为这个环境而开发的;而同样的程序在Unix平台上执行,它的行为又似乎是专为Unix环境开发的。
开发人员可以创建自己的客户化Swing组件,带有他们想设计出的任何外观和感觉。这增加了用于跨平台应用程序和Applet的可靠性和一致性。一个完整应用程序的GUI可以在运行时刻从一种外观和感觉切换到另一种。
Swing的体系结构
与AWT比较,Swing提供了更完整的组件,引入了许多新的特性和能力。Swing API是围绕着实现AWT各个部分的API构筑的。这保证了所有早期的AWT组件仍然可以使用。AWT采用了与特定平台相关的实现,而绝大多数Swing组件却不是这样做的,因此Swing的外观和感觉是可客户化和可插的。
<shape id="_x0000_i1032" style="WIDTH: 301.5pt; HEIGHT: 180pt" type="#_x0000_t75" fillcolor="window"><imagedata o:title="mod11_jfc-7" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image053.png"></imagedata></shape>
上图显示了JFC各个部分之间的相互关系。Java2D,Accessibility,Drag & Drop,和Accessibility API是AWT和JFC的一部分,但它们不属于Swing。这是因为,这些组件使用了一些本地代码,而Swing却不是这样的。
Swing是围绕着一个称为JComponent的新组件构建的,而JComponent则由AWT的容器类扩展而来。
Swing的层次结构
下图说明了Swing组件的层次结构:
<shape id="_x0000_i1033" style="WIDTH: 366pt; HEIGHT: 306.75pt" type="#_x0000_t75" fillcolor="window"><imagedata o:title="mod11_jfc-8" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image055.gif"><font face="Times New Roman" size="3"></font></imagedata></shape>
Swing GUI使用两种类型的类,即GUI类和非GUI支持类。GUI类是可视的,它从JComponent继承而来,因此称为“J”类。非GUI类为GUI类提供服务,并执行关键功能;因此它们不产生任何可视的输出。
注-Swing的事件处理类是非GUI类的一例。
Swing组件
Swing组件主要为文本处理、按钮、标签、列表、pane、组合框、滚动条、滚动pane、菜单、表格和树提供了组件。其中一些组件如下所示:
<shape id="_x0000_i1034" style="WIDTH: 285pt; HEIGHT: 84pt" type="#_x0000_t75" fillcolor="window"><imagedata o:title="mod11_jfc-10" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image058.gif"><font face="Times New Roman"></font></imagedata></shape>
<wrapblock><shape id="_x0000_s1058" style="MARGIN-TOP: 15.6pt; Z-INDEX: 33; LEFT: 0px; MARGIN-LEFT: 37.05pt; WIDTH: 285pt; POSITION: absolute; HEIGHT: 84pt; TEXT-ALIGN: left" o:allowincell="f" type="#_x0000_t75"><font face="Times New Roman"><imagedata o:title="mod11_jfc-11" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image059.gif"></imagedata><wrap type="topAndBottom"></wrap></font></shape></wrapblock>
JApplet JButton
JComboBox JOptionPane
<shape id="_x0000_i1035" style="WIDTH: 285pt; HEIGHT: 70.5pt" type="#_x0000_t75" fillcolor="window"><imagedata o:title="mod11_jfc-12" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image060.gif"></imagedata></shape>
JList JLabel
<shape id="_x0000_i1036" style="WIDTH: 271.5pt; HEIGHT: 82.5pt" type="#_x0000_t75" fillcolor="window"><imagedata o:title="mod11_jfc-13" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image061.gif"></imagedata></shape>
JScrollPane JTable
<shape id="_x0000_i1037" style="WIDTH: 271.5pt; HEIGHT: 57pt" type="#_x0000_t75" fillcolor="window"><imagedata o:title="mod11_jfc-14" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image062.gif"></imagedata></shape>
JScrollBar JSlider
<shape id="_x0000_i1038" style="WIDTH: 271.5pt; HEIGHT: 90pt" type="#_x0000_t75" fillcolor="window"><imagedata o:title="mod11_jfc-15" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image063.gif"></imagedata></shape>
JTooltip JTree
基本的Swing应用程序
HelloSwing应用程序的输出产生下图所示的窗口:
<shape id="_x0000_i1039" style="WIDTH: 210pt; HEIGHT: 122.25pt" type="#_x0000_t75" fillcolor="window"><imagedata o:title="mod11_jfc-16" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image064.gif"></imagedata></shape>
每次用户点击按钮时,标签就会更新。
HelloSwing
1.import java.awt.*;
2.import java.awt.event.*;
3.import com.sun.java.swing.*;
4.import java.awt.accessibility.*;
5.
6.public class HelloSwing implements ActionListener {
7.private JFrame jFrame;
8.private JLabel jLabel;
9.private JPanel jPanel;
10.private JButton jButton;
11.private AccessibleContext accContext;
12.
13.private String labelPrefix =
14."Number of button clicks: ";
15.private int numClicks = 0;
16.
17.public void go() {
18.
19.// Here is how you can set up a particular
20.// lookAndFeel. Not necessary for default.
21.//
22.// try {
23.// UIManager.setLookAndFeel(
24.// UIManager.getLookAndFeel());
25.// } catch (UnsupportedLookAndFeelException e) {
26.// System.err.println("Couldn't use the " +
27.// "default look and feel " + e);
28.// }
29.
30.jFrame = new JFrame("HelloSwing");
31.jLabel = new JLabel(labelPrefix + "0");
32.
33.jButton = new JButton("I am a Swing button!");
34.
35.// Create a shortcut: make ALT-A be equivalent
36.// to pressing mouse over button.
37.jButton.setMnemonic('i');
38.
39.jButton.addActionListener(this);
40.
HelloSwing(续)
1.// Add support for accessibility.
2.accContext = jButton.getAccessibleContext();
3.accContext.setAccessibleDescription(
4."Pressing this button increments " +
5."the number of button clicks");
6.
7.// Set up pane.
8.// Give it a border around the edges.
9.jPanel = new JPanel();
10.jPanel.setBorder(
11.BorderFactory.createEmptyBorder(
12.30,30,10,30));
13.
14.// Arrange for compts to be in a single column.
15.jPanel.setLayout(new GridLayout(0, 1));
16.
17.// Put compts in pane, not in JFrame directly.
18.jPanel.add(jButton);
19.jPanel.add(jLabel);
20.jFrame.setContentPane(jPanel);
21.
22.// Set up a WindowListener inner class to handle
23.// window's quit button.
24.WindowListener wl = new WindowAdapter() {
25.public void windowClosing(WindowEvent e) {
26.System.exit(0);
27.}
28.};
29.jFrame.addWindowListener(wl);
30.
31.jFrame.pack();
32.jFrame.setVisible(true);
33.}
34.
HelloSwing(续)
1.// Button handling.
2.public void actionPerformed(ActionEvent e) {
3.numClicks++;
4.jLabel.setText(labelPrefix + numClicks);
5.}
6.
7.public static void main(String[] args) {
8.
9.HelloSwing helloSwing = new HelloSwing();
10.helloSwing.go();
11.}
12.}
导入Swing包
语句行import com.sun.java.swing.*装入整个Swing包,它包括了标准Swing组件和功能。
选择外观和感觉
Hello Swing的第22-28行给定了应用程序外观和感觉的格式。getLookAndFeel()方法返回在Windows环境中的外观和感觉。在运行Solaris操作系统的机器上,这个方法则返回一个公共桌面环境(CDE)/Motif的外观和感觉。因为都是缺省值,所以对本例来说,这些行都不是必需的。
建立窗口
Swing程序用JFrame对象实现了它们的窗口。JFrame类是AWT Frame类的一个子类。它还加入了一些Swing所独有的特性。Hello Swing中,处理JFrame的代码如下:
public HelloSwing() {
JFrame jFrame;
JPanel jPanel;
.....
jFrame = new JFrame("HelloSwing");
jPanel = new JPanel();
.......
jFrame.setContentPane(jPanel);
这段代码与使用Frame的代码十分相似。唯一的区别在于,你不能将组件加入到JFrame中。你可以或者将组件加入到JFrame的content pane中,或者提供一个新的content pane。
一个content pane是一个包含除菜单条(如果有的话)外所有框架的可视组件的容器。要获得一个JFrame的content pane,可使用getContentPane()方法。要设置它的content pane(如前面本例所示),则可使用set ContentPane()方法。
建立Swing组件
Hello Swing程序显式地实例化了4个组件:JFrame,JButton,JLabel和JPanel。Hello Swing用第33-45行中的代码来初始化JButton。
第33行创建了按钮。第37行将ACT-I键组合设置为快捷键,用来模拟按钮的点击。第39行为点击注册了一个事件处理器。第41-45行描述了一个按钮,使得辅助技术可以提供有关按钮功能的信息。
第49-59行初始化了JPanel。这些代码创建了JPanel对象,设置它的边框,并将它的布局管理器设置为单列地放置panel的内容。最后,将一个按钮和一个标签加入到Panel中。Hello Swing中的Panel使用了一个不可见的边框,用来在它周围放入额外的填充。
支持辅助技术
Hello Swing.java中唯一支持辅助技术的代码是:
accContext = jButton.getAccessibleContext();
accContext.setAccessibleDescription(
"Pressing this button increments " +
" the number of button clicks.");
下列信息集也可由辅助技术使用:
jButton = new JButton("I'm a Swing button!");
jLabel = new JLabel(labelPrefix + "0"); jLabel.setText(labelPrefix + numClicks);
在JFrame,JButton,JLabel和其他所有组件中,都有内建的Accessibility支持。辅助技术可以很容易地获得文本,甚至与一组件某特定部分相关的文本。
构造一个Swing GUI
Swing包定义了两种类型的组件:
顶层容器(JFrame,JApplet,JWindow,和JDialog)
轻质组件(其他的J…,如JButton,JPanel和JMenu)
顶层容器定义了可以包含轻质组件的框架。特别地,一个顶层Swing容器提供了一个区域,轻质组件可在这个区域中绘制自身。顶层容器是它们对应的重质AWT组件的Swing子类。这些Swing容器依靠它们的AWT超类的本地方法与硬件进行适当的交互。
通常,每个Swing组件在其容器层次结构中都应当有一个位于组件上面的顶层Swing容器。例如,每个包含Swing组件的Applet都应作为JApplet(而它自身又是java.applet.Applet的一个子类)的子类来实现。相似地,每个包含Swing组件的主窗口都应用JFrame来实现。典型地,如果你在使用Swing组件,你将只能使用Swing组件和Swing容器。
Swing组件可以加入到一个与顶层容器关联的content pane中,但绝不能直接加入到顶层容器中。content pane是一个轻质Swing组件,如JPanel。
下面是一个典型Swing程序的GUI容器层次结构图,这个程序实现了
一个包含2个按钮,一个文本域和一个列表:
Jframe ( a top-level Swing container)
x
¼¼
x
content pane
x
` +---------+-------+
x x x
JButton JButton JPanel
x
+---------+
x x
JTextField JList
下面是关于同样的GUI的另一个容器层次结构,只是在这里,GUI是在浏览器中运行的一个Applet。
¼
x
¼
x
JApplet ( a top-level Swing container)
x
content pane
x
` +---------+-------+
x x x
JButton JButton JPanel
x
+---------+
x x
JTextField JList
下面是构造如上图所示的GUI层次结构的代码:
1.import com.sun.java.swing.*;
2.import java.awt.*;
3.
4.public class SwingGUI {
5.
6.JFrame topLevel;
7.JPanel jPanel;
8.JTextField jTextField;
9.JList jList;
10.
11.JButton b1;
12.JButton b2;
13.Container contentPane;
14.
15.Object listData[] = {
16.new String("First selection"),
17.new String("Second selection"),
18.new String("Third selection")
19.};
20.
21.public static void main (String args[]) {
22.SwingGUI swingGUI = new SwingGUI();
23.swingGUI.go();
24.}
25.
26.public void go() {
27.topLevel = new JFrame("Swing GUI");
28.
29.// Set up the JPanel, which contains the text field
30.// and list.
31.jPanel = new JPanel();
32.jTextField = new JTextField(20);
33.jList = new JList(listData);
34.
35.contentPane = topLevel.getContentPane();
36.contentPane.setLayout(new BorderLayout());
37.
38.b1 = new JButton("1");
39.b2 = new JButton("2");
40.contentPane.add(b1, BorderLayout.NORTH);
41.contentPane.add(b2, BorderLayout.SOUTH);
42.
43.jPanel.setLayout(new FlowLayout());
44.jPanel.add(jTextField);
45.jPanel.add(jList);
46.contentPane.add(jPanel, BorderLayout.CENTER);
47.
48.topLevel.pack();
49.topLevel.setVisible(true);
50.}
51.}
JComponent类
所有Swing都作为JComponent的子类来实现,而JComponent类又是从Container类继承而来。Swing组件从JComponent继承了如下功能:
边框
你可以用setBorder()方法来指定在组件周围显示的边框。还可用一个EmptyBorder的实例来指定一个组件在其周围留有一定的额外空间。
双缓冲
双缓冲可以改善一个频繁被改变的组件的外观。现在你不需要编写双缓冲代码――Swing已为你提供了。缺省情况下,Swing组件是双缓冲的。
提示框
通过用setToolTipText()方法来指定一个字符串,你可以提供给用户有关某个组件的帮助信息。当光标暂停在组件上时,所指定的字符串就会在组件附近的一个小窗口中显示出来。
键盘导航
使用registerKeyboardAction()方法,你可以让用户以键盘代替鼠标来操作GUI。用户为启动一个动作所必须按下的修饰键与字符的组合,由一个KeyStroke对象来表示。
应用程序范围的可插式外观和感觉
每个Java应用程序在运行时刻有一个GUIManager对象,它用于确定运行时刻Swing组件的外观和感觉。由于安全性的限制,你可以通过调用UIManager.setLookAndFeel()方法选择所有Swing组件的外观和感觉。在你所看见的东西背后,每个JComponent对象都有一个对应的ComponentGUI对象,它用来执行所有关于该JComponent的绘制、事件处理、大小判定等任务。