站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Eclipse 专题将基于 Swing 的开发工具插入 Eclipse 中

将基于 Swing 的开发工具插入 Eclipse 中

摘要: 将基于 Swing 的开发工具插入 Eclipse 中
<table cellSpacing=0 cellPadding=0 width="100%" border=0> <tr vAlign=top> <td width=2></td> <td> <td width=8></td> <td vAlign=bottom align=right width=180>英文原文</td> <td width=6></td></tr> <tr vAlign=top> <td bgColor=#000000 colSpan=5></td></tr> <tr vAlign=top> <td bgColor=#ffffff colSpan=5></td></tr></table> <table cellSpacing=0 cellPadding=0 width="100%" border=0> <tr vAlign=top> <td width=10></td> <td width="100%"> <table cellSpacing=0 cellPadding=0 width=168 align=right border=0> <tr> <td width=8></td> <td width=160> <table cellSpacing=0 cellPadding=0 width=160 border=0> <tr> <td width=160 bgColor=#000000 height=1></td></tr> <tr> <td align=middle background=/developerWorks/cn/i/bg-gold.gif height=5>内容:</td></tr> <tr> <td width=160 bgColor=#666666 height=1></td></tr> <tr> <td align=right> <table cellSpacing=0 cellPadding=3 width="98%" border=0> <tr> <td>引言</td></tr> <tr> <td>假定的 Swing 编辑器:Ed</td></tr> <tr> <td>基本概念</td></tr> <tr> <td>编辑器集成</td></tr> <tr> <td>双向传递</td></tr> <tr> <td>首选项页面</td></tr> <tr> <td>Workbench 知晓性</td></tr> <tr> <td>结束语</td></tr> <tr> <td>参考资料</td></tr> <tr> <td>关于作者</td></tr> <tr> <td>对本文的评价</td></tr></table></td></tr></table> <table cellSpacing=0 cellPadding=0 width=160 border=0> <tr> <td width=160 bgColor=#000000 height=1></td></tr> <tr> <td align=middle background=/developerWorks/cn/i/bg-gold.gif height=5>相关内容:</td></tr> <tr> <td width=160 bgColor=#666666 height=1></td></tr> <tr> <td align=right> <table cellSpacing=0 cellPadding=3 width="98%" border=0> <tr> <td>Working the Eclipse Platform </td></tr></table></td></tr></table> <table cellSpacing=0 cellPadding=0 width=160 border=0> <tr> <td width=160 bgColor=#000000 height=1></td></tr> <tr> <td align=middle background=/developerWorks/cn/i/bg-gold.gif height=5>Java 专区中还有:</td></tr> <tr> <td width=160 bgColor=#666666 height=1></td></tr> <tr> <td align=right> <table cellSpacing=0 cellPadding=3 width="98%" border=0> <tr> <td>教学</td></tr> <tr> <td>工具与产品</td></tr> <tr> <td>代码与组件</td></tr> <tr> <td>所有文章</td></tr> <tr> <td>实用技巧</td></tr></table></td></tr></table> <table cellSpacing=0 cellPadding=0 width=160 border=0> <tr> <td width=150 bgColor=#000000 colSpan=2 height=2></td></tr> <tr> <td width=150 bgColor=#ffffff colSpan=2 height=2></td></tr></table>

Terry Chan(terrych@ca.ibm.com)
软件工程师,IBM Canada Ltd.
2003 年 1 月

学习如何将独立的、基于 Swing 的编辑器作为插件集成到 Eclipse Platform 中。通过使用一些简单的技术,您就可以在 Swing 工具、Eclipse Platform 和各种 SWT 小窗口(widget)之间共享资源,而且这些资源可以通过相互知晓性(mutual awareness)通信。工具供应商如果打算将基于 Eclipse 的开发工具引入市场,又想最低限度地重新编码,也将发现本文有所帮助。

引言
Eclipse Platform 为工具开发提供一组健壮的服务和 API。它使来自完全不同的供应商的工具之间的集成变得平滑,为不同类型的开发工作创建了一个无缝的环境。

