*******************************************************
作者:陈刚,普通程序员,曾有幸以Eclipse插件方式开发过一个中型软件。现将所学
付诸于纸,暂取书名<Eclipse开发指南>,将于2005年初由清华大学出版社出版。
blog:http://blog.csdn.net/glchengang/
*******************************************************
第7章 SWT/JFace的事件模型7.1 事件的四种写法SWT的事件模型是和Java标准的AWT基本一样的。在第6章的例子中,如何来实现文本框的事件响应呢?比如:鼠标双击文本框弹出一个对话框。下面将按照事件的四种写法来实现它。
7.1.1 匿名内部类写法在原来的代码行“text = new Text(shell, SWT.BORDER);”之下插入如下语句:
//addMouseListener加入鼠标事件的监听器
text.addMouseListener(new MouseAdapter() {
public void mouseDoubleClick(MouseEvent e) {//鼠标双击事件的方法
//打开一个信息框
MessageDialog.openInformation (null,"","Hello World");
}
});
new MouseAdapter()就是一个匿名内部类。我们建立了一个继承于MouseAdapter的类,但并没有给这个类命名,并且没有用通常的写法,而是直接在text.addMouseListener方法中写下了类的代码,这就是所谓的匿名内部类(更详尽的解释请参阅Java基础类书籍)。
使用匿名内部类来写事件代码简单方便,但也要注意它的一些缺点:
l 由于事件处理代码会随着组件一起分散在代码中的各个部份,不够集中,这样会导致代码阅读与维护上的不便。
l 各事件的处理全部由嵌套的程序块组成,视觉上会显示有些乱。如果事件处理代码很长,也会导致了阅读与维护上的不便。
l 当工具栏、菜单栏目等也需要处理相同的用户行为时,无法重用事件中的处理代码,导致了代码的臃肿。
7.1.2 命名内部类写法事件代码使用命名内部类的方式,可以解决匿名内部类存在的问题:首先,事件处理代码都集中在一起,并且都具有有意义的名称,程序容易阅读与维护;另外,单个的事件处理程序也可以被工具栏、菜单栏等重用。实现代码如下:
public class HelloWorld {
public static void main(String[] args) {
……
Text text = new Text(shell, SWT.BORDER);
//加入鼠标事件监听器,并用下面代码所定义的内部类生成一个对象
text.addMouseListener(new MyMouseDoubleClick());
……
}
//定义一个名为MyMouseDoubleClick的内部类
private static final class MyMouseDoubleClick extends MouseAdapter {
public void mouseDoubleClick(MouseEvent e) {
MessageDialog.openInformation(null, "", "Hello World");
}
}
}
7.1.3 外部类写法这种写法和命名内部类有些相似,只不过是将MyMouseDoubleClick类从HelloWorld.java中拿出去,单独写成一个类文件。这种写法有和命名内部类一样的优点,但因为要单独写成一个文件,写起来会麻烦一些。实现代码如下
//文件1: HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
……
Text text = new Text(shell, SWT.BORDER);
//加入鼠标事件监听器,并用下面代码所定义的内部类生成一个对象
text.addMouseListener(new MyMouseDoubleClick());
……
}
}
//文件2:MyMouseDoubleClick.java
public class MyMouseDoubleClick extends MouseAdapter {
public void mouseDoubleClick(MouseEvent e) {
MessageDialog.openInformation(null, "", "Hello World");
}
}
7.1.4 实现监听接口的写法将HelloWorld类实现MouseListener接口,这样类本身就成了一个监听器,使得加入监听器的代码可以更简洁。这种方式适合加入监听器的组件较多,且要求监听器的事件处理代码可以被组件共用。这种方式还有一个要注意的地方:事件方法和其他方法混合在了一起,容易引起误读,所以应该在事件方法前加入详细的注释说明。
实现MouseListener接口要写的事件方法多一些,当然没用的事件方法可以空实现。如果继承MouseListener接口的适配器MouseAdapter,则只写需要的方法就行了。另外要注意:只有接口才能有多继承的特性,所以如果HelloWorld已经是某个类的子类,就只能用实现接口的方式,而不能继承接口的适配器了。
给出示例代码如下:
public class HelloWorld extends MouseAdapter{//或implements MouseListener
public static void main(String[] args) {
……
Text text1 = new Text(shell, SWT.BORDER);
Text text2 = new Text(shell, SWT.BORDER);
text1.addMouseListener(this);
text2.addMouseListener(this);
……
}
public void mouseDoubleClick(MouseEvent e) {
MessageDialog.openInformation(null, "", "Hello World");
}
}
7.1.5 总结匿名内部类方式在写起来方便些,但不适合事件代码太长太多的情况。从代码书写、阅读、维护以及程序的可扩展性角度来看,命名内部类写法最为值得推荐。外部类的写法主要是为了代码重用才考虑使用,如果包(package)外的类要用到此事件处理代码,这时外部类就派上用场了。而第四种写法,要求组件都可以共用事件代码时才能使用。
7.2 常用事件介绍除了上例中用于响应鼠标事件的addMouseListener,Eclipse还有一些常用的监听器,它们在各组件中的使用方法相同(如果该组件支持此种事件的话)。在此将它们简单介绍如下:
l addSelectionListener:这个监听器最最常用。
a) widgetSelected方法:当组件被选择(鼠标单击、按回车键)时触发此方法的事件处理程序。
b) widgetDefaultSelected方法:用于某些很少触发选择事件的组件,所以这个方法在实际开发中也很少用。比如,文本框回车事件、列表框双击事件等,就只能用widgetDefaultSelected方法,用widgetSelected方法无效。
l addKeyListener(按键)
a) keyPressed方法:当前焦点停在组件时,按下键盘任一键时触发。但对于某些组件(如按钮Button)按回车键无法执行此方法。
b) keyReleased方法:按键弹起时触发。
l addFocusListener(焦点)
a) focusGained方法:得到焦点时触发。
b) focusLost方法:失去焦点时触发
l addMouseListener(鼠标)
a) mouseDown方法:鼠标按下时触发
b) mouseUp方法:鼠标放开时触发
c) mouseDoubleClick方法:鼠标双击时触发
以上几个就是常用的事件了,很少吧,SWT的事件模型还是极容易掌握的。事实上除了addSelectionListener较常用之外,其他基本都很少用到。
7.3 在事件代码中如何访问类中的变量7.3.1 访问类中的变量的三种方法在写事件代码的时候,常常需要引用主类中的变量,要访问这些变量是需要一些技巧的。
方法一:加final修饰符。
public class HelloWorld {
public static void main(String[] args) {
……
//将变量前加final,否则在事件代码里不能引用
final String str="陈刚";
text.addMouseListener(new MouseAdapter() {
public void mouseDoubleClick(MouseEvent e) {
System.out.println(str); //str变量前要加final
}
});
……
}
}
方法二:将变量str变成类的实例变量。但这种方法扩大了str变量的有效范围。
public class HelloWorld {
//由于引用它的代码是在静态方法内才加static,否则不必要static。
static String str="陈刚";
public static void main(String[] args) {
……
}
}
方法三:将事件代码写成命名内部类,然后通过构造函数的参数来传入。这种方法较繁琐一些。
public class HelloWorld {
public static void main(String[] args) {
String str="陈刚";
//通过构造函数参数将str值传入
text.addMouseListener(new MyMouseDoubleClick(str));
}
//匿名内部类MyMouseDoubleClick
private static final class MyMouseDoubleClick extends MouseAdapter {
private String string;//建一变量引用str的值
public MyMouseDoubleClick(String str){ //通过构造函数参数接受str值
this.string=str;
}
public void mouseDoubleClick(MouseEvent e) {
System.out.println(string);
}
}
}
7.3.2 Java中变量的称法和说明此节中用到了一些Java变量方面的知识,在此一并附上。Java中有三种容易混淆的变量:局部变量、实例变量、类变量,如下程序所示:
public class Variable {
static int allClicks = 0; //类变量
String str = "广西桂林"; //实例变量
public void method() {
int i = 0; //局部变量
}
}
类变量的定义前加有static,这表示它是一个静态的,因此类的多个实例共用一个类变量。实例变量定义在类的方法之外,一般处于类的起始位置,类的每一个实例都独自拥有一份实例变量的拷贝。局部变量的有效范围在程序块中,它的生命期仅限于此程序块内。
实例变量在有些书籍中也翻译成“域”或“成员变量”。在面向数据库的实体类(Hibernate中也称POJO - 简单原始的Java对象)中,被称之为“属性”或“字段”的变量,也是实例变量的一种。
使用变量的一般原则是,尽量使变量的有效范围最小化:优先考虑用局部变量,其次是实例变量,最后才是类变量。
另外,还有一种常量的写法,它比类变量写法仅多了个final,如下所示:
final static int ALL_CLICKS = 0; //常量
注意ALL_CLICKS是全大写的,这是常量的规范命名方式。这时ALL_CLICKS被final约束,它不能再被赋值了。
7.4 本章小结本章主要介绍了事件的四种写法,及事件访问类中变量的方法,这些知识在SWT编程中经常要用到。但是,读者可以不必太执着于本章的内容,可以快速浏览后,进入下一章的学习,当用到时,再回过头来查阅。