Google Web Toolkit(GWT)中的多页面教程

我刚开始学习Google Web Toolkit (GWT)。 我如何在我的GWT应用程序中制作不同的HTML页面?

例如,我想创build一个书店的应用程序。 在这个应用程序中,我将有三个页面:

  1. 主页,我会欢迎用户,并提供用户的书籍
  2. 页面按类别浏览图书并查看详细信息(使用GWT小部件)
  3. 在线查看书籍。

当然,也可能有其他页面,如用户的详细信息,添加新的书籍等等。那么,在GWT中制作不同页面的最佳方式是什么?我怎样才能使页面之间的导航? 有没有例子或教程? 或者,我甚至需要创build不同的页面,当我可以在一个页面上创build一个完整的应用程序?

我平时在这样的情况下做的是先devise网页框架。 我将有一个div的头,侧面菜单和页脚。 我的主要内容还包括HTML格式的div

例:

 <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta name='gwt:module' content='org.project.package.Core=org.project.package.Core'> </head> <body> <!-- Load the JavaScript code for GWT --> <script language="javascript" src="ui/org.project.package.ui.Core.nocache.js"></script> <!-- For some unknown reason in Internet Explorer you have to have cellpadding/spacing ON THE ELEMENT and not on the STYLE if it is in the body tag like this --> <table id="wrapper" cellpadding="0" cellspacing="0" style="width: 100%;height: 100%;"> <!-- Header row --> <tr style="height: 25%;"> <td colspan="2" id="header"></td> </tr> <!-- Body row and left nav row --> <tr style="height: 65%;"> <td id="leftnav"></td> <td id="content"></td> </tr> <!-- Footer row --> <tr style="height: 10%;"> <td colspan="2" id="footer"></td> </tr> </table> <!-- This iframe handles history --> <iframe id="__gwt_historyFrame" style="width:0;height:0;border:0"></iframe> </body> </html> 

(如果你喜欢<div>的布局,可以随意使用它们。)

然后像通常那样build立入口点(在我的例子中是Core.java ),根据需要设置每个元素。

 RootPanel.get("header").add(new Header()); RootPanel.get("leftnav").add(new NavigationMenu()); RootPanel.get("footer").add(new Footer()); 

当然,可以有一个静态的页脚和页眉,但这不是在这里,也不在那里。

我也有一个叫做“Content”的抽象类。 内容对象扩展“复合”,并将有各种方法来简化新页面的创build和布局。 我为此应用程序构build的每个页面(无论是帮助屏幕,search屏幕,购物车或其他任何内容)都是Contenttypes的。

现在,我所做的是创build一个名为“ContentContainer”的类。 这是一个负责pipe理“内容”元素的单身人士。 它有一个方法“setContent”接受types为“Content”的对象。 然后,它基本上删除“content”<td>中的所有内容,并将其replace为通过“setContent”方法分配的任何小部件(Composite)。 setContent方法也处理历史和标题栏pipe理。 基本上,ContentContainer用来聚合所有不同的绑定点,如果每个页面内容必须“知道”它必须执行的所有function,则可能必须进行这些绑定。

最后,你需要一种方法来到这个页面,对吧? 这很简单:

 ContentContainer.getInstance().setContent(new Search()); 

把上面的事情放在一个点击事件的地方,你是金。

您其他小部件唯一需要绑定的是ContentContainer和它们添加的内容的types。

我可以看到ChrisBo的方法的缺点是你有一个列表,必须维护令牌 – >页面。 我可以看到的另一个缺点是,我不明白如何用这种方法有一个实际的历史系统。

它提供的方法之一就是所有的页面select都非常集中。 我会使用某种forms的枚举或至less一个静态类与string值,以防止我自己的链接。

无论哪种情况,我认为这一点可以归纳为: 根据用户点击您的用户所执行的操作来交换某个中央页面元素的内容。

