javaSE 基础笔记之Swing和GUI事件处理

第十一章 Swing GUI事件处理             

 

 

学习目标:

²        理解 Java 基础类基本概念

²        了解 Swing

²        掌握 Swing 组件

²        理解 Jcomponent

²        掌握 GUI 事件的基本概念

²        掌握如何编写代码来处理 GUI 事件

²        熟悉 Adapter 类的编写和应用

²        掌握如何编写 Swing 程序

 

一:Java 基础类 JFC 的基本概念  

                                                   

    Java基础类是关于GUI 组件和服务的完整集合,它大大简化了健壮Java 应用程序的开发和实现。 JFC作为JDK1.2的一个有机部分, 主要包含5 API AWT JavaD AccessibilityDrag & DropSwing。它提供了帮助开发人员设计复杂应用程序的一整套应用程序开发包。 

    正如前面那些模块中所讨论的那样,AWT 组件为Java 应用程序提供了多种 GUI工具。

    JavaD 是一图形API,它为Java应用程序提供了一套高级的有关二维(2D)图形图像处理的类。JavaD API 扩展了 java.awt java.awt. image 类,并提供了丰富的绘图风格,定义复杂图形的机制和精心调节绘制过程的方法和类。 这些 API 使得独立于平台的图形应用程序的开发更加简便。

    Accessibility  API提供了一套高级工具,用以辅助开发使用非传统输入和输出的应用程序。它提供了一个辅助的技术接口,如:屏幕阅读器,屏幕放大器,听觉文本阅读器(语音处理)等等。

    Drag & Drop 技术提供了Java 和本地应用程序之间的互操作性,用来在 Java 应用程序和不支持 Java 技术的应用程序之间交换数据。

    JFC 模块的重点在 SwingSwing 用来进行基于窗口的应用程序开发,它提供了一套丰富的组件和工作框架,以指定GUI如何独立于平台地展现其视觉效果。

 

二:Swing                                                                                       

1 Swing介绍                                                                          

      Swing 提供了一整套GUI 组件,为了保证可移植性,它是完全用 Java语言编写的。

所有Swing都作为JComponent的子类来实现, JComponent类又是从 Container 类继承而来。Swing 组件从JComponent继承了如下功能:

边框你可以用setBorder()方法来指定在组件周围显示的边框。还可用一个 EmptyBorder 实例来指定一个组件在其周围留有一定的额外空间。

 

双缓冲

双缓冲可以改善一个频繁被改变的组件的外观。现在你不需要编写双缓冲代码――Swing已为你提供了。缺省情况下,Swing组件是双缓冲的。

 

提示框

setToolTipText()方法来指定一个字符串,你可以提供给用户有关某个组件的帮助信息。当光标暂停在组件上时,所指定的字符串就会在组件附近的一个小窗口中显示出来。 

 

键盘导航

使用registerKeyboardAction()方法,你可以让用户以键盘代替鼠标来操作GUI。用户

为启动一个动作所必须按下的修饰键与字符的组合,由一个 KeyStroke对象来表示。

 

应用程序范围的可插式外观和感觉

每个 Java 应用程序在运行时刻有一个 GUIManager 对象,它用于确定运行时刻 Swing组件的外观和感觉。由于安全性的限制,你可以通过调用 UIManager.setLookAndFeel()法选择所有 Swing 组件的外观和感觉。在你所看见的东西背后,每个 JComponent 对象都有一个对应的ComponentGUI对象,它用来执行所有关于该JComponent 的绘制、事件处理、大小判定等任务。

    可插的外观和感觉使得开发人员可以构建这样的应用程序:它们可以在任何平台上执行,而且看上去就象是专门为那个特定的平台而开发的。开发人员可以创建自己的客户化Swing组件, 带有他们想设计出的任何外观和感觉。这增加了用于跨平台应用程序和Applet的可靠性和一致性。一个完整应用程序的 GUI 可以在运行时刻从一种外观切换到另一种。

 

2Swing的体系结构                                                                      

    AWT 比较,Swing 提供了更完整的组件,引入了许多新的特性和能力。Swing API围绕着实现AWT各个部分的API构筑的。这保证了所有早期的 AWT 组件仍然可以使用。AWT采用了与特定平台相关的实现,而绝大多数 Swing组件却不是这样做的,因此Swing的外观和感觉是可客户化和可插的。

   

    上图显示了 JFC 各个部分之间的相互关系。JavaDAccessibilityDrag & Drop,和Accessibility API AWTJFC的一部分,但它们不属于 Swing。这是因为,这些组件使用了一些本地代码,而Swing却不是这样的。

下图说明了Swing 组件的层次结构:

 

 

    

Swing GUI 使用两种类型的类,即 GUI 类和非 GUI 支持类。GUI 类是可视的,它从JComponent继承而来,因此称为“J”类。非 GUI类为 GUI 类提供服务,并执行关键功能;因此它们不产生任何可视的输出。

 

三:Swing 组件                                                                                  

    Swing 组件主要为文本处理、按钮、标签、列表、pane、组合框、滚动条、滚动 pane菜单、表格和树提供了组件。其中一些组件如下所示:

    

        JApplet               Jbutton

      

       JComboBox           JOptionPane

      

        JList                  JLabel

    

      JScrollPane             JTable

      

        JScrollBar             JSlider

      

        JTooltip         JTree

 

      

