前言 N年之前,在eclipse刚问世的时候,就惊讶于JAVA居然能开发出这么原生,操作灵敏的界面,那时知道eclipse是基于SWT开发的,却没机会使用swt开发任何项目,最近个人出于兴趣写了一个小工具,就抱着试试看的心态使用swt编写界面。
SWT由来 SWT(Standard Widget Toolkit)即标准小窗口工具箱,是IBM公司推出的一种在Eclipse中使用的集成开发环境,SWT提供可移植的API,并与底层本机OS GUI平台紧密集成,它是一个与本地窗口系统集成在一起的小部件集和图形库。SWT由JNI(Java Native Interface,Java本机接口)调用操作系统的内部API,因此运行速度快,能够获得与操作系统的内部应用程序相同的外观。
Swt和SWING的比较
AWT/SWING是sun公司发布的jdk里的,由于有了虚拟机这一层的屏蔽,SWING不依赖于直接调用本地系统的组件,拥有较强的跨平台性,所以在不同操作系统上显示的都是同个外观。SWING组件博大精深,学习起来更加困难,需要加以时日才能轻松驾驭,但是功能也更加强大。
SWT通过JNI直接调用了操作系统的图形库,从而使得Java应用程序的Look & Feel 与操作系统的习惯完全一致,所以SWT开发的界面在不同的操作系统上显示都是不同的外观;更为重要的是,对本地方法的直接调用大幅度的提高了基于SWT的Java应用程序的运行速度。SWT的缺点主要在于两点:(1) 不是Java语言标准;和(2)支持的平台太少。
开发第一个SWT程序
eclipse的plugins目录下,找到文件org.eclipse.swt.win32.win32.x86_3.2.1.v3235.jar,文件名中中3.2.1是eclipse 的版本号,v3235是SWT 的序列号,不同的eclipse版本这两个数字也不同。
操作步骤:
1 新建一个Java 项目,项目名为:sample4_1。
2 配置构建路径,将org.eclipse.swt.win32.win32.x86_3.2.1.v3235.jar加入到项目中,名称太长这里我直接改名为swt.jar。eclipse包资源管理器可以看到导入的原生库文件和引入的jar文件
3 在项目中新建一个类,文件名为HelloSWT.java。
4
HelloSWT.java文件内容如下:
<span >import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.graphics.*;
class HelloSWT {
public static void main(String[] args) {
Display display = new Display();// 创建一个display对象。
Shell shell = new Shell(display);// shell是程序的主窗体
shell.setLayout(null); // 设置shell的布局方式
Text hello = new Text(shell, SWT.MULTI); // 声明一个可以显示多行信息的文本框
shell.setText("Java应用程序"); // 设置主窗体的标题
shell.setSize(200, 100); // 设置主窗体的大小
Color color = new Color(Display.getCurrent(), 255, 255, 255);// 声明颜色对象
shell.setBackground(color); // 设置窗体的背景颜色
hello.setText("Hello, SWT World!\n\n你好,SWT世界!");// 设置文本框信息
hello.pack(); // 自动调整文本框的大小
// shell.pack(); //自动调整主窗体的大小
shell.open(); // 打开主窗体
while (!shell.isDisposed()) { // 如果主窗体没有关闭则一直循环
if (!display.readAndDispatch()) { // 如果display不忙
display.sleep(); // 休眠
}
}
display.dispose(); // 销毁display
}
}
</span>
在包资源管理器中,右单击文件名HelloSWT.java→运行方式→Java应用程序,程序运行
结果如图所示。该窗体具有典型的Windows风格。
分析本例的源代码,可以看到,创建一个典型的SWT应用程序需要以下步骤:
①创建一个Display
②创建一个或多个Shell
③设置Shell的布局
④创建Shell中的组件
⑤用open()方法打开Shell窗体
⑥写一个事件转发循环
⑦销毁 display
开发一个搜车的小工具
下面来做一个稍微复杂一些的界面,直接上代码,先看最终效果图:
注意:本文仅讨论SWT界面的内容,因此把代码里包含别的业务逻辑的代码都注释掉了
package com.findcar.swt;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
//import com.findcar.MainController;
//import com.findcar.client.ToolsClient;
public class MainApp {
private Shell shell = null;
//private MainController control = null;
private static Text logText = null; // 创建日志打印框Text对象
Button startButton;
List filtersList = null;
Text maillist = null;;
Text carType = null;
Text price = null;
Text miles = null;
Text syear = null;
public void init() {
//control = new MainController();
Display display = new Display();
shell = new Shell(display);
shell.setSize(600, 450);
shell.setText("搜 车");
FillLayout fillLayout = new FillLayout(); // 创建FillLayout对象
GridLayout gridLayout1 = new GridLayout(); // 创建GridLayout对象
gridLayout1.makeColumnsEqualWidth = true;
gridLayout1.numColumns = 1;
gridLayout1.verticalSpacing = 2;
shell.setLayout(gridLayout1); // 将FillLayout对象用于shell上
Group conditionGroup = new Group(shell, SWT.NONE); // 创建分组框
conditionGroup.setText("设置搜索条件"); // 设置分组框说明信息
GridData gridData1 = new GridData(GridData.FILL_BOTH
| GridData.HORIZONTAL_ALIGN_CENTER);
gridData1.grabExcessVerticalSpace = false; // 抢占垂直方向额外空间
GridLayout gridLayout2 = new GridLayout();
gridLayout2.numColumns = 6;
gridLayout2.horizontalSpacing = 10;
// gridLayout2.makeColumnsEqualWidth=false;
conditionGroup.setLayout(gridLayout2);
conditionGroup.setLayoutData(gridData1);
GridLayout gridLayout3 = new GridLayout();
gridLayout3.numColumns = 3;
gridLayout3.horizontalSpacing = 10;
Group buttonGroup = new Group(shell, SWT.NONE); // 创建分组框
buttonGroup.setLayout(gridLayout3);
buttonGroup.setLayoutData(gridData1); // 设置按钮的布局数据
Group listGroup = new Group(shell, SWT.NONE); // 创建分组框
listGroup.setText("已设置条件列表"); // 设置分组框说明信息
filtersList = new List(listGroup, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);
// filtersList.addSelectionListener(new SelectionAdapter(){
// public void widgetSelected(SelectionEvent e){
// filtersList.removeAll();
// }
// });
GridData gridData3 = new GridData(GridData.FILL_BOTH
| GridData.HORIZONTAL_ALIGN_CENTER);
gridData3.grabExcessVerticalSpace = true; // 抢占垂直方向额外空间
listGroup.setLayout(fillLayout);
listGroup.setLayoutData(gridData3);
Group logGroup = new Group(shell, SWT.NONE); // 创建分组框
logGroup.setText("日志"); // 设置分组框说明信息
logGroup.setLayoutData(gridData3);
logGroup.setLayout(fillLayout);
Label label = new Label(conditionGroup, SWT.None); // 在shell中创建Label对象
label.setText("车型(关键词)");
carType = new Text(conditionGroup, SWT.BORDER); // 在composite上创建button
// B1
// carType.setText("B1");
carType.setLayoutData(gridData1);
label = new Label(conditionGroup, SWT.None); // 在shell中创建Label对象
label.setText("最高价格(万)");
price = new Text(conditionGroup, SWT.BORDER); // B2设置同B1
price.addVerifyListener(new TextVerifyListener(2));
price.setLayoutData(gridData1);
label = new Label(conditionGroup, SWT.None); // 在shell中创建Label对象
label.setText("最大公里数(万)");
miles = new Text(conditionGroup, SWT.BORDER); // B2设置同B1
miles.addVerifyListener(new TextVerifyListener(2));
miles.setLayoutData(gridData1);
label = new Label(conditionGroup, SWT.None); // 在shell中创建Label对象
label.setText("最早上牌年份");
syear = new Text(conditionGroup, SWT.BORDER); // B2设置同B1
syear.setLayoutData(gridData1);
syear.setTextLimit(4);
// 添加1-9的数字输入控制器
syear.addVerifyListener(new TextVerifyListener(1));
// final Text endYear = new Text(null, SWT.BORDER); // B2设置同B1
// endYear.setLayoutData(gridData1);
// endYear.setTextLimit(4);
label = new Label(conditionGroup, SWT.None); // 在shell中创建Label对象
label.setText("邮件列表");
maillist = new Text(conditionGroup, SWT.BORDER); // B2设置同B1
GridData gridData2 = new GridData(GridData.FILL_BOTH
| GridData.HORIZONTAL_ALIGN_CENTER);
gridData2.grabExcessVerticalSpace = false; // 抢占垂直方向额外空间
gridData2.horizontalSpan = 3;
maillist.setLayoutData(gridData2);
GridData gridData4 = new GridData();
gridData4.grabExcessVerticalSpace = false; // 抢占垂直方向额外空间
gridData4.horizontalAlignment = GridData.HORIZONTAL_ALIGN_CENTER;
gridData4.verticalAlignment = GridData.VERTICAL_ALIGN_BEGINNING;
Button add = new Button(buttonGroup, SWT.PUSH); // 在composite上创建button
// B1
add.setLayoutData(gridData4);
add.setText(" 添 加 ");
add.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
if(checkInput()){
double pp = 0;
double mm = 0;
int sy = 0;
int ey = 0;
if (price.getText().length() > 0)
pp = Double.parseDouble(price.getText());
if (miles.getText().length() > 0)
pp = Double.parseDouble(miles.getText());
if (syear.getText().length() > 0)
sy = Integer.parseInt(syear.getText());
// ToolsClient tl = new ToolsClient(carType.getText(), pp, mm,
// sy, ey, maillist.getText());
// if (control.clientList.contains(tl)) {
// showMessageBox("搜索条件已存在");
// } else {
// filtersList.add(tl.clientName);
// control.clientList.add(tl);
// }
}
}
});
Button delete = new Button(buttonGroup, SWT.PUSH); // 在composite上创建button
// B1
delete.setText(" 删 除 ");
delete.setLayoutData(gridData4);
delete.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
if (filtersList.getSelectionCount() < 1) {
showMessageBox("选择要删除的选项");
} else {
for (String str : filtersList.getSelection()) {
filtersList.remove(str);
//control.removeClient(str);
}
}
}
});
final Button startButton = new Button(buttonGroup, SWT.PUSH); // 在composite上创建button
// B1
startButton.setLayoutData(gridData4);
startButton.setText("开始搜索");
startButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
if (startButton.getText().equals("开始搜索")) {
if(filtersList.getItemCount()<1){
showMessageBox("至少添加一条搜索条件");
}else{
//control.start();
startButton.setText("停止搜索");
}
} else if (startButton.getText().equals("停止搜索")) {
//control.stop();
startButton.setText("开始搜索");
}
}
});
MainApp.logText = new Text(logGroup, SWT.BORDER | SWT.MULTI|SWT.V_SCROLL); // 创建Text对象
MainApp.logText.setText("");
// shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
//control.stop();
display.dispose();
}
public boolean checkInput() {
// Logger.println(maillist.getTextChars().length);
if (maillist.getText().length() < 1) {
// 声明信息对话框对象,并在对话框中显示信息
showMessageBox("邮件地址必须填写");
return false;
} else if (carType.getText().length() < 1
&& price.getText().length() < 1 && miles.getText().length() < 1
&& syear.getText().length() < 1) {
showMessageBox("请至少填写一个搜索条件");
return false;
} else if (carType.getText().length() < 1
&& price.getText().length() < 1 && miles.getText().length() < 1
&& syear.getText().length() != 4) {
showMessageBox("请正确填写年份");
return false;
} else {
}
return true;
}
public void showMessageBox(String message) {
// 声明信息对话框对象,并在对话框中显示信息
MessageBox dialog = new MessageBox(shell, SWT.OK | SWT.ICON_INFORMATION);
dialog.setText("提示");
dialog.setMessage(message);
dialog.open();
}
public static void print(final String str) {
Display.getDefault().syncExec(new Runnable() {
public void run() {
MainApp.logText.append(str);
MainApp.logText.append("\n");
}
});
}
public static void main(String[] args) {
MainApp app = new MainApp();
app.init();
}
}
个人总结
Swt入门容易些,以前从没用过,根据一些例子我就能使用SWT开发界面了,界面的确反应迅速。但是使用中总觉着SWT各种不习惯,布局方式各种繁琐,因为是直接调用本地系统库的原因,界面上一旦有些异常,很容易整个界面直接挂掉,出现内存错误。跨平台没详细测试,调用不同系统上相应的lib库,我感觉问题应该不大。