个人觉得,GWT的客户端表现并不是Gwt这个项目最关心的方向,所以要做出表现丰富的界面,要花去不少心思,当你为做一个“可排序的Grid”被搞得焦头烂额时,想想EXT,DOJO,只有艳羡地分了。
GWT-Ext 是sanjiv.jivan为了丰富GWT的客户端表现而贡献的开源项目。项目结合了当前客户端表现和用户体验都相当完善的Ext,提供了一种操作性很强的RIA开发方式。
作者应该花了不少心思在项目上,更新很频繁,大概不到1个月就有新的版本问世,目前项目的版本为GWT-Ext 0.9.3,是 Ext 1.1.1 和 GWT 1.4 Final 的结合,在 Ext 方面实现了大多数常用的控件,如Grid、Tree、Form、TabPanel等等。具体实现效果可以查看项目中的 Showcase Demo 。
以下结合一个例子来演示一下如何使用GWT-Ext 做实际开发。
一定要认真阅读项目中的 GettingStarted ,下面起初的几段其实就是该文的中文译本。
一、在Gwt项目中集成GWT-Ext
1.下载gwtext-0.9.3.zip,之后解压,将其中的 gwtext.jar 附加到当前GWT项目(Tree)的BuildPath中。
2.下载ext-1.1.1.zip (注意:暂时还不支持最新的2.0版本),将的ext-all.js、ext-core.js以及resources和adapter两个目录拷贝到module的public中的js/ext位置。对Ext熟悉的话,可以只拷贝需要的文件而不必载入所有的资源文件。
3.修改GWT项目中module的html页面,添加Ext资源,修改后的html页面可能是这样:
- <html>
- <head>
- <title>Wrapper HTML for Treetitle>
- <meta name='gwt:module' content='showcase.Tree'/>
- <link type="text/css" rel='stylesheet' href='Tree.css'/>
- <link rel="stylesheet" type="text/css" href="js/ext/resources/css/ext-all.css"/>
- <link rel="stylesheet" type="text/css" href="js/ext/resources/css/xtheme-aero.css" />
- <script type="text/javascript" src="js/ext/adapter/yui/yui-utilities.js">script>
- <script type="text/javascript" src="js/ext/adapter/yui/ext-yui-adapter.js">script>
- <script type="text/javascript" src="js/ext/ext-all.js">script>
- head>
- <body>
- <script language="javascript" src="gwt.js">script>
- <iframe id="__gwt_historyFrame" style="width:0;height:0;border:0">iframe>
- body>
- html>
4.编辑项目的Tree.gwt.xml文件,添加以下
- <inherits name='com.gwtext.GwtExt'/>
通过以上步骤,你就可以写接近Ext功能的客户端界面了,同时也具有了Gwt的各种开发优势了,比如Java方式开发,清晰的RPC(这点尤其是在客户端需要获取服务器端数据信息时尤其方便)等等。
二、实例
下面我们就编写Showcase Demo 中的Tree节点下的Two Trees Drag & Drop,例子实现的是两个Tree中节点的任意拖放(再次感叹Ext的丰富表现)。
Tree.java代码如下,可直接拷贝到查看效果。
- package showcase.client;
- import com.google.gwt.core.client.EntryPoint;
- import com.google.gwt.user.client.ui.HorizontalPanel;
- import com.google.gwt.user.client.ui.RootPanel;
- import com.gwtext.client.widgets.tree.AsyncTreeNode;
- import com.gwtext.client.widgets.tree.AsyncTreeNodeConfig;
- import com.gwtext.client.widgets.tree.TreePanel;
- import com.gwtext.client.widgets.tree.TreeNode;
- import com.gwtext.client.widgets.tree.DropNodeCallback;
- import com.gwtext.client.widgets.tree.TreePanelConfig;
- import com.gwtext.client.widgets.tree.XMLTreeLoader;
- import com.gwtext.client.widgets.tree.XMLTreeLoaderConfig;
- import com.gwtext.client.widgets.tree.event.TreePanelListenerAdapter;
- import com.gwtext.client.dd.*;
- /**
- * Entry point classes define
onModuleLoad()
. - */
- public class Tree implements EntryPoint {
- public void onModuleLoad() {
- RootPanel rootPanel = RootPanel.get();
- HorizontalPanel horizontalPanel = new HorizontalPanel();
- rootPanel.add(horizontalPanel);
- // create source countried tree
- final TreePanel treePanel = new TreePanel("coutries-grouped", new TreePanelConfig() {
- {
- setAnimate(true);
- setEnableDD(true);
- setContainerScroll(true);
- setRootVisible(true);
- }
- });
- final XMLTreeLoader loader = new XMLTreeLoader(new XMLTreeLoaderConfig() {
- {
- setDataUrl("countries-grouped.xml");
- setMethod("get");
- setRootTag("countries");
- setFolderTitleMapping("@title");
- setFolderTag("continent");
- setLeafTitleMapping("@title");
- setLeafTag("country");
- setQtipMapping("@qtip");
- setIconMapping("@icon");
- }
- });
- AsyncTreeNode root = new AsyncTreeNode("Countries", new AsyncTreeNodeConfig() {
- {
- setLoader(loader);
- }
- });
- treePanel.setRootNode(root);
- treePanel.render();
- root.expand();
- treePanel.expandAll();
- // create target vacation tree
- final TreePanel tripTreePanel = new TreePanel("vacation-tree", new TreePanelConfig() {
- {
- setAnimate(true);
- setEnableDD(true);
- setContainerScroll(true);
- setRootVisible(true);
- }
- });
- final XMLTreeLoader tripLoader = new XMLTreeLoader(new XMLTreeLoaderConfig() {
- {
- setDataUrl("trip.xml");
- setMethod("get");
- setRootTag("vacations");
- setFolderIdMapping("@title");
- setFolderTag("trip");
- setQtipMapping("@qtip");
- setIconMapping("@icon");
- setAttributeMappings(new String[]{"@trip"});
- }
- });
- AsyncTreeNode tripRoot = new AsyncTreeNode("Places to Visit", new AsyncTreeNodeConfig() {
- {
- setLoader(tripLoader);
- }
- });
- tripTreePanel.setRootNode(tripRoot);
- tripTreePanel.render();
- tripRoot.expand();
- tripTreePanel.expandAll();
- // add trip tree listener that handles move / copy logic
- tripTreePanel.addTreePanelListener(new TreePanelListenerAdapter() {
- public boolean doBeforeNodeDrop(TreePanel treePanel, TreeNode target, String point, DragDrop source, TreeNode dropNode, DropNodeCallback dropDropNodeCallback) {
- if("true".equals(target.getAttribute("trip"))) {
- }
- return true;
- }
- });
- horizontalPanel.add(treePanel);
- horizontalPanel.add(tripTreePanel);
- }
- }
注意:例子中需要的两个数据文件需放置于public目录,熟悉Ext的应该很清楚。
编译后执行的效果如下图:
以上示例只是将该种开发方式的流程顺了一下,窥一斑以示全貌,Ext各种丰富的表现都可以实现。闲下来,再写一下将数据提供(其实就是一些多维数据而已)方式改为RPC,这样开发流程就完整了。
可以下载示例程序运行一下,证明我没骗你