四:GUI事件的处理                                                                         

1Java事件和事件处理                                                                  

    你肯定已经注意到,以前的 Java GUI 应用程序不能正常地与用户进行交互,用户鼠标点击Button按钮时程序并无任何动作, 这远不能满足我们的需要。如何让Java  GUI动起来、对用户的操作做出实时处理,而不是作为静态画面来欣赏是本节学习的目标。 JDK1.1 开始,Java 采用了一种名为“事件授权模型”(Event Delegation Model的事件处理机制,以支持 Java  GUI程序与用户的实时交互。与 Java异常处理机制类似,事件处理机制的基本原理如下:

    事先定义多种事件类型(见图9-12 ,用以描述发生了什么事情(用户在 GUI组件上进行的操作)。再约定各种 GUI 组件在与用户交互时,会触发相应的事件,即自动创建事件类对象并提交给Java 运行时系统。系统接收到事件类对象后,立即将其发送给专门的对象,该对象调用其事件处理方法,处理事件对象,实现程序规定的逻辑功能。

 

 

 

这一模型涉及到的主要概念有:

事件(Event----  一个对象,它描述了发生什么事情。

事件源(Event source----产生事件的组件对象,如按钮。

事件处理方法(Event handler----能够接收、解析和处理事件类对象、实现和用户交互

的方法。

事件监听器(Event listener----调用事件处理方法的对象。

    为使学习者更好地理解这一机制,举一个具有现实意义的例子:总统专机 A1(事件源)受到恐怖分子(用户)导弹攻击时,会自动发出警报E1(事件),警报由飞行控制中心(运行时系统)接收后,立即转发给护航的战斗机 F1(事件监听器),F1立即采取必要的防卫和反击措施(调用事件处理方法)。

 

示例Java事件处理

程序:TestActionEvent.java

 

 1 import java.awt.*;
 2 
 3 import java.awt.event.*;
 4 
 5 public class TestActionEvent {
 6 
 7     public static void main(String args[]) {
 8 
 9     Frame f = new Frame("Test");
10 
11     Button b = new Button("Press Me!");
12 
13     Monitor bh = new Monitor();
14 
15     b.addActionListener(bh);
16 
17     f.add(b,BorderLayout.CENTER);
18 
19     f.pack();
20 
21     f.setVisible(true);
22 
23     }
24 
25 }
26 
27  
28 
29 class Monitor implements ActionListener {
30 
31     public void actionPerformed(ActionEvent e) {
32 
33         System.out.println("a button has been pressed");    
34 
35     }
36 
37 }

 

程序GUI如图所示

 

 

 

    其运行效果为:用户每次鼠标单击“Press Me!”按钮时,程序都会在控制台窗口中输出一行字符串“a button has been pressed”。

    程序中,Button 对象b作为事件源组件,调用其成员方法 addActionListener()与监听器对象 bh 建立了监听和被监听的关系,这一过程称为注册监听器。然后,当按钮 b 受到用户鼠标单击时,会触发ActionEvent事件,即创建一个ActionEvent 类型的对象并将之提交给运行时系统。运行时系统再将此其转发给此前曾在 b 上注册过的监听器 bh,并以其做为实参自动调用bh的相应事件处理方法 actionPerformed()

     Java GUI 设计中,就是通过这种注册监听器的方式对所关注的事件源进行监控的。如果关注某个组件产生的事件,就可以在该组件上注册适当的监听器。那么到底有哪些常见的

事件呢?这里罗列了一些常见的事件:

action(Event evt, Object what) 

当典型的事件针对组件发生(例如,当按下一个按钮或下拉列表项目被选中)

时调用。

keyDown(Event evt, int key) 

当按键被按下,组件拥有焦点时调用。第二个自变量是按下的键并且是冗余的

是从evt.key处复制来的。

keyup(Event evt, int key) 

当按键被释放,组件拥有焦点时调用。

lostFocus(Event evt, Object what) 

焦点从目标处移开时调用。通常,what是从 evt.arg里冗余复制的。

gotFocus(Event evt, Object what) 

焦点移动到目标时调用。

mouseDown(Event evt, int xint y) 

一个鼠标按下存在于组件之上,在 XY座标处时调用。

mouseUp(Event evt, int x, int y) 

一个鼠标升起存在于组件之上时调用。

mouseMove(Event evt, int x, int y) 

当鼠标在组件上移动时调用。

mouseDrag(Event evt, int x, int y) 

鼠标在一次mouseDown 事件发生后拖动。 所有拖动事件都会报告给内部发生了

mouseDown

事件的那个组件,直到遇到一次 mouseUp为止。

mouseEnter(Event evt, int x, int y) 

鼠标从前不在组件上方,但目前在。

mouseExit(Event evt, int x, int y) 

鼠标曾经位于组件上方,但目前不在。

 

    接下来读者可能的困惑是, 什么样的对象才能做监听器?什么样的方法才算是事件处理方法?注册监听器的方法有那些?

   首先,并不是任何类型的对象都能做事件监听器,相信没人会指望 String 对象能处理ActionEvent事件。JDKjava.awt.event包中定义了一系列的事件监听器接口,在这些接口中定义了各种 Java GUI 事件处理方法,只有这些接口的实现类的对象才有资格做为监听器,去处理相应的事件。事件监听器类型和对应的事件处理方法都是事先约定好的,例如ActionListener接口中定义的actionPerformed()方法是专门用于处理ActionEvent类型事

件的,其格式如下:

public void actionPerformed(ActionEvent e);

 

   其次,注册监听器的方法是在Java GUI组件类中定义的,其格式为:

public void add×××Listener(×××Listener l)

  其中的×××代表某种事件类型,一个 Java GUI 组件可能产生多种不同类型的事件,因而可以注册多种不同的监听器。例如,当用户鼠标点击 Button 按钮时还会同时产生MouseEvent 事件,Fram 窗口组件可以产生 MouseEvent WindowEvent 等,这是为了实现程序与用户的有效交互。 ,在java.awt.event 包中定义的相应的事件类型,注册监听器时应指明该监听器所监控的事件种类。

 

例如示例中,Button类的addActionListener()方法:

public void addActionListener(ActionListener l)

Java GUI事件监听器接口

 

2 多重监听器                         

   由于事件源可以产生多种不同类型的事件,因而可以注册(触发)多种不同类型的监听器。是当事件源发生了某种类型的事件时,只触发事先已就该种事件类型注册过的监听器。 

需要注意的两点是:

  针对同一个事件源的同一种事件也可以注册多个监听器。如同为总统请多个保镖,他们都监听同一个事件源可能产生的同一种事件,即总统遇刺事件。

  同一个监听器对象可以被同时注册到多个不同的事件源上。 如同一支消防队伍同时负责多家工厂的安全,处理可能发生的火灾事件。

 

示例使用多重监听器

程序:TestActionEvent2.java

 

 1 import java.awt.*;
 2 
 3  
 4 
 5 import java.awt.event.*;
 6 
 7 public class TestActionEvent2 {
 8 
 9     public static void main(String args[]) {
10 
11     Frame f = new Frame("Test");
12 
13     Button b1 = new Button("Start");
14 
15     Button b2 = new Button("Stop");
16 
17     Monitor2 bh = new Monitor2();
18 
19     b1.addActionListener(bh);       
20 
21     b2.addActionListener(bh);
22 
23     b2.setActionCommand("game over");
24 
25     f.add(b1,"North");       
26 
27     f.add(b2,"Center"); 
28 
29     f.pack();           
30 
31     f.setVisible(true);
32 
33     }
34 
35 }
36 
37  
38 
39 class Monitor2 implements ActionListener {
40 
41     public void actionPerformed(ActionEvent e) {
42 
43       System.out.println(e.getActionCommand());    
44 
45   }
46 
47 }

 

程序GUI 如图所示

 

 

TestActionEvent2.java

程序图形用户界面

 

   其运行效果为:用户点击“Start”按钮时,程序会在控制台窗口中输出一行字符串Start ;用户点击“Stop”按钮时,程序输出“game over”。

   程序TestActionEvent2.java中,在Button组件b1b2注册了同一个监听器对象bh因此b1 b2 被点击时产生的的 ActionEvent事件均被传送给 bh,并由 bh 调用其约定的方actionPerformed()进行处理。

   需要说明的是,Button 类中定义了一个 String 类型属性 actionCommand,用于记录该按钮对象所激发的ActionEvent 事件的命令名称,此属性的默认值为该按钮的标签字符串,也可以使用 Button 类的成员方法 setActionCommand()重置它的值。ActionEvent 类中也包含了一个同名属性 actionCommand,其作用是:当ActionEvent 对象被创建时,触发了此事件的事件源组件(例如示例中的 Button 对象 b1 b2)将把其自身的 actionCommand 属性值复制给新创建ActionEvent对象的同名属性。随着ActionEvent 对象的传递,有关事件源组件的个性化信息作为事件对象的属性被发送到事件处理方法 actionPerformed()中,再通过ActionEvent类的getActionCommand()进行解析,以实现对事件源的区分和不同处理。 

  程序 TestActionEvent2.java 的运行结果可能每次都不相同,因为它受两个 Button件触发ActionEvent事件的时机和次序、即与用户交互情况的影响。这种程序运行方式称为事件驱动(Event-driven)。

 

示例复杂多重监听器

程序TestMultiListener.java

 

 1 import java.awt.*;
 2 
 3 import java.awt.event.*;
 4 
 5 public class TestMultiListener implements 
 6 
 7   MouseMotionListener,MouseListener {
 8 
 9   Frame f = new Frame("多重监听器测试");
10 
11   TextField tf = new TextField(30);
12 
13   public TestMultiListener(){
14 
15     Label l = new Label("请按下鼠标左键并拖动");
16 
17     f.add(l, "North");
18 
19     f.add(tf, "South");
20 
21     f.setBackground(new Color(180,225,225));
22 
23     f.addMouseMotionListener(this);
24 
25     f.addMouseListener(this);
26 
27     f.setSize(300, 200);       
28 
29     f.setVisible(true);
30 
31   }
32 
33   public static void main(String args[]) {    
34 
35     TestMultiListener t = new TestMultiListener();
36 
37   }
38 
39   public void mouseDragged(MouseEvent e) {
40 
41       String s = "鼠标拖动到位置(" + e.getX() + 
42 
43             "," + e.getY() + ")";
44 
45       tf.setText(s);
46 
47   } 
48 
49   public void mouseEntered(MouseEvent e) {
50 
51       tf.setText("鼠标已进入窗体");
52 
53   } 
54 
55   public void mouseExited(MouseEvent e) {
56 
57       tf.setText("鼠标已移出窗体");
58 
59   }
60 
61   public void mouseMoved(MouseEvent e) { }
62 
63   public void mousePressed(MouseEvent e) { }
64 
65   public void mouseClicked(MouseEvent e) { }
66 
67   public void mouseReleased(MouseEvent e) { }
68 
69 }
70 
71  

 

 

程序GUI如图所示

 

 

 

 

 

   其运行效果为:用户鼠标移入(移出)Frame 窗口范围时,TextField(单行文本框)组件中会显示“鼠标已进入(移出)窗体”的信息;当用户鼠标在窗体范围内按下左键并拖动时,TextField 组件中会显示鼠标的当前位置坐标。

   程序TestMultiListener.java 中,用到了 MouseMotionListener MouseListener种常用的事件监听器接口。他们都是用来处理组件所触发的鼠标动作事件 MouseEvent 的,但有不同的分工。

   MouseListener 负责接收和处理鼠标的 press, release, click, enter exit(即按下、松开、点击、移入、移出)动作触发的MouseEvent事件,其相应的事件处理方法为:

 

public void mousePressed(MouseEvent e);

public void mouseReleased(MouseEvent e);

public void mouseClicked(MouseEvent e);

public void mouseEntered(MouseEvent e);

public void mouseExited(MouseEvent e);

 MouseMotionListener 负责接收和处理鼠标的 move,  drag(即移动、拖动)动作触发的MouseEvent事件,其相应的事件处理方法为:

public void mouseMoved(MouseEvent e);

public void mouseDragged(MouseEvent e);

3:事件适配器                                                                            

   为简化编程, 针对大多数事件监听器接口定义了相应的实现类----事件适配器类,在适配器类中,实现了相应监听器接口中所有的方法,但不做任何事情。然后程序员在定义监听器类时就可以继承事件适配器类,并只重写所需要的方法。

先来看一个事件适配器类WindowAdapter 的定义:

 

 

 1 public abstract class WindowAdapter implements WindowListener {
 2 
 3 public void windowOpened(WindowEvent e) {}
 4 
 5 public void windowClosing(WindowEvent e) {}
 6 
 7 public void windowClosed(WindowEvent e) {}
 8 
 9 public void windowIconified(WindowEvent e) {}
10 
11 public void windowDeiconified(WindowEvent e) {}
12 
13 程序TestMultiListener.java GUI
14 
15  
16 
17 public void windowActivated(WindowEvent e) {}
18 
19 public void windowDeactivated(WindowEvent e) {}
20 
21 }

 

常用的事件适配器类有:

ComponentAdapter(组件事件适配器)

ContainerAdapter(容器事件适配器)

FocusAdapter(焦点事件适配器)

KeyAdapter(键盘事件适配器)

MouseAdapter(鼠标事件适配器)

MouseMotionAdapter(鼠标运动事件适配器)

WindowAdapter(窗口事件适配器)

   适配器类只是为了简化编程而提供的一种中间性转换工具,使程序员在定义监听器类时可以不必因直接实现监听器接口而被迫重写所有的抽象方法。 但因 Java单继承机制的限制,如果要定义的监听器类需要同时能处理两种以上的GUI事件, 则只能直接实现有关的监听器接口,而无法只通过继承适配器实现。或者,一旦继承了适配器类,则无法再继承其他所需的父类。

示例事件监听器接口和适配器类使用对比

程序:TestListener.java

 

 1 import java.awt.*;
 2 
 3 import java.awt.event.*;
 4 
 5 public class TestListener{
 6 
 7     public static void main(String args[]){
 8 
 9     Frame f = new Frame("Java Gui");
10 
11     f.setSize(150,150);
12 
13     MyListener m = new MyListener();
14 
15     f.addWindowListener(m);
16 
17     f.setVisible(true);
18 
19   } 
20 
21 } 
22 
23  
24 
25 class MyListener implements WindowListener{
26 
27   public void windowOpened(WindowEvent e){}
28 
29   public void windowClosing(WindowEvent e){
30 
31     System.exit(1); 
32 
33   }
34 
35   public void windowClosed(WindowEvent e){}
36 
37   public void windowIconified(WindowEvent e){}
38 
39   public void windowDeiconified(WindowEvent e){}
40 
41   public void windowActivated(WindowEvent e){}
42 
43  
44 
45   public void windowDeactivated(WindowEvent e){} 
46 
47 }

 

示例程序运行时,用户鼠标点击其 GUI 窗口角标 可以关闭窗口并退出当前程序。其中,System类的exit()方法可以终止当前正在运行的 Java虚拟机。其格式为:

public  static  void  exit(int  status),其中参数 status为返回给操作系统的状态码,status取值为0表示JVM正常结束,非0 表示非正常结束。

将程序 TestListener.java 中的 MyListener 类改为继承事件适配器类,可以实现完全相同的功能,但在编码上大大简化了。其形式如下:

 1 class MyListener extends WindowAdapter{
 2 
 3   public void windowClosing(WindowEvent e){
 4 
 5     System.exit(1); 
 6 
 7   }
 8 
 9 }
10 
11  

 

 

4:内部类和匿名类在Java事件处理中的应用                                                

    在JavaGUI事件处理中,常常采用内部类的形式来定义监听器类,这是因为监听器类中封装的商务逻辑具有非常强的针对性,通常没有重用价值。而且作为内部类的监听器类对象可以直接访问其封装类的成员, 这可以提供很大的便利。让我们来分析一个内部类做监听器的例子:

示例  GUI 事件处理中使用内部类

程序:TestInner.java

 

 1 import java.awt.*; 
 2 import java.awt.event.*; 
 3 public class TestInner { 
 4 Frame f = new Frame("内部类测试"); 
 5 TextField tf = new TextField(30); 
 6 public TestInner(){ 
 7 f.add(new Label("请按下鼠标左键并拖动"), "North"); 
 8 f.add(tf, "South"); 
 9 f.setBackground(new Color(120,175,175)); 
10 f.addMouseMotionListener(new InnerMonitor()); 
11 f.addMouseListener(new InnerMonitor()); 
12 f.setSize(300, 200); 
13 f.setVisible(true); 
14 } 
15 public static void main(String args[]) { 
16 TestInner t = new TestInner(); 
17 } 
18 
19 private class InnerMonitor 
20 implements MouseMotionListener,MouseListener { 
21 public void mouseDragged(MouseEvent e) {
22 
23 String s = "鼠标拖动到位置(" + e.getX() + "," + e.getY() + ")"; 
24 tf.setText(s); 
25 } 
26 
27 public void mouseEntered(MouseEvent e) { 
28 String s = "鼠标已进入窗体"; 
29 tf.setText(s); 
30 } 
31 
32 public void mouseExited(MouseEvent e) { 
33 String s = "鼠标已移出窗体"; 
34 tf.setText(s); 
35 } 
36 public void mouseMoved(MouseEvent e) { } 
37 public void mousePressed(MouseEvent e) { } 
38 public void mouseClicked(MouseEvent e) { } 
39 public void mouseReleased(MouseEvent e) { } 
40 } 
41 } 

 

 

     程序运行效果同程序TestMultiListener.java,其中用到的监听器类 InnerMonitor被定义为内部类,因此可以直接访问封装类的属性 tf,以实现显示内容的改变。请读者思考如果把监听器类InnerMonitor改为一个普通的顶层类,要实现相同的功能可能遇到的问题。 

    以内部类做为监听器类的方式还可能进一步简化, 这就是匿名内部类----在定义类的同
时直接创建并一次性地使用该类对象,而不指定类的名称。看先一个使用匿名类的例子:

示例 在GUI 事件处理中使用匿名类
程序:TestAnonymous.java

 

 1  
 2 
 3 import java.awt.*; 
 4 import java.awt.event.*; 
 5 public class TestAnonymous { 
 6 Frame f = new Frame("匿名内部类测试"); 
 7 TextField tf = new TextField(30); 
 8 public TestAnonymous(){ 
 9 f.add(new Label("请按下鼠标左键并拖动"), "North"); 
10 f.add(tf, "South"); 
11 f.addMouseMotionListener( 
12 new MouseMotionAdapter(){ 
13 public void mouseDragged(MouseEvent e) { 
14 tf.setText("鼠标位置" + e.getPoint()); 
15 } 
16 }); 
17 f.setSize(300, 200); 
18 f.setVisible(true); 
19 }
20 
21 public static void main(String args[]) { 
22 TestAnonymous t = new TestAnonymous(); 
23 } 
24 }

   程序运行效果与示例程序 TestMultiListener.java 基本相同,查看其中监听器对象的创建方式,会有一些奇怪的发现:new MouseMotionAdapter(){},看来是要创建一个抽象类MouseMotionAdapter类型的对象,这似乎是不可能的。而且new  MouseMotionAdapter()后面紧跟的大括号又是什么意思?这怎么可能是合法的 Java 代码?实际上,这些代码是合法的,它的含义是:定义MouseMotionAdapter的一个匿名子类,并创建一个该子类的对象。

上面提及的花括号内容就是子类的类体,子类中重写了父类的 mouseDrag()方法。

注意:在匿名类中不能声明构造方法,因为构造方法的名字要和所处的类名一样,而匿

名是没有名字的。

匿名内部类在本质上是普通内部类的一种简化形式,可以进行相互转换。例如,下面两

段程序完全等价:

 

示例 内部类和匿名内部类使用比较

程序1Test1.java

   

 2       public void windowClosing(WindowEvent e){ 
 3         System.exit(1);  
 4       } 
 5     }); 
 6     f.setSize(150,150); 
 7     f.setVisible(true);   
 8   } 
 9     public static void main(String args[]){ 
10     new Test2(); 
11   }  
12 }import java.awt.*; 
13 import java.awt.event.*; 
14  
15 public class Test1{ 
16   Frame f = new Frame("Java Gui"); 
17   public Test1(){ 
18     MyInner m = new MyInner(); 
19     f.addWindowListener(m); 
20     f.setSize(150,150); 
21     f.setVisible(true);   
22   } 
23     public static void main(String args[]){ 
24     new Test1(); 
25   } 
26   class MyInner extends WindowAdapter{ 
27     public void windowClosing(WindowEvent e){ 
28       System.exit(1);  
29     } 
30   }  
31 } 
32 
程序2:Test2.java
 1 import java.awt.*; 
 2 import java.awt.event.*; 
 3 public class Test2{ 
 4 Frame f = new Frame("Java Gui"); 
 5  public Test2(){ 
 6  f.addWindowListener(new WindowAdapter(){ 
 7 public void windowClosing(WindowEvent e){ 
 8 System.exit(1); 
 9 } 
10 }); 
11 f.setSize(150,150); 
12 f.setVisible(true); 
13 } 
14 public static void main(String args[]){ 
15 new Test2(); 
16 }
17 
18 }

 


 

匿名类也可以从接口中派生,其格式为 new <interface_name>(){}。此时,匿名类必须实现接口中所有的抽象方法。除此之外,匿名类具备内部类的全部特性,比如可以根据需要添加新的属性和方法,或访问其封装类的成员。

示例 使用匿名内部类实现接口

程序1TestAnonymous2.java

 1 import java.awt.*;
 2 
 3 import java.awt.event.*;
 4 
 5 public class TestAnonymous2 {
 6 
 7   Frame f = new Frame("Test");
 8 
 9   TextField tf = new TextField(10);
10 
11   Button b1 = new Button("Start");
12 
13   public TestAnonymous2(){
14 
15     f.add(b1,"North");
16 
17     f.add(tf,"South");
18 
19     b1.addActionListener(new ActionListener(){
20 
21       private int i;
22 
23       public void actionPerformed(ActionEvent e) {
24 
25          tf.setText(e.getActionCommand() + ++i);    
26 
27       }
28 
29     });
30 
31     f.pack();         
32 
33     f.setVisible(true);  
34 
35   } 
36 
37   public static void main(String args[]) {
38 
39     new TestAnonymous2();
40 
41   }
42 
43 } 

 

程序中的匿名内部类实现了 ActionListener 接口,其中添加了新的属性 i,用做计数器。

需要注意的是,匿名内部类无法同时实现多个接口。

 

 

 

五:基本的 Swing 程序                                                                       

1:一个基本的示例程序                                                                  

下面详细讲述一个Swing 的应用程序 HelloSwing

HelloSwing应用程序的输出产生下图所示的窗口:

 

         

 

    每次用户点击按钮时,标签就会更新。

 

1.1 HelloSwing

 

   1.import java.awt.*;

   2.import java.awt.event.*; 

   3.import javax.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.

  41.// Add support for accessibility. 

  42.accContext = jButton.getAccessibleContext(); 

  43.accContext.setAccessibleDescription( 

  44."Pressing this button increments " + 

  45."the number of button clicks"); 

  46.

  47.// Set up pane. 

  48.// Give it a border around the edges. 

  49.jPanel = new JPanel();  

  50.jPanel.setBorder( 

  51.BorderFactory.createEmptyBorder( 

  52.30,30,10,30)); 

  53.

  54.// Arrange for compts to be in a single column. 

  55.jPanel.setLayout(new GridLayout(0, 1)); 

  56.

  57.// Put compts in pane, not in JFrame directly. 

  58.jPanel.add(jButton); 

  59.jPanel.add(jLabel); 

  60.jFrame.setContentPane(jPanel); 

  61.

  62.// Set up a WindowListener inner class to handle 

  63.// window's quit button. 

  64.WindowListener wl = new WindowAdapter() { 

  65.public void windowClosing(WindowEvent e) { 

  66.System.exit(0); 

  67.} 

  68.}; 

 

  69.jFrame.addWindowListener(wl); 

  70.

  71.jFrame.pack(); 

  72.jFrame.setVisible(true); 

  73.} 

  74.

  75.// Button handling. 

  76.public void actionPerformed(ActionEvent e) { 

  77.numClicks++; 

  78.jLabel.setText(labelPrefix + numClicks); 

  79.} 

  80.

  81.public static void main(String[] args) { 

  82.

  83.HelloSwing helloSwing = new HelloSwing();  

  84.helloSwing.go(); 

  85.} 

  86.}

 

 

