SWT(Standard Widget Toolkit)是IBM公司为了开发Eclipse用C语言和C++开发的一套Java GUI库。SWT跟Java标准类库中的Swing和AWT有着本质的区别,这一点绝不仅仅是将Frame和JFrame改名为Shell这样表面的东西,从实现机制上讲,SWT从线程、事件监听等各方面都是一个完全新的世界。
另外,推荐使用Eclipse IDE来进行下面的体验——似乎在命令行无法运行SWT程序?SWT和Eclipse IDE都可以在 Eclipse官方站点 上下载。开发时,将swt.jar包导入到项目构建路径(Build path)中即可。
第一个SWT程序:
仍旧以著名的HelloWorld为例,如果你曾经用过Swing/AWT来写,也许你的代码中最后几行是
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.pack();
一个JFrame/Frame在设置可见以后实际上就创建了一个新线程,从逻辑上来说这是有问题的,因为程序员只是想让这个窗口可以被看到而已,但是这个窗口越俎代庖做了一些出轨的事情,结果就是额外的线程交织在一起让整个程序变得非常混乱。
现在看看SWT的HelloWorld吧
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
public class HelloWorld {
public HelloWorld() {
final Display display = new Display();
final Shell shell = new Shell(display);
final Label helloText = new Label(shell, SWT.CENTER);
helloText.setText("Hello,World!");
helloText.setFont(new Font(display, "Courier New", 14, SWT.BOLD));
helloText.pack();
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
public static void main(String[] args) {
new HelloWorld();
}
}
首先你一定会注意到在构造方法最后的那个while循环,如果你将它去掉,程序也可以运行,然后一闪而过并终止掉。也就是说这个循环是维持窗口线程存在的语句。但是请注意,这个while循环不是随意写的,加入你写成
while (true);
那么程序窗口就会进入无限循环,它倒也确实不会消失,然而却会失去响应。所以在这里readAndDispatch()方法是重要的!
这个方法是org.eclipse.swt.widgets.Display的成员方法,而不是org.eclipse.swt.widgets.Shell的方法,我认为这两者的区别并不是Frame与Panel之间的区别,而更像Container与LayoutManager之间的区别。
另外,你也不需要为窗口来设置诸如DefaultCloseOperation之类的属性了,如果你想要在窗口关闭后做些什么,只需要在while循环后面添加就可以了,这样就把更多的自由留给了开发者。
也许你还注意到了我的一个小动作:设置了一下标签的字体。这里可以看到,SWT不像Swing有些地方还是用AWT的一些库,而是使用自己完全独立的一套GUI库,包括字体在内,与AWT/Swing平行。
给Swing/AWT开发者:Font的构造方法多了一个参数Device,另外字号参数和字样式参数的顺序与AWT相反;另外,很多常数量都在SWT这个类中以静态常量给出,包括诸如BUTTON1之类处理按键事件的常数全部在SWT类中,也有一些其他的类中包含常数,使用时最好通过文档来查找。
事件监听:
Swing中对事件的处理可以算上混乱了,ActionListener、MouseListener、MouseMotionListener——当你要往一个JButton上增加监听者以响应按下按钮的事件,你会选择谁?当你要关掉一个窗口时,你会选择实现WindowListener.windowClosed还是WindowListener.windowClosing?事件监听者的大部分方法是空着的,为什么我只需要响应其中的一种事件,却要实现所有的方法来白白增加代码的长度——以及混乱程度?
幸好这一切噩梦都会在SWT中被终结掉,在SWT的事件监听机制中,对事件的监听只有一个接口:
org.eclipse.swt.widgets.Listener
而这个监听者内仅有一个需要实现的方法:
public void handleEvent(org.eclipse.swt.widgets.Event event)
好了,所有的复杂烦恼都在此刻终结了,现在的问题是,这个东西这么简单,那么怎么知道是哪种类型的事件呢?
答案很简单,在org.eclipse.swt.widgets.Widget中的方法
public void addListener(int, org.eclipse.swt.widgets.Listener)
我们可以看到跟AWT/Swing中不同的是,这里还需传入一个整数,这个整数决定了响应何种类型的事件,比如:
final org.eclipse.swt.widgets.Button button =
new org.eclipse.swt.widgets.Button(shell, SWT.CENTER);
button.setText("Exit");
button.setSize(120, 50);
button.setLocation(200, 0);
button.addListener(SWT.MouseUp,
new org.eclipse.swt.widgets.Listener() {
@Override
public void handleEvent(Event event) {
shell.dispose();
}
} /* anonymous inner class */ );
在你的代码中合适的位置加入这样一段,再来运行一下试试。常量SWT.MouseUp预示着这次加入的事件监听对象需要相应鼠标点击事件(注:SWT内似乎没有MouseClick之类的常量,故使用MouseUp替代之)。
这对于开发人员来说是一种莫大的福音:也许某个按钮、某个菜单项、某个热键操作它们响应事件后执行相同的操作(典型的就是从文本框复制文本),这样只需要实现一个Listener,然后向所有需要的控件中添加就可以了——AWT事件监听者由于类型纷繁,很可能能丢给按钮而不可以绑定热键操作。
多线程:
也许这有一点不好,然而不要乱使用,或者根本撇开Thread来操作与SWT相关的多线程,否则程序会产生奇怪的异常。
这里有一个例子,假如我们试图通过多线程的方法在程序执行了1秒以后将button换个位置,使用Thread来操作通常应该这样:
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
button.setLocation(0, 200);
shell.pack();
}
} /* anonymous inner class */ ).start();
然而事情并不像我们想象的那样,而是在控制台输出了一串异常:
Exception in thread "Thread-0" org.eclipse.swt.SWTException: Invalid thread access
不过SWT给了我们另外的方法,那就是
org.eclipse.swt.widgets.Display
类中的方法
public void asyncExec(java.lang.Runnable)
和
public void syncExec(java.lang.Runnable)
以及
public void timerExec(int, java.lang.Runnable)
最前面那个方法是同步线程执行,也就相当于就地使用Thread来start一个线程,比如刚才的例子,改成
display.asyncExec(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
button.setLocation(0, 200);
shell.pack();
}
} /* anonymous inner class */ );
就可以了。而第二个则是异步线程执行,也就是说调用了这个以后,该Display将在一段时间中无法操作、不响应事件,直到该线程执行结束,大家可以简单地将上面的
asyncExec
中的a去掉试试。而最后一个则是定时同步线程执行,第一个参数单位为毫秒,在经过这么一段时间以后才开始执行线程,并且是同步线程(似乎没有定时异步线程?),还是以刚才那个为例:
display.timerExec(1000, new Runnable() {
@Override
public void run() {
button.setLocation(0, 200);
shell.pack();
}
} /* anonymous inner class */ );
这样就不用再去try-catch了。
今天的SWT体验之旅就到这里了,希望在简单的介绍后SWT能够激发你使用Java语言开发GUI桌面应用程序的兴趣。
另外,推荐使用Eclipse IDE来进行下面的体验——似乎在命令行无法运行SWT程序?SWT和Eclipse IDE都可以在 Eclipse官方站点 上下载。开发时,将swt.jar包导入到项目构建路径(Build path)中即可。
第一个SWT程序:
仍旧以著名的HelloWorld为例,如果你曾经用过Swing/AWT来写,也许你的代码中最后几行是
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.pack();
一个JFrame/Frame在设置可见以后实际上就创建了一个新线程,从逻辑上来说这是有问题的,因为程序员只是想让这个窗口可以被看到而已,但是这个窗口越俎代庖做了一些出轨的事情,结果就是额外的线程交织在一起让整个程序变得非常混乱。
现在看看SWT的HelloWorld吧
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
public class HelloWorld {
public HelloWorld() {
final Display display = new Display();
final Shell shell = new Shell(display);
final Label helloText = new Label(shell, SWT.CENTER);
helloText.setText("Hello,World!");
helloText.setFont(new Font(display, "Courier New", 14, SWT.BOLD));
helloText.pack();
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
public static void main(String[] args) {
new HelloWorld();
}
}
首先你一定会注意到在构造方法最后的那个while循环,如果你将它去掉,程序也可以运行,然后一闪而过并终止掉。也就是说这个循环是维持窗口线程存在的语句。但是请注意,这个while循环不是随意写的,加入你写成
while (true);
那么程序窗口就会进入无限循环,它倒也确实不会消失,然而却会失去响应。所以在这里readAndDispatch()方法是重要的!
这个方法是org.eclipse.swt.widgets.Display的成员方法,而不是org.eclipse.swt.widgets.Shell的方法,我认为这两者的区别并不是Frame与Panel之间的区别,而更像Container与LayoutManager之间的区别。
另外,你也不需要为窗口来设置诸如DefaultCloseOperation之类的属性了,如果你想要在窗口关闭后做些什么,只需要在while循环后面添加就可以了,这样就把更多的自由留给了开发者。
也许你还注意到了我的一个小动作:设置了一下标签的字体。这里可以看到,SWT不像Swing有些地方还是用AWT的一些库,而是使用自己完全独立的一套GUI库,包括字体在内,与AWT/Swing平行。
给Swing/AWT开发者:Font的构造方法多了一个参数Device,另外字号参数和字样式参数的顺序与AWT相反;另外,很多常数量都在SWT这个类中以静态常量给出,包括诸如BUTTON1之类处理按键事件的常数全部在SWT类中,也有一些其他的类中包含常数,使用时最好通过文档来查找。
事件监听:
Swing中对事件的处理可以算上混乱了,ActionListener、MouseListener、MouseMotionListener——当你要往一个JButton上增加监听者以响应按下按钮的事件,你会选择谁?当你要关掉一个窗口时,你会选择实现WindowListener.windowClosed还是WindowListener.windowClosing?事件监听者的大部分方法是空着的,为什么我只需要响应其中的一种事件,却要实现所有的方法来白白增加代码的长度——以及混乱程度?
幸好这一切噩梦都会在SWT中被终结掉,在SWT的事件监听机制中,对事件的监听只有一个接口:
org.eclipse.swt.widgets.Listener
而这个监听者内仅有一个需要实现的方法:
public void handleEvent(org.eclipse.swt.widgets.Event event)
好了,所有的复杂烦恼都在此刻终结了,现在的问题是,这个东西这么简单,那么怎么知道是哪种类型的事件呢?
答案很简单,在org.eclipse.swt.widgets.Widget中的方法
public void addListener(int, org.eclipse.swt.widgets.Listener)
我们可以看到跟AWT/Swing中不同的是,这里还需传入一个整数,这个整数决定了响应何种类型的事件,比如:
final org.eclipse.swt.widgets.Button button =
new org.eclipse.swt.widgets.Button(shell, SWT.CENTER);
button.setText("Exit");
button.setSize(120, 50);
button.setLocation(200, 0);
button.addListener(SWT.MouseUp,
new org.eclipse.swt.widgets.Listener() {
@Override
public void handleEvent(Event event) {
shell.dispose();
}
} /* anonymous inner class */ );
在你的代码中合适的位置加入这样一段,再来运行一下试试。常量SWT.MouseUp预示着这次加入的事件监听对象需要相应鼠标点击事件(注:SWT内似乎没有MouseClick之类的常量,故使用MouseUp替代之)。
这对于开发人员来说是一种莫大的福音:也许某个按钮、某个菜单项、某个热键操作它们响应事件后执行相同的操作(典型的就是从文本框复制文本),这样只需要实现一个Listener,然后向所有需要的控件中添加就可以了——AWT事件监听者由于类型纷繁,很可能能丢给按钮而不可以绑定热键操作。
多线程:
也许这有一点不好,然而不要乱使用,或者根本撇开Thread来操作与SWT相关的多线程,否则程序会产生奇怪的异常。
这里有一个例子,假如我们试图通过多线程的方法在程序执行了1秒以后将button换个位置,使用Thread来操作通常应该这样:
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
button.setLocation(0, 200);
shell.pack();
}
} /* anonymous inner class */ ).start();
然而事情并不像我们想象的那样,而是在控制台输出了一串异常:
Exception in thread "Thread-0" org.eclipse.swt.SWTException: Invalid thread access
不过SWT给了我们另外的方法,那就是
org.eclipse.swt.widgets.Display
类中的方法
public void asyncExec(java.lang.Runnable)
和
public void syncExec(java.lang.Runnable)
以及
public void timerExec(int, java.lang.Runnable)
最前面那个方法是同步线程执行,也就相当于就地使用Thread来start一个线程,比如刚才的例子,改成
display.asyncExec(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
button.setLocation(0, 200);
shell.pack();
}
} /* anonymous inner class */ );
就可以了。而第二个则是异步线程执行,也就是说调用了这个以后,该Display将在一段时间中无法操作、不响应事件,直到该线程执行结束,大家可以简单地将上面的
asyncExec
中的a去掉试试。而最后一个则是定时同步线程执行,第一个参数单位为毫秒,在经过这么一段时间以后才开始执行线程,并且是同步线程(似乎没有定时异步线程?),还是以刚才那个为例:
display.timerExec(1000, new Runnable() {
@Override
public void run() {
button.setLocation(0, 200);
shell.pack();
}
} /* anonymous inner class */ );
这样就不用再去try-catch了。
今天的SWT体验之旅就到这里了,希望在简单的介绍后SWT能够激发你使用Java语言开发GUI桌面应用程序的兴趣。
本文介绍了IBM开发的SWT(Standard Widget Toolkit),一种用于Eclipse的Java GUI库。与Swing和AWT相比,SWT提供了更为简洁高效的事件处理机制,并详细讲解了如何创建基本的应用程序、事件监听和多线程操作。
1114

被折叠的 条评论
为什么被折叠?