Eclipse Platform 的软件组件之一就是 SWT。尽管 SWT 不是 Platform 的一个核心组件集,但它还是不可或缺的,因为它为产品和插件开发者提供了一组基于 Java 的 GUI 小窗口。SWT 与操作系统无关且非常方便,然而它的底层 JNI 接口将展现本机平台的外观和感觉(look-and-feel)以及性能。

总体上讲,对于那些想要编写在 Platform 的各种框架中运行良好且视觉上有吸引力的插件的开发者和供应商来说,SWT 提供了一个优秀的解决方案。然而,SWT 与 Java 的 Swing GUI 小窗口之间的互操作性程度相当低,这一点对 SWT 影响很大。例如,Swing 和 SWT 使用完全不同的事件处理机制。这个差异常常会使由 Swing 和 SWT 共同组成的 GUI 不可用。

为了在 Swing 和 SWT 之间提供一个接口以便提供可接受级别的兼容性,我们已经做了一些工作,比如使开发者能够将 Swing 小窗口嵌入到 SWT 中的 org.eclipse.swt.internal.swt.win32.SWT_AWT 实用程序类。但是,这些方法仍然是实验性的,尚未获得官方支持 — 由此包名内含有“internal”。

这个拙劣的互操作性对于 Eclipse 项目和工具供应商来说,都是令人遗憾的障碍。目前,大量软件开发和测试工具提供用 Swing 编写的用户界面。将一个带有复杂的 Swing GUI 的现有工具移植到 SWT 需要来自供应商的相当多的时间和投资。尽管 Eclipse Platform 具有了所有先天的优势,但是 Swing 和 SWT 之间拙劣的互操作性导致开发成果不那么吸引人。

本文向您说明了如何实现下列操作:

本文引入了一些简单的技术来实现上述操作,无需使用任何不被支持的 API。我们不引用任何内部类并且遵守所有通用的插件规则。为了最有效地使用这些技术,您应该具有编写插件和使用插件开发环境(Plug-in Development Environment)的基本知识,您还应该具有对基于 Swing 的编辑器的源代码的访问权。

假定的 Swing 编辑器:Ed
为了模拟真实的各种工具集成的情况,我们来使用一个假定的基于 Swing 的编辑器(名为“Ed”)。下面是 Ed 的一些特征:

基本概念
由于 Swing 和 SWT 互操作性的限制,难以获得这二者之间的直接通信(如事件处理)。在既不使用不被支持的 API 也不服从任何插件开发规则的情况下,实现这个目的的一条途径就是避免它们彼此嵌入,取而代之的是让它们拥有独立的 Frame。插件类(或者说 Singleton 实用程序类)将处理它们之间的通信,如图 1 所示。例如,Ed 上的 JButton 可以使一个 SWT Shell 出现,显示已编辑的 ThirdParty.java 的一些特定于 Workbench 的属性(如 Project References)。

图 1. Singleton 实用程序类

编辑器集成
集成的主要目的是开发一个用 Ed 作为在 Workbench 中找到的任何 ThirdParty.java 的缺省编辑器的插件。

准备插件项目
在 Workbench 中,创建一个新的插件项目“org.eclipse.jumpstart.editorintegration”,然后选择 Create plug-in project 向导中的“Create plug-in using a template wizard”选项。单击 Next。选中“Add default instance acces”选项,然后单击 Finish。Workbench 切换到 Plug-in Development Perspective。一个空白的插件清单(manifest)文件以及继承了 AbstractUIPlugin 的插件类 EditorintegrationPlugin 是被自动创建的。还生成了插件类的一个私有静态实例以及 getter 方法。

插件清单文件编辑器应该是打开的;如果没打开,双击 plugin.xml 启动它。

该插件需要下面这些库。将它们添加到插件项目的 Java Build Path 下:

