GXT之旅:第七章:MVC——Navigation,Main和Item区域交互(1)

(刚干了一个多月的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 ,但是我们先来看看程序运行效果哈~~~



你可能感兴趣的:(GXT之旅:第七章:MVC——Navigation,Main和Item区域交互(1))