我会使用HyperLink和History类。 关于Hyperlink类的好处是,它设置了这个标记(例如#foobar),你所要做的就是捕获事件,当令牌的值被改变(ValueChangeEvent)的时候触发事件。 在事件处理程序中,您将replace页面。

例如:欢迎页面的地址:www.yourpage.com/#home在这个页面上将是一个链接到“浏览书籍”页面,当链接被点击时,新的地址将是这样的:www.yourpage.com /#浏览

这里是代码:

 public class MainEntryPoint implements EntryPoint, ValueChangeHandler { VerticalPanel panel = new VerticalPanel(); Label label=new Label(); public void onModuleLoad() { Hyperlink link1 = new Hyperlink("books", "browse"); Hyperlink link2 = new Hyperlink("user details", "details"); panel.add(link1); panel.add(link2); panel.add(label); RootPanel.get().add(panel); History.addValueChangeHandler(this); //when there is no token, the "home" token is set else changePage() is called. //this is useful if a user has bookmarked a site other than the homepage. if(History.getToken().isEmpty()){ History.newItem("home"); } else { changePage(History.getToken()); } } 

public class MainEntryPoint implements EntryPoint, ValueChangeHandler { VerticalPanel panel = new VerticalPanel(); Label label=new Label(); public void onModuleLoad() { Hyperlink link1 = new Hyperlink("books", "browse"); Hyperlink link2 = new Hyperlink("user details", "details"); panel.add(link1); panel.add(link2); panel.add(label); RootPanel.get().add(panel); History.addValueChangeHandler(this); //when there is no token, the "home" token is set else changePage() is called. //this is useful if a user has bookmarked a site other than the homepage. if(History.getToken().isEmpty()){ History.newItem("home"); } else { changePage(History.getToken()); } }

 public void onValueChange(ValueChangeEvent event) { changePage(History.getToken()); } public void changePage(String token) { if(History.getToken().equals("browse")) { label.setText("Here would be some books"); } else if (History.getToken().equals("details")) { label.setText("Here would be the user details"); } else { label.setText("Welcome page"); } } 

}

真棒! 我把Chris R.的答案和Chris Boesing的答案结合起来,

这是“索引”起始页面

 public class Index implements EntryPoint, ValueChangeHandler<String> { public void onModuleLoad() { History.addValueChangeHandler(this); if (History.getToken().isEmpty()) History.newItem("index"); Composite c = new Login(); FlowControl.go(c); } public void onValueChange(ValueChangeEvent<String> e) { FlowControl.go(History.getToken()); } } 

这是控制器,或根据Chris R. ContentContainer

 public class FlowControl { private static FlowControl instance; private FlowControl() {} public static void go(Composite c) { if (instance == null) instance = new FlowControl(); // not sure why we need this yet since everything is static. RootPanel.get("application").clear(); RootPanel.get("application").getElement().getStyle().setPosition(Position.RELATIVE); // not sure why, but GWT throws an exception without this. Adding to CSS doesn't work. // add, determine height/width, center, then move. height/width are unknown until added to document. Catch-22! RootPanel.get("application").add(c); int left = Window.getClientWidth() / 2 - c.getOffsetWidth() / 2; // find center int top = Window.getClientHeight() / 2 - c.getOffsetHeight() / 2; RootPanel.get("application").setWidgetPosition(c, left, top); History.newItem(c.getTitle()); // TODO: need to change and implement (or override) this method on each screen } public static void go(String token) { if (token == null) go(new Login()); if (token.equals("cart")) go(new Cart()); if (token.equals("login")) go(new Login()); // Can probably make these constants in this class } 

然后,你可以在你的代码中的任何地方胡椒超链接和button。 (还没有尝试超链接。)

  Button submit = new Button("Submit"); submit.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { FlowControl.go(new MyScreen()); } }); 

我添加了一个div到我的HTML

 <!-- This is where the application will reside within. It is controlled by FlowControl class. --> <div id="application"></div> 

现在所有的屏幕都必须在构造函数中调用initWidget(),而不是添加到RootPanel,因为它现在是一个Composite类,就像

  initWidget(myPanel); // all composites must call this in constructor 

如果你想要它是完全AJAX化(如桌面应用程序),当然你只需要一个页面。 然后根据链接改变正文的内容。

此外,还有一个非常非常活跃的GWT谷歌组,我知道这之前已经问过,你只需要使用“search”function。

GWT Multipage – 多页面GWT应用程序的简单框架。

你可以使用MVP模板。 这里是简单的图书馆https://code.google.com/p/gwt-simple-mvp/wiki/GettingStarted

而且你可以将代码拆分成更多的js文件。 https://code.google.com/p/gwt-spliting/

我使用Chloe S.答案(将Chris R.的答案和Chris Boesing的答案结合起来)构build一个适用于工作的GWT Web App的App Controller。 生产中的版本已经过testing(并且正在工作%100),但是下面的编辑版本将需要修改,以便与您自己的应用程序集成(将页面键重命名为菜单项)。

AppController.java

 /** * This App Controller utilizes two static inner-classes (Pages and External) * to manage and server multiple pages with multiple sub-page (through their presenters) * via String key constants which also serve as the literal text for the menu items. * * Pages are added as menu commands in their respective views: * // Add menu items to the menu with commands: * menuItems.put(Pages.PAGE1, mainMenu.addItem(Pages.PAGE1, new Command() { * public void execute() { * History.newItem(Pages.PAGE1); * } * })); * * Pages are fired as History tokens (from entry point java class): * * ** * * Receives history events and pushes them to the AppController using a deferred command. * * Changes the cursor to show waiting. * * @param the value change token * * * public void onValueChange(ValueChangeEvent<String> e) { * // check token to cover first historical "back" navigation: * if(!History.getToken().isEmpty()) { * AppController.waitCursor.execute(); // cursor is reset in page attach method * } * Scheduler.get().scheduleDeferred(new ScheduledCommand() { * public void execute() { * AppController.go(History.getToken()); * } * }); * } * * Wait cursors are implemented as CSS: * * body.wait, body.wait * { * cursor: wait !important; * } * * NOTE: This page swapping implementation technique (based on the StackOverflow solution * found here: [http://stackoverflow.com/questions/1061705/multiple-pages-tutorial-in-google-web-toolkit-gwt][1]) * differs from the obtuse and ancient 2010 GWT framework documentation in that the App Controller manages / handles * adding the widget to the container, and therefore all the Presenters must implement the * "AppControlPresenter" or "AppControlContainerPresenter" interface to give it access to their containers. * (thus eliminating "public void go(final HasWidgets container);" method in all presenter architecture except for 'MainAppPresenter') * There is also no event bus; static method calls are used for any needed interactivity. * * Includes a popup for pages still under construction. */ package com.; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import java.util.HashMap; import java.util.Map; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.logical.shared.AttachEvent; import com.google.gwt.event.logical.shared.ResizeEvent; import com.google.gwt.event.shared.HandlerManager; import com.google.gwt.user.client.History; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.DecoratedPopupPanel; import com.google.gwt.user.client.ui.Frame; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.LayoutPanel; import com.google.gwt.user.client.ui.RootLayoutPanel; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.UIObject; /** * */ public class AppController { /** */ public final static String DEFAULT_INITIAL_PAGE1_SUB_PAGE = Pages.PAGE_1A; /** Singleton instance for the AppController */ private static AppController instance = new AppController(); /** Presenter for the main app */ private static MainAppPresenter mainAppPresenter; /** container for the different views */ private static LayoutPanel container; /** sub-container for the different sub-views */ private static LayoutPanel page1Container; /** */ private static DecoratedPopupPanel popup; /** constant for Style-Dependent names for menu items (see menu-style.css) */ public final static String MENU_ACTIVE_STYLE = "active"; /** constant for Style-Dependent class name in css */ public final static String CURSOR_WAIT_CLASS = "wait"; /** */ public final static String POPUP_DEMO_ID = "popupDemo"; /** */ public final static int DEMOP_POPUP_VERTICAL_OFFSET = 0; /** */ public final static String POPUP_DEMO_STATEMENT = "<span class='text'>This page is under construction</span>" + "<span class='char'>&hellip;</span>"; /** */ public static ScheduledCommand waitCursor = new ScheduledCommand() { @Override public void execute() { AppController.waitCursor(true); } }; /** */ public static ScheduledCommand normalCursor = new ScheduledCommand() { @Override public void execute() { AppController.waitCursor(false); } }; /** Flag for determining if the page was reloaded */ private static boolean reloaded = false; private static final LoginServiceAsync loginRpcService = GWT.create(LoginService.class); /** * Called on the resize event to set the position of the demo popup * window to be adjusted to the correct dimensions (size and positoin) * regardless of screen size. */ private static ScheduledCommand resetPopupDimensions = new ScheduledCommand() { @Override public void execute() { if(!UNDER_CONSTRUCTION || popup == null) { return; } int demoWidth = Math.round(Window.getClientWidth() / MainApp.PHI), demoYPosition = Window.getClientHeight() / 2 - Math.round(popup.getOffsetHeight() / 2); popup.setWidth(String.valueOf(demoWidth) + "px"); if(popup.getOffsetWidth() >= Window.getClientWidth()) { popup.setWidth("100%"); popup.setPopupPosition(0, demoYPosition); } else { popup.setPopupPosition(Window.getClientWidth() / 2 - (popup.getOffsetWidth() / 2), demoYPosition); } } }; /** */ private static final String LOGIN_OBJECT_NAME = "Login Presenter Object"; /** * static inner-class for external websites */ public static class External { /** The frame to contain the website */ private static Frame frame; /** */ public static final String EXTERNAL_URL_1 = "http://"; /** */ public static final String EXTERNAL_URL_2 = "http://"; /** * @returns true if the name of the token is equal to one of the URLs * @param token the name to check */ public static boolean has(String token) { return token.equalsIgnoreCase(EXTERNAL_URL_1) || token.equalsIgnoreCase(EXTERNAL_URL_2); } /** * Gets the external Frame object * @param url * @return Frame */ public static Frame get(String url) { if(frame == null) { frame = new Frame(url); frame.addAttachHandler(new AttachEvent.Handler() { @Override public void onAttachOrDetach(AttachEvent event) { // hide the popup: showPopup(false); Scheduler.get().scheduleFinally(resetPopupDimensions); Scheduler.get().scheduleFinally(normalCursor); } }); } else if(!frame.getUrl().equalsIgnoreCase(url)) { frame.setUrl(url); } return frame; } } /** * static inner-class for holding pages activated by the app's main menu commands */ public static class Pages { /** */ public static final String PAGE1 = "foo"; /** */ public static final String PAGE2 = "bar"; /** */ public static final String PAGE_1A = "baz"; /** */ public static final String PAGE_1B = "qux"; /** */ public static String lastPage; /** */ public static String lastPage1SubPage; /** */ public static String unsavedMessage; /** */ private static HashMap<String, AppControlPresenter> pageMap; /** */ private static AppControlPresenter presenter; /** */ private static Composite view; /** * initializes the hashmap of pages */ public static void init() { pageMap = new HashMap<String, AppControlPresenter>(); } /** * @returns true if the name of the token is equal to one of the pages * @param token the name to check */ public static boolean has(String token) { return token.equalsIgnoreCase(PAGE1) || token.equalsIgnoreCase(PAGE2) || token.equalsIgnoreCase(PAGE_1A); } /** * Gets the correct page container to display as a Composite * @param page the token name of the page * @return Composite page */ public static Composite get(String page) { view = null; presenter = null; if(page.equalsIgnoreCase(PAGE1)) { if(pageMap.get(PAGE1) == null) { pageMap.put(PAGE1, new Page1Presenter(PAGE1)); page1Container = ((AppControlContainerPresenter) pageMap.get(PAGE1)).getContentPane(); } presenter = pageMap.get(PAGE1); lastPage = page; mainAppPresenter.setCurrentMenuItem(page); } else if(page.equalsIgnoreCase(PAGE_1A) || page.equalsIgnoreCase(PAGE_1B) { if(pageMap.get(PAGE1) == null) { pageMap.put(PAGE1, new Page1Presenter(PAGE1)); page1Container = ((AppControlContainerPresenter) pageMap.get(PAGE1)).getContentPane(); } presenter = pageMap.get(PAGE1); lastPage1SubPage = page; view = ((AppControlContainerPresenter)presenter).setCurrentPage(page); } else if(page.equalsIgnoreCase(PAGE2)) { if(pageMap.get(PAGE2) == null) { pageMap.put(PAGE2, new Page2Presenter(PAGE2)); } presenter = pageMap.get(PAGE2); lastPage = PAGE2; mainAppPresenter.setCurrentMenuItem(page); } else if(External.has(page)) { throw new Error("App Controller Error -- Use 'External' inner-class for: " + page); } else { throw new Error("App Controller Error -- Page name not found: " + page); } if(view == null) { view = (Composite)presenter.view(); } view.addAttachHandler(new AttachEvent.Handler() { @Override public void onAttachOrDetach(AttachEvent event) { AppController.showPopup(false); presenter.updateAttachOrDetach(event); Scheduler.get().scheduleFinally(resetPopupDimensions); Scheduler.get().scheduleFinally(normalCursor); } }); return view; } /** * Gets the current AppControlPresenter for the last page. * @returns the current AppControlPresenter */ public static AppControlPresenter getCurrentPresenter() { return presenter; } /** * Gets an AppControlPresenter from the pageMap. * @param token the name of the presenter * @returns the AppControlPresenter */ public static AppControlPresenter getPresenter(String token) { return pageMap.get(token); } /** * Returns true if the page is already loaded. * @param token name of the page */ public static boolean alreadyLoaded(String token) { MainApp.debug(1, "[already loaded: " + presenter.toString() + " (token: " + token + ")"); return presenter.toString().equalsIgnoreCase(token); } /** * Returns true if the page is visible * @param page the token name of the page */ public static boolean isVisible(String page) { UIObject component = pageMap.get(page).view(); return !(component.getOffsetHeight() == 0 && component.getOffsetWidth() == 0); } /** * Returns true if the page is visible * @param presenter the AppControlPresenter instance */ public static boolean isVisible(AppControlPresenter presenter) { UIObject component = presenter.view(); return !(component.getOffsetHeight() == 0 && component.getOffsetWidth() == 0); } /** * Returns true if the application has unsaved data. * Iterates through all the pages and checks each presenter. */ public static boolean unsavedData() { if(pageMap.isEmpty()) return false; boolean unsaved = false; for(Map.Entry<String, AppControlPresenter> entry : pageMap.entrySet()) { AppControlPresenter presenter = entry.getValue(); if(presenter != null && presenter.unsavedData()) { MainApp.debug(1, "(!) " + presenter.toString() + " has unsaved data"); unsavedMessage = presenter.dataDescription(); unsaved = true; break; // just need to know one exists for now (window closing event) } } return unsaved; } /** * Called on a resize event on the window. Iterates through all the pages * and tells their presenters to resize their content. */ public static void resize() { for(Map.Entry<String, AppControlPresenter> entry : pageMap.entrySet()) { AppControlPresenter presenter = entry.getValue(); if(presenter != null && isVisible(presenter)) { presenter.resize(); } } } } //end class Pages /** * @returns true if the history token is equal to any of the pages in the app */ public static boolean hasHistory() { String token = History.getToken(); return External.has(token) || Pages.has(token); } /** * Starts the login view at the root layout level */ public static void goLoginScreen() { //check for reload: if(hasHistory()) { MainApp.debug(1, "(!) AppController has History on Login"); reloaded = true; } else { reloaded = false; } RootLayoutPanel.get().clear(); RootLayoutPanel.get().add(new LoginPresenter(LOGIN_OBJECT_NAME).view()); } /** * @returns the last "Page1" page */ public static String getLastPage1Page() { if(Pages.lastPage1SubPage == null || Pages.lastPage1SubPage.isEmpty()) { Pages.lastPage1SubPage = DEFAULT_INITIAL_PAGE1_SUB_PAGE; } return Pages.lastPage1SubPage; } /** * Tells the app to start with the Page1 page. * @param username the username of the person logged-in */ public static void goMainApp(String username) { //hide the login background: RootPanel.getBodyElement().getStyle().setProperty("background", "none"); mainAppPresenter = new MainAppPresenter(username); RootLayoutPanel.get().clear(); mainAppPresenter.go(RootLayoutPanel.get()); //get the center panel: container = mainAppPresenter.getContainer(); //check for reload: //NOTE: the token will be empty if the user refreshes // and navigates all the way back to the zero-state // from the login screen. //NOTE: this logic may change after user-persistence is implemented if(hasHistory() || History.getToken().isEmpty()) { // reset the reloaded flag: reloaded = false; if(History.getToken().isEmpty()) { //land on the first page: History.newItem(AppController.Pages.PAGE1); } else { MainApp.debug(2, "(!) AppController has History on reload: " + History.getToken()); History.fireCurrentHistoryState(); } } else { //land on the first page: History.newItem(AppController.Pages.PAGE1); } } /** * */ public static void checkIfSessionActive() { loginRpcService.loginFromSession(new AsyncCallback<LoginSummary>() { @Override public void onFailure(Throwable throwable) { goLoginScreen(); } @Override public void onSuccess(LoginSummary loginSummary) { if (loginSummary.getErrorString() != null) goLoginScreen(); else goMainApp(loginSummary.getUser().getName()); } }); } /** * */ public static void sessionLogout() { DialogBoxWidget.confirm(200, "Logout", "Are you sure you want to log out?", new ConfirmDialogCallback() { @Override public void onAffirmative() { loginRpcService.logout(new AsyncCallback<Void>() { @Override public void onFailure(Throwable throwable) { goLoginScreen(); } @Override public void onSuccess(Void aVoid) { goLoginScreen(); } }); } @Override public void onCancel() { } }); } /** * Shows or hides the "Under Construction" popup if UNDER_CONSTRUCION is true. * @param show true to show and false to hide */ public static void showPopup(boolean show) { if(MainApp.UNDER_CONSTRUCTION && popup != null) { if(show) { popup.show(); } else { popup.hide(); } } } /** * Called by every history event fired (including the back and forward buttons). * Ignores the login and empty index historically. * @param token the name of the page to load */ public static void go(String token) { if(reloaded) { normalCursor.execute(); } if(token == null || token.isEmpty() || reloaded == true) return; MainApp.debug("<history changed> - AppController.go()-> " + token); // build the popup message for all unfinished pages: if(MainApp.UNDER_CONSTRUCTION) { if(popup == null) { popup = new DecoratedPopupPanel(false); popup.ensureDebugId(POPUP_DEMO_ID); popup.addStyleDependentName(POPUP_DEMO_ID); popup.setWidget(new HTML(new Image("images/workingman.png") + POPUP_DEMO_STATEMENT + new Image("images/workingmanFLIP.png"))); } } // check token for which page to return: if(token.equalsIgnoreCase(External.EXTERNAL_URL_1)) { MainAppPresenter.clearActiveMenuItems(); setExternalContentURL(External.get(token)); } else if(token.equalsIgnoreCase(External.EXTERNAL_URL_2)) { MainAppPresenter.clearActiveMenuItems(); setExternalContentURL(External.get(token)); } else if(token.equalsIgnoreCase(Pages.PAGE1)) { setContent(Pages.get(Pages.PAGE1)); setPage1Content(Pages.get(getLastPage1Page())); } else if(token.equalsIgnoreCase(Pages.PAGE_1A) || token.equalsIgnoreCase(Pages.PAGE_1B)) { setContent(Pages.get(Pages.PAGE1)); setPage1Content(Pages.get(token)); } else if(token.equalsIgnoreCase(Pages.PAGE2)) { setContent(Pages.get(Pages.PAGE2)); } else { // default behavior for a page not described: MainApp.debug(2, "(!) Unknown page: " + token); setContent(Pages.get(token)); } } /** * Called by MainApp on a window resize event. * @param e the ResizeEvent */ public static void resize(ResizeEvent e) { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { if(mainAppPresenter != null) { mainAppPresenter.resize(); } Pages.resize(); Scheduler.get().scheduleFinally(resetPopupDimensions); } }); } /** * Changes the cursor to "wait" or "auto" depending on the parameter * @param wait true to set the cursor to waiting */ private static void waitCursor(Boolean wait) { if(wait) { RootPanel.getBodyElement().addClassName(CURSOR_WAIT_CLASS); } else { RootPanel.getBodyElement().removeClassName(CURSOR_WAIT_CLASS); } } /** * Private Constructor which initializes the Pages object. */ private AppController() { Pages.init(); } /** * Sets the content of the main app container to one of the "Pages." * @param c the Composite widget to be added */ private static void setContent(Composite c) { container.clear(); container.add(c.asWidget()); } /** * Sets the content of the main app container an external URL. * @param f the Frame by which external web sites are added */ private static void setExternalContentURL(Frame f) { container.clear(); container.add(f); // must reset the width and height every time: f.getElement().getStyle().setWidth(100, Unit.PCT); f.getElement().getStyle().setHeight(100, Unit.PCT); } /** * Sets the content of the Page1 container to one of the sub pages. * @param c the Composite widget to be added */ private static void setPage1Content(Composite c) { page1Container.clear(); page1Container.add(c.asWidget()); } } 

AppControlPresenter.java:

 package com.*; import com.google.gwt.event.logical.shared.AttachEvent; import com.google.gwt.user.client.ui.Composite; /** * Base interface for all 'Presenters' used by AppController.java * NOTE: classes that implement this interface do not launch the presenter's view * into the provided container; rather, the view is retrieved and used by the * AppController instance by calling the 'view()' method */ public interface AppControlPresenter { /** * Gets the view (for use in AppController.java) */ public Composite view(); /** * Indicates if current search data is present and unsaved. * @returns true to if a search is still active */ public boolean unsavedData(); /** * Called on resize event to notify presenters with visible * components that need resizing for different screen sizes. * @returns true if elements were resized */ public boolean resize(); /** * Called on attachEvents to tell the presenter to update. * @param event the AttachEvent */ public void updateAttachOrDetach(AttachEvent event); /** * Gets the message to display for unsaved data. * @returns a message String describing the data */ public String dataDescription(); /** * Gets a fully qualified name for use in comparisons * @return the name of this presenter used by the <code>AppController</code> */ public String toString(); } 

AppControlContainerPresenter.java:

 package com.*; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.LayoutPanel; /** */ public interface AppControlContainerPresenter extends AppControlPresenter { /** * * @return */ public LayoutPanel getContentPane(); /** * * @param pageName * @return */ public Composite setCurrentPage(String pageName); } 

为每个需要GWTfunction的页面添加一个模块。 重用您的组件。