插件清单文件
因为这个插件只处理名为 ThirdParty.java 的 Java 文件,所以我们需要为这些 Java 文件指定一个编辑器。在插件清单文件编辑器中,切换到 Extensions 选项卡,然后添加扩展点“Internal and External Editors”。将 default 设为“true”,将 name 设为“Ed - Swing Editor”,将 filenames 设为“ThirdParty.java”,将 launcher 设为“org.eclipse.jumpstart.editorintegration.EdLauncher”。添加的扩展点的源代码看上去应该如清单 1 所示:

清单 1. 添加一个扩展点 <table cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1> <tr> <td>










</td></tr></table>

Ed 现在是所有 ThirdParty.java 文件的缺省编辑器,如图 2 所示。

图 2. Ed 是所有 ThirdParty.java 文件的缺省编辑器

请注意:一定要包括 icons/thirdparty.gif 文件,它被作为“Open With”菜单中所有 ThirdParty.java 文件的缺省编辑器显示。

集成 Ed 源代码
将 Ed 的源代码导入到插件项目中。如何调用 Ed 由您决定。插件类可以包含一个 Ed 私有域以及一个相应的公有 getter 方法:

清单 2. Ed 作为私有域 <table cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1> <tr> <td>


private Ed ed = null;

public Ed getEd()

{

   if (ed == null)

   {

      ed = new Ed ();

   }

   return ed;

}

</td></tr></table>

另外,插件类可以为每个已启动的 ThirdParty.java 文件返回 Ed 的一个单独的实例。您在该插件维护和提供的 Singleton 实用程序类中实现这两种方式中的哪一种都可以。

编辑器启动程序(launcher)
因为插件使用扩展点 org.eclipse.ui.editors,所以它必须为 Eclipse Platform 提供一个清单文件中指定的编辑器启动程序类。

创建类 org.eclipse.jumpstart.editorintegration.EdLauncher 以实现接口 IEditorLauncher(如果没找到这个接口,请确保 workbench.jar 文件包含在 Project Path 中;请参阅准备插件项目)。请一定要选中 Wizard 中的“Inherited abstract methods”选项。

每次双击 ThirdParty.java 文件时,Eclipse Platform 都执行 EdLauncher.open(IFile) 来调用该文件类型的缺省编辑器。Platform 将单击的构件作为 IFile 传送给方法。在这种情况下,IFile 是一个 Java 源文件,因此您可以将它因此您可以将它强制转型为 ICompilationUnit。

由于 Ed 并不是为了处理 JDT 对象而设计的,所以您必须从 ICompilationUnit 中抽取源代码内容并将它放到 Ed 中以便查看:

<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1> <tr> <td>


EditorintegrationPlugin.getDefault().getEd().getEditorPane().setText

   (ICompilationUnit.getSource());

EditorintegrationPlugin.getDefault().getEd().show();

</td></tr></table>

一旦执行了 show() 方法,Ed 就被作为主 Workbench 窗口外部的一个 JFrame 显示(请参见图 3)。插件记录已编辑的 ThirdParty.java 的项目名称和包名称。当您试图保存 Ed 中所作的更改时,该信息是至关重要的。

图 3. Swing 编辑器显示在 Workbench 外面

双向传递(round-tripping):将源代码的更改返回到 Workbench 中
传统的编辑器将在平面文件、二进制资源库中保存源代码,或者将源代码保存到源代码控制系统中。作为一个编辑器,Ed 需要一些方法来保存它显示的对源代码的更改。

Ed 有一个“Save”按钮(JButton),如 Swing 编辑器:Ed 中所描述。按下按钮后,actionPerformed() 方法被调用,Save 按钮触发一个事件。实现一个事件侦听器的对象接收事件并执行源代码保存操作。

您可以用 Singleton 实用程序类(请参阅编辑器启动程序)作为实现事件侦听器的对象。实用程序类一接收到来自 Save 按钮的事件对象,就从 Ed 中抽取源代码,然后将源代码放入对应的 Workbench 对象中。保存到文件系统的实际工作被委托给 Eclipse Platform。

