使用MVC重构RSSReader项目——Navigation区域
上一节,在程序的入口文件(RSSReader),我们派发了EventType为Init的AppEvent,AppController会处理此事件,将其转发到AppView。依次的,AppView会的处理该事件,去调用onInit方法,完成基础的UI创建。在此过程中负责了程序主体的区域创建。但是,唯一没有处理的,就是没有把组装好的components添加到UI。
那么现在,我们需要再分别创建几组Controller和View,分别去负责RSSReader项目其他的各个区域。首先我们要创建一对Controller-View,去处理Navigation区域。
大致思路如下:
- 负责navigation区域的controller,能够将Init EventType转发到对应的View里
- 负责navigation区域的view,会处理Init EventType,在处理的过程中,又会派发一个新的事件(NavPanelReady)——此事件会承载着NavPanel的实例被派发到Dispatcher中。
- AppController会监听此事件(NavPanelReady),当接收到此事件传入的时候,会将其转发到AppView。
- AppView会处理会理NavPanelReady的事件——将其传递的NavPanel实例,加入到ViewPort上
此前,onModuleLoad方法里已经派发了UIReady事件。因此,NavPanel应该已经被添加到Viewport里,显示出来。具体实现步骤如下:
- 在com.danielvaughan.rssreader.client.mvc.events.AppEvents类,加入新的EventType——NavPanelReady
public static final EventType NavPanelReady = new EventType();
- 新建NavController extends Controller
package com.danielvaughan.rssreader.client.mvc.controllers;
import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
public class NavController extends Controller {
@Override
public void handleEvent(AppEvent event) {
}
}
- 新建NavView extends View
package com.danielvaughan.rssreader.client.mvc.views;
import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
import com.extjs.gxt.ui.client.mvc.View;
public class NavView extends View {
public NavView(Controller controller) {
super(controller);
}
@Override
protected void handleEvent(AppEvent event) {
}
}
- 在NavController的构造函数里,注册Init EventType
public NavController() {
registerEventTypes(AppEvents.Init);
}
- 在NavController,定义NavView类实例,通过其initialize方法实例化NavView——让controller与view关联起来。实现handleEvent方法,将NavController接收的事件转发到NavView里。
package com.danielvaughan.rssreader.client.mvc.controllers;
import com.danielvaughan.rssreader.client.mvc.events.AppEvents;
import com.danielvaughan.rssreader.client.mvc.views.NavView;
import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
public class NavController extends Controller {
private NavView navView;
public NavController() {
registerEventTypes(AppEvents.Init);
}
@Override
public void handleEvent(AppEvent event) {
forwardToView(navView, event);
}
@Override
public void initialize() {
super.initialize();
navView = new NavView(this);
}
}
- 将com.danielvaughan.rssreader.client.components.RssNavigationPanel类,重命名为NavPanel。然后在NavView类里,加入NavPanel的实例属性
package com.danielvaughan.rssreader.client.mvc.views;
import com.danielvaughan.rssreader.client.components.NavPanel;
import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
import com.extjs.gxt.ui.client.mvc.View;
public class NavView extends View {
private final NavPanel navPanel = new NavPanel();
public NavView(Controller controller) {
super(controller);
}
@Override
protected void handleEvent(AppEvent event) {
}
}
- 在NavView类里实现handleEvent方法——当Init EventType接收到之后,再派发一个新事件(此过程我习惯称之事件联动):NavPanelReady ,并且派发的同时,承载navPanel对象
package com.danielvaughan.rssreader.client.mvc.views;
import com.danielvaughan.rssreader.client.components.NavPanel;
import com.danielvaughan.rssreader.client.mvc.events.AppEvents;
import com.extjs.gxt.ui.client.event.EventType;
import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
import com.extjs.gxt.ui.client.mvc.Dispatcher;
import com.extjs.gxt.ui.client.mvc.View;
public class NavView extends View {
private final NavPanel navPanel = new NavPanel();
public NavView(Controller controller) {
super(controller);
}
@Override
protected void handleEvent(AppEvent event) {
EventType eventType = event.getType();
if (eventType.equals(AppEvents.Init)) {
Dispatcher.forwardEvent(new AppEvent(AppEvents.NavPanelReady,
navPanel));
}
}
}
- 既然NavView会派发NavPanelReady事件,我们希望交给AppController注册,也就意味着,AppView会接收到此事件,做后续的操作。
public AppController() {
registerEventTypes(AppEvents.Init);
registerEventTypes(AppEvents.Error);
registerEventTypes(AppEvents.UIReady);
registerEventTypes(AppEvents.NavPanelReady);
}
- 在AppView对NavPanelReady事件的处理实现如下——将NavPanelReady事件传递的navPanel对象,对后续处理,将其添加到viewport上
package com.danielvaughan.rssreader.client.mvc.views;
import com.danielvaughan.rssreader.client.mvc.events.AppEvents;
import com.extjs.gxt.ui.client.Style.LayoutRegion;
import com.extjs.gxt.ui.client.Style.Orientation;
import com.extjs.gxt.ui.client.event.EventType;
import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
import com.extjs.gxt.ui.client.mvc.View;
import com.extjs.gxt.ui.client.widget.Component;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.Viewport;
import com.extjs.gxt.ui.client.widget.layout.BorderLayout;
import com.extjs.gxt.ui.client.widget.layout.BorderLayoutData;
import com.extjs.gxt.ui.client.widget.layout.RowLayout;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.RootPanel;
public class AppView extends View {
private final ContentPanel mainPanel = new ContentPanel();
private final Viewport viewport = new Viewport();
public AppView(Controller controller) {
super(controller);
}
@Override
protected void handleEvent(AppEvent event) {
EventType eventType = event.getType();
if (eventType.equals(AppEvents.Init)) {
onInit(event);
} else if (eventType.equals(AppEvents.Error)) {
onError(event);
} else if (eventType.equals(AppEvents.UIReady)) {
onUIReady(event);
}else if (eventType.equals(AppEvents.NavPanelReady)) {
onNavPanelReady(event);
}
}
private void onInit(AppEvent event) {
final BorderLayout borderLayout = new BorderLayout();
viewport.setLayout(borderLayout);
HTML headerHtml = new HTML();
headerHtml.setHTML("<h1>RSS Reader</h1>");
BorderLayoutData northData = new BorderLayoutData(LayoutRegion.NORTH,
20);
northData.setCollapsible(false);
northData.setSplit(false);
viewport.add(headerHtml, northData);
BorderLayoutData centerData = new BorderLayoutData(LayoutRegion.CENTER);
centerData.setCollapsible(false);
RowLayout rowLayout = new RowLayout(Orientation.VERTICAL);
mainPanel.setHeaderVisible(false);
mainPanel.setLayout(rowLayout);
viewport.add(mainPanel, centerData);
}
private void onUIReady(AppEvent event) {
RootPanel.get().add(viewport);
}
private void onNavPanelReady(AppEvent event) {
BorderLayoutData westData = new BorderLayoutData(LayoutRegion.WEST,
200, 150, 300);
westData.setCollapsible(true);
westData.setSplit(true);
Component component = event.getData();
viewport.add(component, westData);
}
private void onError(AppEvent event) {
}
}
- 所有的准备工作做好了,那么一切事件的触发点,我们要回到RSSReader的onModuleLoad方法里。加入NavController
@Override
public void onModuleLoad() {
final FeedServiceAsync feedService = GWT.create(FeedService.class);
Registry.register(RSSReaderConstants.FEED_SERVICE, feedService);
Dispatcher dispatcher = Dispatcher.get();
dispatcher.addController(new AppController());
dispatcher.addController(new NavController());
// 注意:dispatcher.dispatch(AppEvents.Init);会派发Init事件,虽然只是执行了一次派发操作,但是会派发到多个controller中去!
// 原因:因为AppController和NavController都注册了Init !
// 顺序:两个controller的接收到event的顺序是根据上面的两行代码(controller的加入顺序)有关!
dispatcher.dispatch(AppEvents.Init);
dispatcher.dispatch(AppEvents.UIReady);
}
- 程序运行效果如下: