准备工作——Item
正如我们所知,一个feed的信息,里面会包含多组Item的信息,也就是一对多的概念。如果想将这些items都显示出来,我们需要有另外的model bean class来存储他们。整个处理过程如下:
- 新建Item modalData类,其属性用来存储对应的信息(在这里为了简单起见,直接使用之前提到的第一种方法,让Item成为ModalData类。):
package com.danielvaughan.rssreader.shared.model;
import com.extjs.gxt.ui.client.data.BaseModel;
@SuppressWarnings("serial")
public class Item extends BaseModel {
public Item() {
}
public String getCategory() {
return get("category");
}
public String getDescription() {
return get("description");
}
public String getLink() {
return get("link");
}
public String getTitle() {
return get("title");
}
public void setCategory(String category) {
set("category", category);
}
public void setDescription(String description) {
set("description", description);
}
public void setLink(String link) {
set("link", link);
}
public void setTitle(String title) {
set("title", title);
}
}
- 在FeedService接口里,定义loadItems方法,根据url,返回Item List
package com.danielvaughan.rssreader.client.services;
import java.util.List;
import com.danielvaughan.rssreader.shared.model.Feed;
import com.danielvaughan.rssreader.shared.model.Item;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
@RemoteServiceRelativePath("feed-service")
public interface FeedService extends RemoteService {
void addExistingFeed(String feedUrl);
Feed createNewFeed();
List<Feed> loadFeedList();
List<Item> loadItems(String feedUrl);
void saveFeed(Feed feed);
}
- 同样的FeedServiceAsync 异步回调类,添加对应的回调方法
package com.danielvaughan.rssreader.client.services;
import java.util.List;
import com.danielvaughan.rssreader.shared.model.Feed;
import com.danielvaughan.rssreader.shared.model.Item;
import com.google.gwt.user.client.rpc.AsyncCallback;
public interface FeedServiceAsync {
void addExistingFeed(String feedUrl, AsyncCallback<Void> callback);
void createNewFeed(AsyncCallback<Feed> callback);
void loadFeedList(AsyncCallback<List<Feed>> asyncCallback);
void loadItems(String feedUrl, AsyncCallback<List<Item>> callback);
void saveFeed(Feed feed, AsyncCallback<Void> callback);
}
- 在FeedServiceImpl实现类,实现抽象方法loadItems
@SuppressWarnings("unchecked")
public List<Item> loadItems(String feedUrl) {
List<Item> items = new ArrayList<Item>();
try {
SAXBuilder parser = new SAXBuilder();
Document document = parser.build(new URL(feedUrl));
Element eleRoot = document.getRootElement();
Element eleChannel = eleRoot.getChild("channel");
List<Element> itemElements = eleChannel.getChildren("item");
for (Element eleItem : itemElements) {
Item item = new Item();
item.setTitle(eleItem.getChildText("title"));
item.setDescription(eleItem.getChildText("description"));
item.setLink(eleItem.getChildText("link"));
item.setCategory(eleItem.getChildText("category"));
items.add(item);
}
return items;
} catch (IOException e) {
e.printStackTrace();
return items;
} catch (JDOMException e) {
e.printStackTrace();
return items;
}
}
Grid
Grid很类似与flex的datagrid。GXT的Grid component 拥有这许多不同的功能。现在,我们就从最基本的功能——如何的让Grid显示数据开始。当通过Grid的构造函数创建实例的之后,需要指定两方面的内容:ListStore和ColumnModelGrid<ModelData> grid = new Grid<ModelData>(itemStore, columnModel);
ColumnConfig
Columnconfig定义了Grid在显示的时候每一列。具体点说就是指明了:1.使用了列数据;2.数据是如何被渲染出来的。
Conlumnconfig和ColumnModel之间的关系,查看源码的构造函数一看便知:
public ColumnModel(List<ColumnConfig> columns) {
this.configs = new ArrayList<ColumnConfig>(columns);
}
Columnconfig可以有很多组,存入list里,然后传入到ColumnModel的构造函数中去,生成ColumnModel的实例。
那么将上面提到所有内容加入到我们的RSSReader项目里——让应用程序在启动的时候,自动的读取一个RSS url 将读取的items信息显示到Grid中:
- 创建新package:com.danielvaughan.rssreader.client.grids,在新包内加入ItemGrid内的自定义组件。我们将在这个ItemGrid 内加入Grid的代码
- 为了让Grid能够自适应浏览器窗口的大小,ItemGrid类要继承LayoutContainer
- 习惯上,构造函数里,设置其LayOut
public ItemGrid() {
setLayout(new FitLayout());
}
- 当然了,必须要override onRender方法。首先,定义ColumnConfigs 他是一个List<ColumnConfig>
final List<ColumnConfig> columns = new ArrayList<ColumnConfig>();
columns.add(new ColumnConfig("title", "Title", 200));
columns.add(new ColumnConfig("description", "Description", 200));
- 将定义好的columns传入ColumnModel的构造函数
final ColumnModel columnModel = new ColumnModel(columns);
- 定义测试的RSS url,为了保证定义的url一定好用:
final String TEST_DATA_FILE = "http://127.0.0.1:8888/rss2sample.xml";
- 从Registry里拿feedService
final FeedServiceAsync feedService = Registry.get(RSSReaderConstants.FEED_SERVICE);
- 还是按照之前的远程调用,RpcProxy去调用FeedService的loadItems方法。通过
final FeedServiceAsync feedService = Registry.get(RSSReaderConstants.FEED_SERVICE);
RpcProxy<List<Item>> proxy = new RpcProxy<List<Item>>() {
@Override
protected void load(Object loadConfig,
AsyncCallback<List<Item>> callback) {
feedService.loadItems(TEST_DATA_FILE, callback);
}
};
- 因为Item是使用的一种方法实现的modelData——直接继承了BaseModel(其属性都是用get/set写的),因此不需要reader进行转换。loader可以直接通过proxy获得
ListLoader<ListLoadResult<Item>> loader = new BaseListLoader<ListLoadResult<Item>>(proxy);
- 通过loader,获得store数据
ListStore<ModelData> itemStore = new ListStore<ModelData>(loader);
- ListStore和ColumnModel都准备完毕之后,开始创建Grid,将两个对象传入Grid的构造函数当中。设置description列可以自动伸展,填充空白区域。
Grid<ModelData> grid = new Grid<ModelData>(itemStore,
columnModel);
grid.setBorders(true);
grid.setAutoExpandColumn("description");
- 调用load方法,让Grid装载store
loader.load();
- 将Grid添加到LayoutContainer内显示
add(grid);
- 最后编辑com.danielvaughan.rssreader.client.components.RssMainPanel,添加ItemGrid
package com.danielvaughan.rssreader.client.components;
import com.danielvaughan.rssreader.client.grids.ItemGrid;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
public class RssMainPanel extends ContentPanel {
public RssMainPanel() {
setHeading("Main");
setLayout(new FitLayout());
add(new ItemGrid());
}
}