多个文件在 Workbench 中可能拥有相同的名称。这就是 ThirdParty.java 的项目名称和包名称有用的地方。该信息由插件存储。确切的实现方式由您决定。假定编辑器存储信息,您可以在实用程序类中使用下列代码片段(snippet):

清单 3. 管理文件名称 <table cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1> <tr> <td>


public void saveButtonPressed() {

   try {

      IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();

  

      IProject myProj = root.getProject(getEd().getProjectname());

  

      IFolder myFolder = myProj.getFolder(getEd().getPackageName());

  

      IJavaElement myPackageFragment = JavaCore.create(myFolder);

  

      if (myPackageFragment != null) {

         IPackageFragment packageFrag = (IPackageFragment)myPackageFragment;

     

         String sourceFromEd = getEd().getJEditorPane1().getText();

     

         ICompilationUnit icu = packageFrag.getCompilationUnit("ThirdParty.java");

     

         icu.getBuffer().setContents(sourceFromValidator);

     

         icu.save(null, true);

      }

      else {

         System.out.println("myPackageFragment is null.");

      }

   } catch (Exception e) {

      e.printStackTrace();

   }

}

</td></tr></table>

逆向进行双向传递
清单 3 处理“正向”双向传递。还需要“反向”双向传递来把用 Eclipse Platform 的 JDT Java 编辑器在 ThirdParty.java 中所作的任何更改带回到 Ed。

实用程序类可以实现接口 org.eclipse.jdt.core.IElementChangedListener,您可以用这个接口跟踪对任何 IElements(包括 ICompilationUnit)作的更改。当源代码更改被引入到 Workbench 中的 Java 文件内时,调用方法 elementChanged(ElementChangedEvent)

您需要有选择地过滤出那些不涉及 Ed 插件的 IElement 更改。一种过滤方式是从 IElementChangedEvent 参数中抽取 IJavaElementDelta 对象并对其进行检查。例如,下面的语句过滤 Ed 插件情况下不相关的源代码更改。

清单 4. 过滤不相关的源代码更改 <table cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1> <tr> <td>


IJavaElementDelta delta = event.getDelta();

if (delta != null) {

   if(delta.getElement().getElementName().equalsIgnoreCase("ThirdParty.java")) {

      //code to update Ed's editor panel.

   }

}

</td></tr></table>

对于非 Java 构件的编辑器,IElementChangedListener 不能捕获在 Workbench 中所作的更改。Eclipse Platform 提供接口 org.eclipse.core.resources.IResourceChangeListener 来处理对非 Java 资源所作的更改。

首选项页面
要为用户提供丰富的、易于使用的功能,工具应该提供可以通过启动参数访问的、或者可以通过 GUI(它不是编辑器的核心图形界面的一部分)访问的可配置的选项。在用于 Eclipse Platform 的插件的情况中,强烈推荐通过 Platform 的 Preference Page 框架(Window -> Preferences)对这些选项进行配置。

为了举例起见,我们将 Ed 的颜色作为一个使用 Platform 首选项页面的可配置的选项来控制。

在插件清单文件中添加一个首选项页面扩展点
在 Eclipse Platform 中,首选项页面被定义为一个扩展点。要使用它,请将它添加到插件清单文件编辑器中,或者将下列代码放入 plugin.xml 中:

清单 5. 将首选项页面添加到 plugin.xml <table cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1> <tr> <td>








</td></tr></table>

首选项页面类
首选项页面继承了 org.eclipse.jface.preference.PreferencePage。在这个示例中,简单的首选项页面由三个最大值为 255 的滑动条(slider bar)组成,表示 Ed 的 java.awt.Color 对象的颜色(红、绿和蓝)。

在插件项目中创建清单文件中指定的类 org.eclipse.jumpstart.editorintegration.EdPreferencePage1。这个类必须继承 org.eclipse.jface.preference.PreferencePage 并实现接口 org.eclipse.ui.IWorkbenchPreferencePage

首选项页面呈现出与编辑器启动程序类似的编码问题:JFace/SWT 将如何与 Swing 通信?幸运的是,同样的方式适用。例如,performApply() 方法可能看上去像这样:

清单 6. performApply() 方法 <table cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1> <tr> <td>


protected void performApply() {

   int red = redSWTSlider.getSelection();

   int green = greenSWTSlider.getSelection();

   int blue = blueSWTSlider.getSelection();

  

   java.awt.Color newColor = new java.awt.Color(red, green, blue);

   EditorintegrationPlugin.getDefault().getEd().getContentPane().setBackground(

      newColor);

}

</td></tr></table>

插件应该使用 Platform 的 Preference Store 机制存储已配置的值,任何其他的插件也应该这么做。performOk() 方法可能看上去像这样:

清单 7. performOk() 方法 <table cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1> <tr> <td>


public boolean performOk() {

   getPreferenceStore().setValue("redValue", redSWTSlider.getSelection();

   getPreferenceStore().setValue("greenValue", greenSWTSlider.getSelection());

   getPreferenceStore().setValue("blueValue", blueSWTSlider.getSelection());

   return true;

}

</td></tr></table>

图 4 中显示了从首选项页面控制 Swing 编辑器的颜色。

图 4. 从首选项页面控制 Swing 编辑器的颜色

Workbench 知晓性
由于大多数编辑器最初被设计为独立的 Java 应用程序设计,所以它们不注意 Workbench 的存在。它们可能不能处理 Platform 的一些环境属性,这就限制了编辑器与 Platform 集成的亲密度。为了给用户提供一个更平滑更一致的开发体验,开发者和插件供应商应该认真考虑增强他们现有的 Swing 工具从而使其变为 Workbench 知晓的。

例如,Ed 被编码为直接处理基于文件系统的 Java 文件。因此,Platform 的 Java Project 和 Project Reference 与 Ed 无关。在这一部分中,我们将把 JButton 添加到 Ed 以启动一个 SWT 对话框,该对话框显示了已编辑的 ThirdParty.java 被引用的项目。从用户角度看,他单击一个 Swing 小窗口,触发了一个显示特定于 Workbench 的信息的 SWT 窗口,这表示 Swing 编辑器、SWT 以及 Workbench 彼此正在紧密交互。

增强编辑器
假设您具有对 Ed 源代码的访问权,您可以添加额外的 Swing 小窗口以获得额外的 Workbench 知晓性功能。将 JButton 添加到编辑器的主内容窗格,然后它会启动一个 SWT 对话框。将 JButton 的文本设置为“Referenced Project”。

Referenced Project 按钮的事件处理机制的工作方式将与 Save 按钮(请参阅双向传递:将源代码的更改返回到 workbench 中)的工作方式类似。插件实用程序类将侦听来自这个按钮的事件。实用程序类一接收到 Referenced Project 按钮触发的一个事件对象,它就会执行必要的操作来检索项目引用信息并在 SWT 中显示该信息。

检索项目引用信息
在 SWT 对话框可以被显示之前,插件需要弄明白包含已编辑的 ThirdParty.java 的项目引用了 Workbench 中的哪些项目。这是插件类的工作,而且它可以使用如清单 8 所示的一种方法,其中传入该方法的字符串变量是项目的名称:

清单 8. 检索项目引用信息 <table cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1> <tr> <td>


private String[] getReferencedProjectArray(String arg) {

   String[] projectNameArray = null;

  

   try {

      IProject[] referencedProjects =

         ResourcesPlugin.getWorkspace().getRoot().getProject(

            arg).getReferencedProjects();

     

      int referencedProjectsLength = referencedProjects.length;

     

      if (referencedProjectsLength == 0) {

         projectNameArray = new String[1];

         projectNameArray[0] = "none";

      }

      else {

         projectNameArray = new String[referencedProjectsLength];

         for (int i=0; i < referencedProjectsLength; i++) {

            projectNameArray[i] = referencedProjects[i].getName();

         }

      }

      return projectNameArray;

   } catch (Exception e) {

      e.printStackTrace();

      return null;

   }

}

</td></tr></table>

SWT 对话框
Project Referenced SWT 对话到底应该会是什么样子要由插件 GUI 设计师决定。在这个示例中,一个带有 List 对象的简单的 SWT Shell(要显示引用的项目)就足够了:

清单 9. 带有 List 对象的 SWT Shell <table cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1> <tr> <td>


public class SWTProjectReferenceFrame implements Runnable {

   private Shell shell;

   private Display display;

   Thread myThread;

  

   public void run() {

      open();

   }

  

   public void open() {

      display = new Display();

      shell = new Shell(display);

      shell.setLayout(new org.eclipse.swt.layout.GridLayout());

      shell.setText("Projects Referenced - SWT Frame");

      shell.setSize(400, 400);

      createListGroup();

      shell.open();

     

      while (!shell.isDisposed()) {

         if (!display.readAndDispatch()) {

            EditorintegrationPlugin.getDefault().getEd().repaint();

            display.sleep();

         }

      }

      myThread = null; // disposing the thread when the SWT window is disposed.

   }

  

   // Other methods appear here ...

}

</td></tr></table>

方法 createListGroup() 准备了 List 对象并设置其内容以包含 projectNameArray(请参阅检索项目引用信息)。

清单 10. 准备 List 对象 <table cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1> <tr> <td>


private void createListGroup() {

   Group listGroup = new Group(shell, SWT.NULL);

   listGroup.setLayout(new GridLayout());

   listGroup.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL |

                                        GridData.HORIZONTAL_ALIGN_FILL |

                                        GridData.VERTICAL_ALIGN_FILL));

   listGroup.setText("listGroup");

  

   List list = new List(listGroup, SWT.V_SCROLL);

   list.setItems(projectNameArray);

}