1.2  导入Swing

 

 

    语句行import javax.swing.*装入整个 Swing包,它包括了标准 Swing组件和功能。选择外观和感觉

    Hello Swing 的第 2228 行给定了应用程序外观和感觉的格式。getLookAndFeel()法返回在 Windows环境中的外观和感觉。在运行 Solaris操作系统的机器上,这个方法则返回一个公共桌面环境(CDE/Motif 的外观和感觉。因为都是缺省值,所以对本例来说,这些行都不是必需的。

 

1.3 建立窗口

    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 是一个包含除菜单条(如果有的话)外所有框架的可视组件的容器。要获得一个JFramecontent  pane,可使用getContentPane()方法。要设置它的 content pane(如前面本例所示),则可使用set ContentPane()方法。

 

 

1.4 建立Swing组件

 

 

 

 

 

    Hello  Swing 程序显式地实例化了 4个组件: JFrame JButton JLabelJPanel HelloSwing用第3345行中的代码来初始化JButton

    33 行创建了按钮。第37行将ACTI 键组合设置为快捷键,用来模拟按钮的点击。

   第39行为点击注册了一个事件处理器。第 4145行描述了一个按钮,使得辅助技术可以提供有关按钮功能的信息。

    4959 行初始化了 JPanel。这些代码创建了 JPanel 对象,设置它的边框,并将它的布局管理器设置为单列地放置panel的内容。最后,将一个按钮和一个标签加入到 Panel中。Hello Swing 中的 Panel使用了一个不可见的边框,用来在它周围放入额外的填充。

 

1.5 支持辅助技术

    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);

      accContext = jButton.getAccessibleContext();

                    accContext.setAccessibleDescription(

"Pressing this button increments " + 

" the number of button clicks.");

 JFrameJButtonJLabel和其他所有组件中,都有内建的 Accessibility 支持。辅

