嵌入式富客户端平台(eRCP)的目的是把 Eclipse 的富客户端平台(RCP)带到嵌入式领域。
eRCP 由以下组件构成:
- 标准部件工具包(eSWT)—— 核心,扩展和移动扩展
- eJFace
- eWorkbench
- eUpdate
我们将讨论每个组件,并在合适的地方使用代码示例。
嵌入式标准部件工具包(eSWT)是著名的 Java™ 图形工具包 —— 标准部件工具包(SWT)—— 的子集。它提供了一套控件、面板和其他常用的部件,作为用户界面的构造块。除了 SWT 中原来包含的部件之外,eSWT 还引入了一个新组件:移动扩展(由 IBM、Nokia 和 Motorola 联合设计的规范),主要针对的是像 PDA 和智能手机这样的移动设备。
从平台独立性的角度来说,eSWT 的设计与它的近亲 SWT 不同。SWT 使用平台独立的方式,试图保持本机代码尽可能简单,以便提高在不同操作系统之间的可移植性。问题在于:可移植性和性能是一对矛盾的问题,所以 eSWT 决定采用另一种方式:通用图形层(UGL),它仍然把 Java 的本机界面(JNI)保留在本机工具包实现上。但是,UGL 没有充当一对一的 JNI 包装器,而试图保持本机实现尽可能接近,只需要通过 JNI 进行回调的信息。eSWT 的方式牺牲了可移植性,因为本机工具包完全依赖于它使用的图形系统,但是这种方式极大地提高了性能(移动设备上的主要考虑因素)。
eSWT 中包含了三个组件(请参阅图 1 ):
- 核心
- 扩展的
- 移动扩展
图 1. eSWT 用户界面工具包
核心和扩展组件是 eSWT 的子集。新发明的移动扩展组件针对的是移动设备。这种组件构成方式支持根据设备的能力和目的,灵活地对设备上要包含哪些组件进行配置。核心组件是必需的,包含运行基本应用程序所必需的最小功能。扩展和移动扩展组件是可选的。
在后面的小节中,我们将研究每个组件,同时提供示例应用程序。
eSWT 核心包含基本的用户界面元素,包括低级图形、事件和基本的部件基础设施。表 1 显示了 eSWT 核心中的类。
| swt.widgets | swt.graphics | swt.events | swt.layout | swt.swt |
|---|---|---|---|---|
| Button | Color | ControlEvent | FormLayout | SWT |
| Canvas | Device | DisposeEvent | FormData | SWTException |
| Combo | Font | FocusEvent | FormAttachment | SWTError |
| Composite | FontData | KeyEvent | - | - |
| Control | FontMetrics | MenuEvent | - | - |
| Decorations | GC | ModifyEvent | - | - |
| Dialog | Image | MouseEvent | - | - |
| Display | ImageData | PaintEvent | - | - |
| Event | PaletteData | SelectionEvent | - | - |
| FileDialog | Point | ShellEvent | - | - |
| Item | Rectangle | TraverseEvent | - | - |
| Label | Resource | TypedEvent | - | - |
| Layout | RGB | VerifyEvent | - | - |
| List | - | - | - | - |
| Menu/MenuItem | - | - | - | - |
| MessageBox | - | - | - | - |
| ProgressBar | - | - | - | - |
| Scrollable | - | - | - | - |
| Scrollbar | - | - | - | - |
| Shell | - | - | - | - |
| Slider | - | - | - | - |
| Synchronizer | - | - | - | - |
| Text | - | - | - | - |
| TypedListener | - | - | - | - |
我们从一个完整的 eSWT 程序开始,这个程序创建一个窗口,在应用程序的标题栏上显示 “HelloWorld”。然后我们将添加一些核心控件(文本、按钮、列表)。对于布局,我们采用 FormLayout 和 FormData。还将在按钮上添加 SelectionListener,它将弹出一个消息框。完整的代码如清单 1 所示。
清单 1:eSWAT 风格的 HelloWorld
01 public class HelloWorldeSWT {
02
03 public static void main(String[] args) {
04 Display display = new Display();
05 final Shell shell = new Shell(display);
06
07 Text text = new Text(shell,SWT.SINGLE);
08 text.setText("This is a text");
09 Button buttonleft = new Button(shell, SWT.PUSH);
10 buttonleft.setText("Left Push!");
11 Button buttonright = new Button(shell, SWT.PUSH);
12 buttonright.setText("Right Push!");
13 buttonright.addSelectionListener(new SelectionListener(){
14 public void widgetSelected(SelectionEvent e) {
15 MessageBox messageBox = new MessageBox(shell,
16 SWT.ICON_INFORMATION| SWT.YES | SWT.NO);
17 messageBox.setText("MessageBox");
18 messageBox.setMessage("Can you see me?");
19 messageBox.open();
20 }
21 public void widgetDefaultSelected(SelectionEvent e) {
22 }});
23 List list = new List(shell,SWT.MULTI|SWT.BORDER);
24 for (int i=0; i<5; i++) {
25 list.add("item "+i);
26 }
27
28 shell.setText("HelloWorld from eSWT");
29 FormLayout layout = new FormLayout();
30 layout.spacing = 5;
31 layout.marginHeight = layout.marginWidth = 9;
32 shell.setLayout(layout);
33
34 FormData textData = new FormData();
35 textData.top = new FormAttachment(0);
36 textData.left = new FormAttachment(0);
37 textData.right = new FormAttachment(90);
38 text.setLayoutData(textData);
39
40 FormData buttonleftData = new FormData();
41 buttonleftData.top = new FormAttachment(text);
42 buttonleftData.left = new FormAttachment(0);
43 buttonleftData.right = new FormAttachment(40);
44 buttonleft.setLayoutData(buttonleftData);
45
46 FormData buttonrightData = new FormData();
47 buttonrightData.top = new FormAttachment(text);
48 buttonrightData.left = new FormAttachment(buttonleft);
49 buttonright.setLayoutData(buttonrightData);
50 FormData listData = new FormData();
51
52 listData.top = new FormAttachment(buttonleft);
53 list.setLayoutData(listData);
54
55 shell.setSize(240,320);
56 shell.open();
57
58 while (!shell.isDisposed()) {
59 if (!display.readAndDispatch())
60 display.sleep();
61 }
62 display.dispose();
63 }
|
图 2 显示了在日文版 PocketPC 设备上运行的应用程序。当用户按下 Right Push! 按钮时,就出现一个消息框(如图 3 所示)。eSWT 利用底层的本机图形支持来提供一致的用户界面(本机应用程序的观感,而不是发明一个新外观)。
图 2. eSWT 风格的 HelloWorld
图 3. 具有本机观感的消息框
对于清单 1 中的代码实现的效果,值得逐行进行解释。
- 行 04-05:
-
创建了显示和外壳。外壳把显示设置成自己的双亲,并把它变成顶级窗口。在 PocketPC 平台上,顶级窗口 全都自动最大化,保持一致性。我们用 final 限定符创建外壳,因为稍后要在
SelectionListener中使用它。
- 行 07-08:
-
创建文本并调用
setText()来设置文本字符串。
- 行 09-22:
-
创建了两个按钮。在右侧的按钮上,添加了
SelectionListener,以弹出一个简单的消息框。
- 行 23-26:
- 创建了一个包含五个项目的列表。
- 行 28:
- 设置窗口标题栏上的文本。
- 行 29-53:
-
用
FormLayout、FormData和FormAttachment协助布局过程。
- 行 55-56:
-
设置外壳尺寸并打开外壳。在这个示例中,
setSize()对顶级窗口不生效(因为设备的原因)。
- 行 58-61:
-
在
while()循环内部建立了一个显式循环,不断地读取和分派来自操作系统的用户事件。如果再没有事件发生,就调用display.sleep()并进入睡眠,等待下一个事件。
- 行 62:
-
代码最后一行的
display.dispose()调用显式地清除显示,并释放 eSWT 应用程序的所有相关资源。
eSWT 扩展是可选组件,包含多个复杂的用户界面元素和布局。这些功能通常在高端移动设备和 PDA 中才能找到。表 2 显示了 eSWT 扩展中的类的详细列表。
| org.eclipse.swt.widgets | org.eclipse.swt.browser | org.eclipse.swt.dnd | org.eclipse.swt.graphics |
|---|---|---|---|
| ColorDialog | Browser | ByteArrayTransfer | ImageLoader |
| DirectoryDialog | LocationEvent | Clipboard | - |
| FontDialog | ProgressEvent | TextTransfer | - |
| Table | StatusTextEvent | Transfer | - |
| Tree | TitleEvent | TransferData | - |
Browser 部件是 eSWT 扩展中一个有意思的部件。我们将用 Broswer 控件创建一个完全能够工作的 Web 浏览器(请参阅清单 2)。在这个示例中将允许用户设置 URL、前进、后退或重新装入页面。
清单 2. 简单的 HTML 浏览器
public class BrowserTest {
static Button prev, reload, next, go;
static Text url;
static Browser browser;
public static void main(String[] args) {
final Display display = new Display();
Shell shell = new Shell(display);
//set window title and size
shell.setText("SimpleBrowser");
shell.setSize(240,320);
//previous button
prev = new Button(shell, SWT.PUSH);
prev.setText("<<");
prev.addSelectionListener(new SelectionListener(){
public void widgetSelected(SelectionEvent e) {
browser.back();
}
public void widgetDefaultSelected(SelectionEvent e) {
}});
//reload button
reload = new Button(shell, SWT.PUSH);
reload.setText("R");
reload.addSelectionListener(new SelectionListener(){
public void widgetSelected(SelectionEvent e) {
browser.refresh();
}
public void widgetDefaultSelected(SelectionEvent e) {
}});
//next button
next = new Button(shell, SWT.PUSH);
next.setText(">>");;
next.addSelectionListener(new SelectionListener(){
public void widgetSelected(SelectionEvent e) {
browser.forward();
}
public void widgetDefaultSelected(SelectionEvent e) {
}});
// url text
url = new Text(shell, SWT.SINGLE|SWT.BORDER);
url.setText("http://");
// go button
go = new Button(shell, SWT.PUSH);
go.setText("GO");
go.addSelectionListener(new SelectionListener(){
public void widgetSelected(SelectionEvent e) {
// TODO Auto-generated method stub
browser.setUrl(url.getText());
}
public void widgetDefaultSelected(SelectionEvent e) {
}});
// browser
browser = new Browser(shell, SWT.NONE);
browser.setUrl("http://www.google.com");
FormLayout formLayout = new FormLayout();
shell.setLayout(formLayout);
formLayout.spacing = 1;
formLayout.marginHeight = formLayout.marginWidth = 2;
FormData prevData = new FormData();
prevData.left = new FormAttachment(0);
prevData.top = new FormAttachment(0);
prevData.width = 16;
prev.setLayoutData(prevData);
FormData reloadData = new FormData();
reloadData.left = new FormAttachment(prev);
reloadData.top = new FormAttachment(0);
reloadData.width = 16;
reload.setLayoutData(reloadData);
FormData nextData = new FormData();
nextData.left = new FormAttachment(reload);
nextData.top = new FormAttachment(0);
nextData.width = 16;
next.setLayoutData(nextData);
FormData urlData = new FormData();
urlData.left = new FormAttachment(next);
urlData.top = new FormAttachment(0);
urlData.right = new FormAttachment(89);
urlData.bottom = new FormAttachment(browser);
url.setLayoutData(urlData);
FormData goData = new FormData();
goData.left = new FormAttachment(url);
goData.top = new FormAttachment(0);
goData.width = 24;
go.setLayoutData(goData);
FormData browserData = new FormData();
browserData.top = new FormAttachment(prev);
browserData.left = new FormAttachment(0);
browserData.right = new FormAttachment(100);
browserData.bottom = new FormAttachment(100);
browser.setLayoutData(browserData);
shell.open();
while( !shell.isDisposed() ) {
if( !display.readAndDispatch() )
display.sleep();
}
display.dispose();
}
}
|
图 4 显示了在 PocktPC 上运行的简单浏览器。
图 4. 简单的浏览器
eSWT 移动扩展是可选组件,提供了在无数移动设备上常见的用户界面元素。表 3 显示了 eSWT 移动扩展组件中的类。
| org.eclipse.ercp.swt.mobile |
|---|
| CaptionedControl |
| Command |
| ConstrainedText |
| DateEditor |
| HyperLink |
| Input |
| ListBox/ListBoxItem |
| MobileDevice/MovileDeviceEvent/MobileDeviceListener |
| MobileShell |
| MultiPageDialog |
| QueryDialog |
| Screen/ScreenEvent/ScreenListener |
| SortedList |
| TaskTip |
| TextExtension |
| TimedMessageBox |
在这个示例中,将采用 MobileShell 代替 Shell 来提供全屏幕功能。MobileShell 在顶部包含一个 SortedList,在底部包含一个 ListView。命令与 MobileShell 关联起来,让用户可以改变全屏幕模式,并用 ListView 改变布局的密度。源代码如清单 3 所示。
清单 3. MobileExtension 示例
public class MobileExtensionSample implements IPlatformRunnable {
public static void main(String[] args) {
Display display = new Display();
final MobileShell shell = new MobileShell(display);
final Button resetButton = new Button(shell, SWT.PUSH|SWT.BORDER);
Command shellCommand = new Command(shell, Command.SELECT,0);
shellCommand.setText("FullScreen");
shellCommand.addSelectionListener(new SelectionListener(){
public void widgetSelected(SelectionEvent e) {
shell.setFullScreenMode(true);
resetButton.setVisible(true);
}
public void widgetDefaultSelected(SelectionEvent e) {
}});
resetButton.setText("Normal Screen");
resetButton.addSelectionListener(new SelectionListener(){
public void widgetSelected(SelectionEvent e) {
shell.setFullScreenMode(false);
resetButton.setVisible(false);
}
public void widgetDefaultSelected(SelectionEvent e) {
}});
// Create SortedList and add items
SortedList sortedList = new SortedList(
shell,
SWT.MULTI|SWT.V_SCROLL|SWT.BORDER,
SortedList.FILTER);
sortedList.add("banana");
sortedList.add("123");
sortedList.add("12");
sortedList.add("happyhour");
sortedList.add("toobad");
sortedList.add("youknowwhat");
sortedList.add("yes");
sortedList.add("886222333");
// Create ListView and add items with image set
Image[] image = new Image[4];
image[0] = new Image(
Display.getDefault(),
MobileExtensionSample.class.getResource\
AsStream("/icons/sample.gif"));
image[1] = new Image(
Display.getDefault(),
MobileExtensionSample.class.getResource\
AsStream("/icons/sample.gif"));
image[2] = new Image(
Display.getDefault(),
MobileExtensionSample.class.getResource\
AsStream("/icons/sample.gif"));
image[3] = new Image(
Display.getDefault(),
MobileExtensionSample.class.getResource\
AsStream("/icons/sample.gif"));
final ListView lv = new ListView(shell, SWT.MULTI|SWT.BORDER);
for (int i=0; i<20; i++) {
lv.add("item"+i, image[i % 4]);
}
//Create a Command for setting low density
Command lowCommand = new Command(lv, Command.SELECT, 0);
lowCommand.setText("LOW");
lowCommand.addSelectionListener(new SelectionListener(){
public void widgetSelected(SelectionEvent e) {
lv.setLayoutDensity(ListView.LOW);
}
public void widgetDefaultSelected(SelectionEvent e) {
}});
//Create a Command for setting high density
Command midCommand = new Command(lv, Command.SELECT, 0);
midCommand.setText("MEDIUM");
midCommand.addSelectionListener(new SelectionListener(){
public void widgetSelected(SelectionEvent e) {
lv.setLayoutDensity(ListView.MEDIUM);
}
public void widgetDefaultSelected(SelectionEvent e) {
}});
FormLayout layout = new FormLayout();
layout.spacing = 2;
layout.marginHeight = layout.marginHeight = 2;
shell.setLayout(layout);
FormData sortedListData = new FormData();
sortedListData.top = new FormAttachment(0);
sortedListData.left = new FormAttachment(0);
sortedListData.right = new FormAttachment(100);
sortedListData.height = 120;
sortedList.setLayoutData(sortedListData);
FormData lvData = new FormData();
lvData.top = new FormAttachment(sortedList);
lvData.right = new FormAttachment(100);
lvData.left = new FormAttachment(0);
lvData.height = 130;
lv.setLayoutData(lvData);
FormData resetData = new FormData();
resetData.top = new FormAttachment(lv);
resetData.left = new FormAttachment(0);
resetButton.setLayoutData(resetData);
resetButton.setVisible(false);
shell.setSize(240,320);
shell.setText("Mobile Example");
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}
|
图 5 显示了在 PocketPC 平台上运行的结果,图 6 显示了全屏幕模式。
图 5. 带有 SortedList 和 ListView 的 MobileShell
图 6. 全屏幕 MobileShell
eJFace 是一个依赖 eSWT 的平台独立的用户界面工具包。eJFace 提供了一套组件和辅助工具,可以简化基于 eSWT 的应用程序的开发(通过对 eSWT 部件进行包装),就像 JFace 帮 SWT 做的事一样。实际上,eJFace 是 JFace 的一个严格的子集,所以它与它的近亲有许多相似性。eJFace 提供了对资源管理、查看器、动作和首选项页面的支持。在 参考资料 中有介绍 JFace 的教程,可以帮助您使用 eJFace。
eJFace 中可用的查看器类型有:
CheckBoxTableViewerCheckBoxTreeViewerComboViewerListViewerTableViewerTreeViewer
eWorkbench 允许 eRCP 应用程序在一个工作台窗口中同时运行,类似在 RCP 中的工作情况。eWorkbench 客户机提供了特定显示场景的视图,而且 eWorkbench 会自动根据使用的移动设备决定使用哪个视图。在 eWorkbench 中,没有透视图(perspective)的概念 —— 可以把它当成只有一个共享透视图的应用程序 —— 原因是这个概念在嵌入式设备上不适用。
创建 eWorkbench 应用程序只需要几步(RCP 开发人员会很熟悉)。过程与使用 contribution 的概念创建 Eclipse RCP 应用程序的过程类似。
eWorkbench 允许定义三类视图,这三类视图都扩展自 org.eclipse.ui.part.ViewPart。正常视图是必需的,其他两个视图是可选的。
- 正常(Normal): 默认视图
- 大(Large): 显示器比较大的时候使用这个视图。
- 状态(Status): 显示器比较小的时候使用这个视图
现在创建一个示例视图。
清单 4. 示例视图
public class DefaultView extends ViewPart {
public void createPartControl(Composite parent) {
//create a composite with fill layout to host a label
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(new FillLayout());
// create a label
Label label = new Label(composite,SWT.CENTER);
label.setText("Hello eWorkbench!"); }
public void setFocus() {}
}
|
通过使用扩展点机制,让 Eclipse 知道有可用的视图(请参阅清单 5)。
清单 5. plugin.xml
<extension point="org.eclipse.ui.views"> <view allowMultiple="false" category="org.eclipse.ercp.eworkbench.viewCategory" class="com.ibm.ercp.application.views.DefaultView" icon="icons/sample.gif" id="com.ibm.ercp.application.defaultView" name="Sample DefaultView"/> </extension> |
步骤 2:定义 eWorkbench contribution
要成为 eWorkbench 应用程序,必须扩展 org.eclipse.ercp.eworkbench.applications 扩展点,并提供一些信息(请参阅清单 6 中的示例):
- id: 代表 eWorkbench 应用程序的惟一标识符
- name: 应用程序的名称(在工作台上显示)
- views: 应用程序支持的视图(正常、大、状态)
清单 6. plugin.xml
<extension point="org.eclipse.ercp.eworkbench.applications"> <application id="com.ibm.ercp.application" name=\ "IBM Sample Application" singleton="true"> <views normal="com.ibm.ercp.application.views.normal" /> </application> </extension> |
图 7 显示了 eWorkbench 应用程序列表的一个挨一个的截屏,后面是刚启动的示例应用程序。
图 7. Hello, eWorkbench
RCP 提供的一个优势是用更新管理器界面从中央服务器更新插件的能力。也有与更新管理器关联的特性,例如调度更新。而且,通过使用开放网格服务基础设施(OGSI)和插件,可以动态地安装特性。eRCP 对这些优势的回答是 eUpdate,在编写这篇文章的时候它正在开发当中。
eUpdate 为需要包含更新管理功能的插件提供逻辑和用户界面。可以在这些插件的帮助下,或者通过使用 eUpdate 工作台应用程序提供对更新相关信息进行配置的完整 GUI,编写自己的更新功能。
这篇文章介绍了嵌入式富客户端平台(eRCP)和它的各种组件,提供了示例代码,包括示例 eWorkbench 应用程序和其他可以作为独立示例的代码清单。
| 描述 | 名字 | 大小 | 下载方法 |
|---|---|---|---|
| Sample code | os-ecl-rcp-com.ibm.ercp.application_1.0.0.jar | 25KB | HTTP |
学习
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文。
-
了解关于 embedded Rich Client Platform 的更多信息。
-
通过 Eclipse.org 上的 “How to use the JFace Tree Viewer” 了解使用 JFace 的更多信息。
-
用几个 SWT Snippets 学习更多关于使用 eSWT 的信息。
-
了解关于 Eclipse Foundation 和它的众多项目的更多信息。
-
访问 developerWorks 的 Eclipse 项目参考资料 了解关于 Eclipse 的更多信息。
-
保持关注 developerWorks 技术活动和 webcasts。
-
请访问 developerWorks 开放源码专区 获得丰富的 how-to 信息、工具和项目更新,帮助您用开放源码技术进行开发,并在 IBM 的产品上利用它们。
获得产品和技术
-
请参阅 IBM alphaWorks 上最新的 Eclipse 技术下载。
-
请用 IBM 试用版软件 革新您的下一个开放源码开发项目,这可以下载或从 DVD 上得到。
讨论
-
通过加入 eRCP 邮件列表 参与 eRCP 开发。
-
请先在 eRCP 新闻组 上寻求帮助。
-
Eclipse 新闻组 有许多资源,针对有意使用和扩展 Eclipse 的人们。
-
通过参与 developerWorks blogs 加入 developerWorks 社区。

Chris Aniszczyk 是 IBM Lotus 的软件工程师和 IBM 的 Extreme Blue 实习项目的毕业生。他是衷心的开放源码支持者,一直在 Gentoo Linux (http://www.gentoo.org)发布方面工作,是 Eclipse Modeling Framework Technology(EMFT)项目的参与者。

Uriel Liu 是 IBM 中国软件开发实验室的软件开发人员,一直从事 WED 客户技术。他也是 eRCP 项目的参与者。