</td></tr></table>

根据启动 SWT 对话框的方式,您可能需要在一个单独的线程(如清单 10 中的 myThread 对象所指出的那样)中执行 SWT 窗口以避免在 Swing 编辑器中的重绘制(repaint)问题。

图 5 中显示了 Swing 按钮启动一个 SWT 框架。

图 5. 从 Swing 按钮启动一个 SWT 框架

结束语
这里描述的这些技术提供了一个临时的解决方案,它可以帮助您快速地将基于 Swing 的工具集成到 Eclipse Platform 中。但是,只要有可能,您就应该在现有的 Swing 小窗口上使用紧密集成的 SWT/JFace 组件。例如,编辑器应该用 Eclipse Platform 的 Preference Page 框架作为配置插件的中心入口点,而不是用各个引用对话框框架来处理多个用户引用。

尽管本文中的这些概念相对简单且易于实现,但是请不要将 Swing 小窗口作为永久设备留在插件中。要控制和利用 Eclipse 项目中的所有服务,您就应该逐渐减少插件中陈旧的 Swing 代码的数量以便支持 Eclipse 项目提供的各种框架。

参考资料

<table cellSpacing=0 cellPadding=0 width="100%" border=0> <tr> <td>关于作者
Terry Chan 是位于安大略省多伦多市的 IBM 的一名软件工程师。他作为一名分析家为 IBM Global Services 工作,专门从事 OS/2 核心转储分析。后来,他加入了 IBM 多伦多软件实验室,他在那里是 VisualAge for Java 的一名客户服务分析师。2001 年,他加入了 WebSphere Studio Application Developer(WSAD)Jumpstart 小组,该小组的主要目的是帮助 ISV 根据 WSAD 创建商业产品。您可以通过 terrych@ca.ibm.com 与 Terry 联系。 </td></tr></table></td></tr></table>

↑返回目录
前一篇: 在 Eclipse Workbench 之外使用 Eclipse GUI,第 2 部分:使用 JFace 图像注册表
后一篇: 在 Eclipse Workbench 之外使用 Eclipse GUI,第 3 部分:添加操作、菜单和工具栏