助技术可以很容易地获得文本,甚至与一组件某特定部分相关的文本。

 

2:构造一个Swing GUI                                                                  

  Swing包定义了两种类型的组件:

    • l-  顶层容器(JFrameJAppletJWindow,和 JDialog
    • l-  轻质组件(其他的J…,如 JButtonJPanelJMenu

    顶层容器定义了可以包含轻质组件的框架。特别地,一个顶层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)

        §

       ……..

      §

content pane

      §

`  +---------+---------+

  §         §        §

JButton   JButton   JPanel

                            §

          +----------------+

               §              §

JTextField     JList

 

 

 

下面是关于同样的GUI 的另一个容器层次结构,只是在这里,GUI是在浏览器中运行的

一个Applet

 

 ….

   §

 ….

   §

        JApplet  ( a top-level Swing container)

          §

content pane

          §

`  +---------+---------+

  §      §             §

JButton   JButton   JPanel

                          §

                   +----------+

§         §

JTextField   JList

 

    下面是构造如上图所示的GUI层次结构的代码:

 

  1    import javax.swing.*;
  2 
  3    import java.awt.*; 
  4 
  5     6 
  7    public class SwingGUI { 
  8 
  9     10 
 11    JFrame topLevel; 
 12 
 13    JPanel jPanel; 
 14 
 15    JTextField jTextField; 
 16 
 17    JList jList; 
 18 
 19    20 
 21   JButton b1; 
 22 
 23   JButton b2; 
 24 
 25   Container contentPane; 
 26 
 27   28 
 29   Object listData[] = { 
 30 
 31   new String("First selection"), 
 32 
 33   new String("Second selection"), 
 34 
 35   new String("Third selection") 
 36 
 37   }; 
 38 
 39    40 
 41   public static void main (String args[]) { 
 42 
 43   SwingGUI swingGUI = new SwingGUI(); 
 44 
 45   swingGUI.go(); 
 46 
 47   } 
 48 
 49    50 
 51   public void go() { 
 52 
 53   topLevel = new JFrame("Swing GUI"); 
 54 
 55    56 
 57   // Set up the JPanel, which contains the text field 
 58 
 59   // and list. 
 60 
 61   jPanel = new JPanel(); 
 62 
 63   jTextField = new JTextField(20); 
 64 
 65   List = new JList(listData); 
 66 
 67   68 
 69   contentPane = topLevel.getContentPane(); 
 70 
 71   contentPane.setLayout(new BorderLayout()); 
 72 
 73    74 
 75  
 76 
 77   b1 = new JButton("1"); 
 78 
 79   b2 = new JButton("2"); 
 80 
 81   contentPane.add(b1, BorderLayout.NORTH); 
 82 
 83   contentPane.add(b2, BorderLayout.SOUTH); 
 84 
 85   86 
 87   jPanel.setLayout(new FlowLayout()); 
 88 
 89   jPanel.add(jTextField); 
 90 
 91   jPanel.add(jList); 
 92 
 93   contentPane.add(jPanel, BorderLayout.CENTER); 
 94 
 95    96 
 97   topLevel.pack(); 
 98 
 99   topLevel.setVisible(true); 
100 
101   } 
102 
103   }

 

 

六:谈谈 GUI编程中的“写”界面                                                               

   前面已经总结了如何“画”界面,画出来的界面是静态的,如何让它“动”起来呢?下面就谈谈如何在画好的界面上写代码,通过代码来实现动态的功能。

 

1:表现层三大功能                                                                

   应用程序的界面层也被称为表现层,专门用于图形化的跟用户进行交互。通常表现层具有如下3 个主要的功能:

1):展示数据:主要是从逻辑层获取需要展示给用户看或操作的数据

