首先我们来看一段代码:
package com.gujin.swt;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
public class HelloSWT
{
public static void main(String[] args)
{
Display display = Display.getDefault();
Shell shell = new Shell(display);
Label hello = new Label(shell, SWT.NONE);
hello.setBounds(10, 10, 100, 30);
hello.setText("Hello SWT");
shell.open();
shell.pack();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
{
display.sleep();
}
}
display.dispose();
}
}
这是一段很简单的示例代码,我们可以将其分为三个部分:
第一部分:创建Display对象
Display display = Display.getDefault();
第二部分:创建并配置Shell
Shell shell = new Shell(display);
Label hello = new Label(shell, SWT.NONE);
hello.setBounds(10, 10, 100, 30);
hello.setText(“Hello SWT”);
shell.open();
shell.pack();
第三部分:进入一个循环
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
{
display.sleep();
}
}
这三部分也是一个SWT程序都需要的三部分。
所有的SWT程序在开始运行时都必须获取Display对象,没有Display,SWT程序就无法和操作系统交互,一个SWT程序中至少含有一个Display对象,创建Display对象的线程称为UI线程。
在SWT程序中,我们可以通过如下方式获取一个Display对象:
// 创建一个Display对象
Display create = Display.getDefault();
// 获得Display对象
Display display = Display.getDefault();
// 两次获得对象为同一个
System.out.println(create == display);
这段代码的输出结果为true,同样的下面这段代码也是一样的效果
// 创建一个Display对象
Display create = new Display();
// 获得Display对象
Display display = Display.getDefault();
// 两次获得对象为同一个
System.out.println(create == display);
一个线程中不能同时存在两个活动的Display,如果这样做的话,程序在运行时就会抛出一个SWT异常。如果确实需要创建多个同时活动的Display实例,则必须在不同的线程中创建。我们可以通过Display.getCurrent()函数来获取当前线程对应的Display实例,使用Display.findDisplay(Thread)则可以找到任意线程中的Display对象。
在多线程中,我们不应该使用Display.getDefault()来获得Display实例,这样很容易出现“非法线程访问”的异常,我们可以通过如下方法来获得Display实例:
public static Display getThreadDisplay()
{
return Display.getCurrent() == null ? new Display() : Display.getCurrent();
}
在SWT程序中,一个Shell就代表一个窗体,在SWT程序中,为了使资源管理更方便而做了规定,当父资源释放时,它要负责释放属于自己的子资源。从Display直接创建而来的Shell是Display的子资源,而从某个Shell(Shell A)创建而来的Shell B则是Shell A的子资源。当作为父资源的Shell被释放时,作为子资源的Shell也会同时被释放,而由于所有的Shell都是由Display直接或间接创建的,因此Display是所有Shell的最高层父资源,当一个Display被释放时,所有存在于这个UI线程中的Shell窗口都会被释放掉。
当我们创建了一个窗口并打开它后,就进入事件循环
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
{
display.sleep();
}
}
这段代码就是用于处理事件的循环。通过这段代码我们也可以发现,SWT是通过循环不断的从事件队列中获得信息并处理,所以当我们有耗时较长的事件要处理时,很容易造成程序堵塞,用户的体验度较低,我们通过一段代码来演示一下:
package com.gujin.swt;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
public class HelloSWT
{
public static void main(String[] args)
{
Display display = Display.getDefault();
Shell shell = new Shell(display);
final Label label = new Label(shell, SWT.NONE);
label.setBounds(10, 10, 200, 30);
label.setText("before");
Button button = new Button(shell, SWT.BORDER);
button.setBounds(10, 50, 200, 30);
button.setText("deal");
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e)
{
try
{
Thread.sleep(10000);
}
catch (Exception e2)
{
}
label.setText("after");
}
});
shell.open();
shell.pack();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
{
display.sleep();
}
}
display.dispose();
}
}
运行这段代码,我们可以发现,当我们点击按钮后,窗口会堵塞无响应,直至事件处理结束,这在实际应用中肯定是不可取的,在Display中为我们提供了syncExec(Runnable r)和asyncExec(Runnable r)来向队列中插入事件,我们可以对上面的代码进行如下修改:
package com.gujin.swt;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
public class HelloSWT
{
public static void main(String[] args)
{
final Display display = Display.getDefault();
Shell shell = new Shell(display);
final Label label = new Label(shell, SWT.NONE);
label.setBounds(10, 10, 200, 30);
label.setText("before");
Button button = new Button(shell, SWT.BORDER);
button.setBounds(10, 50, 200, 30);
button.setText("deal");
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e)
{
new Thread() {
@Override
public void run()
{
try
{
Thread.sleep(10000);
}
catch (Exception e2)
{
}
display.syncExec(new Runnable() {
@Override
public void run()
{
label.setText("after");
}
});
}
}.start();
}
});
shell.open();
shell.pack();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
{
display.sleep();
}
}
display.dispose();
}
}
我们启动一个新的线程来处理耗时较多的逻辑,在处理完成后想时间队列中添加一个事件完成后续操作,这时候我们发现单击按钮后,窗口不会出现之前的阻塞现象,在事件处理完成后Label上的文本同样会被改变。