(刚干了一个多月的flex项目,一时半会还无法转过神来专注于GXT的东东,有好多东西自己都忘的差不多了。。。。
我得好好屡屡
)
使用TabPanel显示feeds
之前,我们已经通过RssMainPanel里的 ItemGrid来显示一个feed数据。现在,我们将要使用TabPanel去管理多个TabItem——其中每一个TabItem包含一个 ItemGrid,一个ItemGrid负责显示一个feed数据的多个Items内容。
- 编辑com.danielvaughan.rssreader.client.components.FeedPanel,新建field——TabPanel
private final TabPanel tabPanel = new TabPanel();
- 新建一个方法——addTab,传入TabItem参数。设置一些属性,如下:
public void addTab(TabItem tabItem) {
tabItem.setLayout(new FitLayout());
tabItem.setIcon(Resources.ICONS.rss());
tabItem.setScrollMode(Scroll.AUTO);
}
- 因为我们只是想让一个TabItem负责显示一个feed数据内容,所以当一个TabItem已经拥有feed数据显示到TabPanel的时候,就直接跳转到此TabItem,否则就创建一个新的TabItem
public void addTab(TabItem tabItem) {
tabItem.setLayout(new FitLayout());
tabItem.setIcon(Resources.ICONS.rss());
tabItem.setScrollMode(Scroll.AUTO);
String tabId = tabItem.getId();
TabItem existingTab = tabPanel.findItem(tabId, false);
if (existingTab == null) {
tabPanel.add(tabItem);
tabPanel.setSelection(tabItem);
} else {
tabPanel.setSelection(existingTab);
}
}
- 重构FeedPanel构造函数:
public FeedPanel() {
setHeading("Main");
setLayout(new FitLayout());
add(tabPanel);
}
连接起来
我们现在几乎已经准备好了所有的UI,现在我们需要将它们连接起来——当用户在Main(FeedPanel)区域选择了某条feed,则在Item(ItemPanel)区域显示出具体的内容。
我们可以根据用户的选择事件操作,将其选择的ModelDate在不同的components之间传递。同样的,也可以通过数据的加载事件,将ModelData在不同的components之间传递。
大致思路如下:
- 当用户在FeedList选中了一个Feed(link)的时候,一个FeedSelected AppEvent就会搭载其Feed被派发出去。
- 当FeedSelected AppEvent被派发出来之后,FeedView接收到此事件,然后负责页面跳转到或新建一个ItemGrid来显示Feed
- 当用户在ItemGrid选中了一个Item的时候,一个ItemSelected AppEvent就会搭载其Item被派发出去。
- 当ItemSelected AppEvent被派发出来之后,ItemView接收到此事件,然后负责页面使用ItemPanel来显示Item
- 当用户在TabPanel选中某个tab的时候,一个TabSelected AppEvent就会搭载其Feed被派发出去。
- 当TabSelectedAppEvent被派发出来之后,FeedList接收到此事件,然后负责联动选中到对应的Feed。
Implements:
- AppEvents类,新定义三个EventType
public static final EventType FeedSelected = new EventType();
public static final EventType ItemSelected = new EventType();
public static final EventType TabSelected = new EventType();
- 找到com.danielvaughan.rssreader.client.lists.FeedList类,在其onRender方法里,添加 SelectionChange监听——当用户选择不同的Feed的时候,就会触发SelectionChangedEvent事件,其操作过程,是将传入的selectedItem,派发出去。
package com.danielvaughan.rssreader.client.lists;
import java.util.List;
import com.danielvaughan.rssreader.client.RSSReaderConstants;
import com.danielvaughan.rssreader.client.mvc.events.AppEvents;
import com.danielvaughan.rssreader.client.services.FeedServiceAsync;
import com.danielvaughan.rssreader.shared.model.Feed;
import com.extjs.gxt.ui.client.Registry;
import com.extjs.gxt.ui.client.data.BaseListLoader;
import com.extjs.gxt.ui.client.data.BeanModel;
import com.extjs.gxt.ui.client.data.BeanModelReader;
import com.extjs.gxt.ui.client.data.ListLoadResult;
import com.extjs.gxt.ui.client.data.ListLoader;
import com.extjs.gxt.ui.client.data.LoadEvent;
import com.extjs.gxt.ui.client.data.RpcProxy;
import com.extjs.gxt.ui.client.event.LoadListener;
import com.extjs.gxt.ui.client.event.SelectionChangedEvent;
import com.extjs.gxt.ui.client.event.SelectionChangedListener;
import com.extjs.gxt.ui.client.mvc.Dispatcher;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.form.ListField;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.rpc.AsyncCallback;
public class FeedList extends LayoutContainer {
final ListField<BeanModel> feedList = new ListField<BeanModel>();
public FeedList() {
setLayout(new FitLayout());
}
private String getTemplate() {
StringBuilder sb = new StringBuilder();
sb.append("<tpl for=\".\">");
sb.append("<div class='x-combo-list-item'><b>{title}</b> -{description}</div>");
sb.append("</tpl>");
return sb.toString();
}
@Override
protected void onRender(Element parent, int index) {
super.onRender(parent, index);
// 0:从Registry里获得Service
final FeedServiceAsync feedService = (FeedServiceAsync) Registry
.get(RSSReaderConstants.FEED_SERVICE);
// 1:定义proxy在load方法里掉用Serivce里的方法
RpcProxy<List<Feed>> proxy = new RpcProxy<List<Feed>>() {
@Override
protected void load(Object loadConfig,
AsyncCallback<List<Feed>> callback) {
feedService.loadFeedList(false, callback);
}
};
// 2:定义Reader
BeanModelReader reader = new BeanModelReader();
// 3:将proxy和reader传入,定义loader
ListLoader<ListLoadResult<BeanModel>> loader = new BaseListLoader<ListLoadResult<BeanModel>>(
proxy, reader);
// 4:传入loader,生成store,此时还没有load数据
final ListStore<BeanModel> feedStore = new ListStore<BeanModel>(loader);
// 5:将stroe绑定到data-backed component身上
feedList.setStore(feedStore);
feedList.setTemplate(getTemplate());
feedList.addSelectionChangedListener(new SelectionChangedListener<BeanModel>() {
@Override
public void selectionChanged(SelectionChangedEvent<BeanModel> se) {
Feed feed = se.getSelectedItem().getBean();
if (feed != null) {
Dispatcher.forwardEvent(AppEvents.FeedSelected, feed);//关键在这里~~~
}
}
});
// 6:真正的load数据,load成功之后,data-backed component会自动的显示出来。
loader.load();
add(feedList);
}
}
- 如果想要程序能够对FeedSelected事件作出响应,就必须在关联的Controller注册——FeedController
public FeedController() {
registerEventTypes(AppEvents.Init);
registerEventTypes(AppEvents.FeedSelected);
}
- 重构ItemGrid的构造函数,增加一个参数为Feed——用来接收FeedSelected事件传递过来的Feed
private final Feed feed;
public ItemGrid(Feed feed) {
setLayout(new FitLayout());
this.feed = feed;
}
- 继续在ItemGrid类,将onRender方法里面局部变量的grid,提取到方法外面,作为ItemGrid类的属性存在。
private Grid<ModelData> grid;
@Override
protected void onRender(Element parent, int index) {
…
grid = new Grid<ModelData>(itemStore, columnModel);
}
- 继续在ItemGrid类的onRender方法里。删除如下局部变量的定义
final String TEST_DATA_FILE = "http://localhost:8888/rss2sample.xml";
- 取而代之,是使用Feed对象的UUID属性替换之前的TEST_DATA_FILE
RpcProxy<List<Item>> proxy = new RpcProxy<List<Item>>() {
@Override
protected void load(Object loadConfig,
AsyncCallback<List<Item>> callback) {
feedService.loadItems(feed.getUuid(), callback);
}
};
- 继续在ItemGrid类里,定义一个新的方法——resetSelection,用来重设Grid的选中状态。
public void resetSelection() {
grid.getSelectionModel().deselectAll();
}
- 在FeedView类,新建一个方法(onFeedSelected)用来响应,接收到的 FeedSelected事件:通过传入的event获得Feed对象;将Feed对象作为参数创建一个ItemGrid;新建TabItem,并设置一些属性,将先前新建的ItemGrid加入到Tabitem里;最后,将tabItem加入到feedPanel里。
private void onFeedSelected(AppEvent event) {
Feed feed = event.getData();
final ItemGrid itemGrid = new ItemGrid(feed);
TabItem tabItem = new TabItem(feed.getTitle());
tabItem.setId(feed.getUuid());
tabItem.setData("feed", feed);
tabItem.add(itemGrid);
tabItem.addListener(Events.Select, new Listener<TabPanelEvent>() {//选中之后的tab会清空item selection
@Override
public void handleEvent(TabPanelEvent be) {
itemGrid.resetSelection();
}
});
tabItem.setClosable(true);
feedPanel.addTab(tabItem);
}
- 在FeedView类的handleEvent方法里,加入对FeedSelected事件的跳转。到此FeedSelected事件的处理流程完成
@Override
protected void handleEvent(AppEvent event) {
EventType eventType = event.getType();
if (eventType.equals(AppEvents.Init)) {
onInit(event);
}else if (eventType.equals(AppEvents.FeedSelected)) {
onFeedSelected(event);
}
}
- 在ItemGrid的onRender方法里,添加一个SelectionChange的监听,当用户选择不同的Item的时候,就会触发SelectionChangedEvent事件,其操作过程,是将传入的selectedItem,派发出去。
grid.getSelectionModel().addListener(Events.SelectionChange,
new Listener<SelectionChangedEvent<Item>>() {
@Override
public void handleEvent(SelectionChangedEvent<Item> be) {
Item item = be.getSelectedItem();
if(item!=null)
Dispatcher.forwardEvent(AppEvents.ItemSelected, item);
}
});
- 同样的,对于ItemSelected方法,我们要在ItemController里注册
public ItemController() {
registerEventTypes(AppEvents.Init);
registerEventTypes(AppEvents.ItemSelected);
}
- 在ItemView类里,针对ItemSelected AppEvent处理的方法——onItemSelected()
private void onItemSelected(AppEvent event) {
Item item = (Item) event.getData();
itemPanel.displayItem(item);
}
- 在ItemView类里,handleEvent方法里加入针对ItemSelected事件的处理方法跳转。到此ItemSelected事件的处理流程完成
@Override
protected void handleEvent(AppEvent event) {
EventType eventType = event.getType();
if (eventType.equals(AppEvents.Init)) {
Dispatcher.forwardEvent(new AppEvent(AppEvents.ItemPanelReady,
itemPanel));
} else if (eventType.equals(AppEvents.ItemSelected)) {
onItemSelected(event);
}
}
- 现在我们还少一个事件处理流程——TabSelected ,但是我们先来看看程序运行效果哈~~~