2 :人机交互:用户可以在界面上输入值,也可以点击某些组件,从而引起某些动态

的事件

3):收集界面参数,调用逻辑层接口

2:如何写代码呢                                                                  

  分析表现层的功能,需要写代码来完成的功能不多,主要有两个部分,其一是展示数据部分,其一是事件处理。写代码的通常步骤总结如下:

    展示数据部分的代码通常是写在界面初始化的方法里面,写法如下:

1):调用逻辑层接口,获取需要展示的数据

2):转换数据

3):把数据设置到组件上进行展示

 

事件处理部分的代码通常是写在事件处理的方法里面,写法如下:

1):收集参数

2 :组织参数

3):调用逻辑层接口,获取返回值

4):根据返回值,选择下一个界面

 

 

 

练习实践课:                                         

      • Swing基础
      • 事件处理

 

程序 1                                                          

鼠标移动

需求:

1 不论鼠标何时移动都围绕它画一个小圆;

2 每当按下鼠标时,屏幕显示文字。

目标:

1 鼠标移动事件;

2 绘图语句;

3 色彩处理。

程序:

 

//: BangBean.java

 

  1 package com.useful.java.part4;
  2 
  3  
  4 
  5 import java.awt.*;
  6 
  7 import java.awt.event.*;
  8 
  9 import java.io.*;
 10 
 11 import java.util.*;
 12 
 13  
 14 
 15 public class BangBean extends Canvas implements Serializable {
 16 
 17          protected int xm, ym;
 18 
 19          protected int cSize = 20; // Circle size
 20 
 21          protected String text = "Bang!";
 22 
 23          protected int fontSize = 48;
 24 
 25          protected Color tColor = Color.red;
 26 
 27          protected ActionListener actionListener;
 28 
 29          public BangBean() {
 30 
 31                     addMouseListener(new ML());
 32 
 33                     addMouseMotionListener(new MML());
 34 
 35          }
 36 
 37          public int getCircleSize() { return cSize; }
 38 
 39          public void setCircleSize(int newSize) {
 40 
 41                    cSize = newSize;
 42 
 43          }
 44 
 45          public String getBangText() { return text; }
 46 
 47          public void setBangText(String newText) {
 48 
 49                    text = newText;
 50 
 51  
 52 
 53          }
 54 
 55          public int getFontSize() { return fontSize; }
 56 
 57          public void setFontSize(int newSize) {
 58 
 59                    fontSize = newSize;
 60 
 61          }
 62 
 63          public Color getTextColor() { return tColor; }
 64 
 65          public void setTextColor(Color newColor) {
 66 
 67                    tColor = newColor;
 68 
 69          }
 70 
 71          public void paint(Graphics g) {
 72 
 73                    g.setColor(Color.black);
 74 
 75                    g.drawOval(xm - cSize/2, ym - cSize/2,
 76 
 77                                 cSize, cSize);
 78 
 79          }
 80 
 81          // This is a unicast listener, which is
 82 
 83          // the simplest form of listener management:
 84 
 85          public void addActionListener (
 86 
 87                              ActionListener l)
 88 
 89                              throws TooManyListenersException {
 90 
 91                    if(actionListener != null)
 92 
 93                              throw new TooManyListenersException();
 94 
 95                     actionListener = l;
 96 
 97          }
 98 
 99          public void removeActionListener(
100 
101                              ActionListener l) {
102 
103                     actionListener = null;
104 
105          }
106 
107          class ML extends MouseAdapter {
108 
109                    public void mousePressed(MouseEvent e) {
110 
111                              Graphics g = getGraphics();
112 
113                              g.setColor(tColor);
114 
115                              g.setFont(
116 
117                                                  new Font(
118 
119                                                  "TimesRoman", Font.BOLD, fontSize));
120 
121                              int width =
122 
123                                                  g.getFontMetrics().stringWidth(text);
124 
125                              g.drawString(text,
126 
127                                               (getSize().width - width) /2,
128 
129                                               getSize().height/2);
130 
131                              g.dispose();
132 
133                              // Call the listener's method:
134 
135                              if(actionListener != null)
136 
137                                         actionListener.actionPerformed(
138 
139                                                              new ActionEvent(BangBean.this,
140 
141  
142 
143                                                            
144 
145 ActionEvent.ACTION_PERFORMED, null));
146 
147                    }
148 
149          }
150 
151          class MML extends MouseMotionAdapter {
152 
153                    public void mouseMoved(MouseEvent e) {
154 
155                              xm = e.getX();
156 
157                              ym = e.getY();
158 
159                               repaint();
160 
161                    }
162 
163          }
164 
165          public Dimension getPreferredSize() {
166 
167                    return new Dimension(200, 200);
168 
169          }
170 
171          // Testing the BangBean:
172 
173          public static void main(String[] args) {
174 
175                    BangBean bb = new BangBean();
176 
177                    try {
178 
179                              bb.addActionListener(new BBL());
180 
181                              } catch(TooManyListenersException e) {}
182 
183                              Frame aFrame = new Frame("BangBean Test");
184 
185                               aFrame.addWindowListener(
186 
187                                                  new WindowAdapter() {
188 
189                                        public void windowClosing(WindowEvent e) {
190 
191                                                  System.exit(0);
192 
193                                        }
194 
195                              });
196 
197                                                   aFrame.add(bb, BorderLayout.CENTER);
198 
199                                                   aFrame.setSize(300,300);
200 
201                                                   aFrame.setVisible(true);
202 
203          }
204 
205          // During testing, send action information
206 
207          // to the console:
208 
209          static class BBL implements ActionListener {
210 
211                    public void actionPerformed(ActionEvent e) {
212 
213                              System.out.println("BangBean action");
214 
215                    }
216 
217          }
218 
219 }
220 
221  

 

说明:

1 你可以尝试改变一下此程序,如圆的特征,色彩等;

2 BangBean同样拥有它自己的addActionListener()removeActionListener()方法,因此我们可以附上自己的当用户单击在 BangBean 上时会被激活的接收器。这样, 我们将能够确认可支持的属性和事件, 最重要的是我们会注意到 BangBean执行了这种串联化的接口。这意味着应用程序构建工具可以在程序设计者调整完属性值后利用串联为 BangBean贮藏所有的信息。 Bean作为运行的应用程序的一部分被创建时,那些被贮藏的属性被重新恢复,因此我们可以正确地得到我们的设计。

3 程序运行如下:

 

 

 

 

 

作业                                                                               

1:完成地址本的应用,为已经画出的增、删、改、查、列表界面添加动态功能的实现。

要求数据存储使用第九章集合完成的作业。也就是使用 Swing 做表现层,集合做逻辑

层来实现可以增删改查的地址本的功能。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2014-03-27 20:01  Jemutse  阅读(712)  评论(0)    收藏  举报