Вы можете скачать последнюю версию библиотеки в разделе Downloads её проекта на github и добавить её в свой проект. Для корректной работы библиотеки, естественно, требуется наличие в проекте jar-файла совместимой версии mvp4g и, на данный момент, gwt-log.
Потенциально библиотека вполне надёжна для использования — сложно-составной пример работает стабильно, а ядро этой идеи (правда, устаревшая и невылизанная версия — актуальную встроить пока нет возможности) работает в нашем проекте.
Поясню, как пользоваться всем этим кодом, если вы решили разработать на его основе проект. Ниже представлены одновременно последовательные и отдельные сценарии — выполнив их по очереди (на некоторых переходя в цикл “повторять несколько раз пока необходимо”) можно написать всё приложение целиком, но и после этого каждое из действий можно выполнять отдельно, по мере потребности изменений в приложении.
Словами описать можно отнюдь не всё, иногда удобнее взглянуть по ссылкам на примеры в приведённой очередности. Не проходите мимо этой возможности :).
тип-объекта/действие.enum-файла. Эти четыре enum-файла — главная и единственная конфигурация структуры страниц и лэйатуов в проекте (они не должны сразу же отражать действительность, но что-то потенциально реальное в них всё же должно быть):
тип-объекта/действие) и связывания их с лэйаутами. Для каждой страницы автоматически создаётся ID по методу id() (в примере в качестве ID везде берётся name() элемента из enum, но это может быть что угодно) и привязывается к созданному инстансу класса Portal: для этого enum должен имплементить интерфейс PortalId. Также вам понадобится быстрый способ получить значение enum из идентификатора Portal: в примере это метод by(Portal page), для этого инстансы Portal хранятся внутри.Group: возвращать уникальный ID в методе id().LayoutId: возвращать уникальный ID в методе id().Place: возвращать уникальный ID в методе id().EntryPoint вашего проекта и зарегистрировать в нём описанные порталы, причём выполнить это до запуска модуля mvp4g. В дальнейшем тут же нужно будет регистрировать новые лэйауты и лэйаут-билдеры, но в данный момент это необязательно. см. точку входа LayoutingDemo.java в примере.Presenter главной страницы и отнаследовать его от AMainPresenter. Ничего больше особенного здесь делать не нужно. см. page/main/presenter/MainPresenter.java в примере.View главной страницы и отнаследовать его от AMainView (см. page/main/view/MainView.java в примере). Методы, которые требуется определить:
getLayoutHolder() должен возвращать панель, в которую будет “вставляться” лэйаут.getPortalHolder() может возвращать панель, оборачивающую предыдущую, а может ту же самую. Этот метод применяется только для назначения CSS-классов, классы портала и лэйаута будут назначены одному элементу в последнем случае.getScrollable() может возвращать область, которая будет скроллиться внутри страницы. Если такой области нет, метод может возвращать null, но тогда не будет работать подписка на скролл-события страницы по шине событий (addPageScrollHandler).IsMainEventBus и перенаправить их в презентер главной страницы . Также нужно создать главный модуль, ничем не отличающийся от главного модуля mvp4g. см. page/main/MainEventBus.java и page/main/MainModule.java в примере.enum лэйатуов. см. в примере id/L.java.enum плейсхолдеров. см. в примере id/O.java.ui.xml. В этом ui.xml для обозначения мест, куда будут вставляться портлеты (плейсхолдеров) потребуется использовать виджет Outlet. см. layout/LayoutItem.ui.xml в примере. Можно описывать лэйаут и не декларативно, тогда придётся создавать плэйсхолдеры используя конструктор Outlet. Не забудьте, что вам нужен плейсходер для информировании о статусе.prepareOutlet() возвратите нужный Outlet по переданному идентификатору. Виджет должен инициироваться прямо в конструкторе. см. layout/LayoutItem.java в примере.Layouts.register(). см. LayoutingDemo.java:47 в примере.enum лэйатуов. см. в примере id/L.java.enum плейсхолдеров. Туда же добавьте плейсхолдер для информирования о состоянии (я назвал его STATUS), если вы его ещё не завели. см. в примере id/O.java.ui.xml. В этом ui.xml для обозначения мест, куда будут вставляться портлеты (плейсхолдеров) потребуется использовать виджет Outlet. Для места, куда будет вставляться сообщение о состоянии тоже подготовьте плэйсхолдер. см. layout/LayoutEdit.ui.xml в примере. Можно описывать лэйаут и не декларативно, тогда придётся создавать плэйсхолдеры используя конструктор Outlet.prepareOutlet() возвратите нужный Outlet по переданному идентификатору. В методе prepare(State) вы можете по переданному состоянию переключать видимость тех или иных виджетов внутри лэйаута и/или делать что-то ещё, если нужно. Виджет должен инициироваться прямо в конструкторе. см. layout/LayoutEdit.java в примере.Layouts.register(). см. LayoutingDemo.java:47 в примере.enum для групп. см. в примере id/G.java.ChildModules главной шины событий. см. в примере page/main/MainEventBus.java.HistoryConverter и отнаследуйте его от PortalsHistoryConverter. Передайте в родительский конструктор идентификатор группы. Метод convertFromUrl предназначен для того, чтобы по полученным PortalUrl/Portal (используйте метод P.by() из пункта 1.2.1) вызвать нужный метод в шине событий. см. в примере page/user/history/UserHistoryConverter.java.layout() вставляет портлеты в плейсходеры и вовращает true, если всё прошло удачно. здесь также удобно использовать метод P.by(). см. в примере page/user/layout/UserLayoutBuilder.java.LayoutBuilders.register(). см. LayoutingDemo.java:62 в примере.enum для страниц. см. в примере id/P.java.news(), edit() и show() в интерфейсе page/news/NewsEventBus.java.convertFromUrl() у HistoryConverter вашего модуля добавьте вызов этого события, если по истории был получен ваш портал. Создайте в конвертере метод on... для вашего события и возвратите отконвертированные параметры, если они есть — для удобства можно использовать проперти url конвертера. см. в примере page/news/history/NewsHistoryConverter.Presenter вашей страницы и отнаследуйте его от класса PortalPresenter. Интерфейс View должен расширять интерфейс IsPortalView. В конструктор нужно передать идентификатор портала, которому соответствует презентер. У презентера, как и у конвертера, также есть проперти url, вы можете использовать его для построения URL и передаче их во view. см. в примере page/news/presenter/NewsEditPresenter.java.View вашей страницы, отнаследовав его от класса Portal. Для каждой части страницы (виджета), которая будет вставлена в отдельный плейсходер, нужно использовать оборачивающий виджет Plug, независимо от того, используете вы ui.xml или нет. Корневым элементом для view должен быть виджет Plugs, который позволяет перечислить внутри нёго несколько виджетов Plug. см. в примере page/news/view/NewsEditView.java и page/news/view/NewsEditView.ui.xml, в случае NewsEditView в разные плейсхолдеры будут вставлены infoPlug (блок с информацией) и savePlug (кнопка “Save”).LayoutBuilder вашего модуля. Для сборки зарегистрируйте в шине событий модуля по одному новому событию plug... (принимающему в параметре Place) для каждого виджета, который будет вставляться в плейсхолдер. Эти события должны уходить в презентер, созданный в п.3 и вызывать в нём метод plug(Place, view.get...), физически вставляя виджеты в плейсхолдеры. Для этого создайте в презентере методы, перехватывающие эти события и вызывающие plug() с нужной частью view. Затем, в LayoutBuilder-е, вызовите созданные события поочерёдно, передавая в параметре соответствующие идентификаторы плейсхолдеров. см. в примере page/news/layout/NewsLayoutBuilder.java, page/news/NewsEventBus.java и page/news/presenter/NewsEditPresenter.java, для NEWS_EDIT это методы plugItemEditor и plugSaveButton.enum для страниц. см. в примере id/P.java.news(), edit() и show() интерфейса page/news/NewsEventBus.java.convertFromUrl() у HistoryConverter вашего модуля добавьте вызов этого события, если по истории был получен ваш портал. Создайте в конвертере метод on... для вашего события и возвратите отконвертированные параметры, если они есть — для удобства можно использовать проперти url конвертера. см. в примере page/news/history/NewsHistoryConverter.Presenter и отнаследуйте каждый от класса PortletPresenter. Интерфейс View у них должен расширять интерфейс IsPortletView. У презентера, как и у конвертера, также есть проперти url, вы можете использовать его для построения URL и передаче их во view. см. в примере page/news/presenter/NewsInfoPresenter.java, page/news/presenter/NewsListPresenter.java и page/news/presenter/UserCardPresenter.java.View каждого из портлетов должен наследоваться от виджета Portlet. Корневым элементом для этих view должен быть виджет Plug. см. в примере page/news/view/NewsListView.java, page/news/view/NewsListView.ui.xml, page/news/view/NewsInfoView.java, page/news/view/NewsInfoView.ui.xml и page/news/view/UserCardView.java.LayoutBuilder вашего модуля. Для сборки зарегистрируйте в шине событий модуля по одному новому событию plug... (принимающему в параметре Place) для каждого виджета, который будет вставляться в плейсхолдер. Эти события должны уходить в презентеры, созданные в п.3 и вызывать в них отнаследованный метод plug(Place), физически вставляя виджеты в плейсхолдеры. Поскольку в данном случае view однозначен, переопределять в презентерах ничего не нужно. Затем, в LayoutBuilder-е, вызовите созданные события поочерёдно, передавая в параметре соответствующие идентификаторы плейсхолдеров. см. в примере page/news/layout/NewsLayoutBuilder.java и page/news/NewsEventBus.java. Для NEWS_SHOW это методы plugNewsInfo, plugUserCard и plugTestWidget. Для NEWS_LIST это методы plugNewsList, plugUserCard и plugTestWidget.enum для страниц. см. в примере id/P.java.users(), edit() и show() интерфейса page/user/UserEventBus.java.convertFromUrl() у HistoryConverter вашего модуля добавьте вызов этого события, если по истории был получен ваш портал. Создайте в конвертере метод on... для вашего события и возвратите отконвертированные параметры, если они есть — для удобства можно использовать проперти url конвертера. см. в примере page/user/history/UserHistoryConverter.Presenter вашей страницы и отнаследуйте его от класса StatedPortalPresenter. Интерфейс View должен расширять интерфейс IsStatedPortalView. В конструктор нужно передать идентификатор портала, которому соответствует презентер. Для изменения состояния вида достаточно вызвать в необходимый момент подходящий метод у проперти state (например, state.noData(), state.loading(), state.noMatches(), state.gotData()…). По умолчанию виды с состояниями имеют состояние LOADING. У презентера, как и у конвертера, также есть проперти url, вы можете использовать его для построения URL и передаче их во view. см. в примере page/user/presenter/UserEditPresenter.java.View вашей страницы, отнаследовав его от класса StatedPortal. Для каждой части страницы (виджета), которая будет вставлена в отдельный плейсходер, нужно использовать оборачивающий виджет Plug, независимо от того, используете вы ui.xml или нет. Корневым элементом для view должен быть виджет Plugs, который позволяет перечислить внутри нёго несколько виджетов Plug. Для каждого из необходимых состояний вида (можно регистрировать/создавать не все) также надо создать отдельный Plug. После этого, используя метод register(State, Plug), их нужно связать с соответствующим им состоянием в методе createView см. в примере page/user/view/UserEditView.java и page/user/view/UserEditView.ui.xml, в случае UserEditView в разные плейсхолдеры будут вставлены infoPlug (блок с информацией), avatarPlug, agePlug и testPlug (просто фраза “Test Widget”); состояниям NO_DATA и LOADING соответствуют блоки ifEmpty и whenLoading, они регистрируются в методе createViewLayoutBuilder вашего модуля. В метод layout кроме всего прочего приходит состояние страницы state, его можно использовать для построения различных вариантов страницы, лэйаут уже подготовлен. Для сборки зарегистрируйте в шине событий модуля по одному новому событию plug... (принимающему в параметре Place) для каждого виджета и для каждого нужного вам состояния, которые будет вставляться в плейсхолдеры. Эти события должны уходить в презентер, созданный в п.3 и вызывать в нём метод plug(Place, view.get...), физически вставляя виджеты в плейсхолдеры или уже определённые методы plugEmpty/plugNoMatches/plugLoading для состояний. Для этого создайте в презентере методы, перехватывающие события виджетов и вызывающие plug() с нужной частью view. Для событий “подключения” состояний ничего создавать не нужно. Затем, в LayoutBuilder-е, вызовите созданные события поочерёдно, передавая в параметре соответствующие идентификаторы плейсхолдеров (плейсхолдер состояния для виджета состояния). см. в примере page/user/layout/UserLayoutBuilder.java ветка USER_EDIT, page/user/UserEventBus.java и page/user/presenter/UserEditPresenter.java, для USER_EDIT это методы plugInfoEditor, plugAgeEditor, plugAvatarEditor и plugTestWidget.enum для страниц. см. в примере id/P.java.users(), edit() и show() интерфейса page/user/UserEventBus.java.convertFromUrl() у HistoryConverter вашего модуля добавьте вызов этого события, если по истории был получен ваш портал. Создайте в конвертере метод on... для вашего события и возвратите отконвертированные параметры, если они есть — для удобства можно использовать проперти url конвертера. см. в примере page/user/history/UserHistoryConverter.Presenter и отнаследуйте каждый от класса StatedPortletPresenter (или от PortletPresenter, если у виджета не должно быть состояний). Интерфейс View у них должен расширять интерфейс IsStatedPortletView (или IsPortletView, если у виджета нет состояний). Для изменения состояния вида портлетов достаточно вызвать в необходимый момент подходящий метод у проперти state (например, state.noData(), state.loading(), state.noMatches(), state.gotData()…). По умолчанию виды с состояниями имеют состояние LOADING. У презентеров, как и у конвертера, также есть проперти url, вы можете использовать его для построения URL и передаче их во view. см. в примере page/user/presenter/UserAvatarPresenter.java, page/user/presenter/UserDetailsPresenter.java, page/user/presenter/UserInfoPresenter.java и page/user/UserListPresenter.View каждого из портлетов должен наследоваться от виджета StatedPortlet (или Portlet, если виджет не имеет состояний). Корневым элементом для view с состоянием должен быть виджет Plugs, внутри которого должны находиться виджеты Plug с основным и побочными необходимыми вам состояниями. Для того, чтобы связать виджеты состояний с соответствующими им состояниями, используйте метод register(Plug, State) в реализации createView(). Для view без состояний, корневым элементом должен быть Plug, здесь всё проще — он и будет главным видом. см. в примере виды с состояниями: page/user/view/UserAvatarView.java, page/user/view/UserAvatarView.ui.xml, page/user/view/UserDetailsView.java, page/user/view/UserDetailsView.ui.xml, page/user/view/UserInfoView.java, page/user/view/UserInfoView.ui.xml, виды без состояний: page/news/view/NewsInfoView.java, page/news/view/NewsInfoView.ui.xmlLayoutBuilder вашего модуля. Для сборки зарегистрируйте в шине событий модуля по одному новому событию plug... (принимающему в параметре Place) для каждого виджета, который будет вставляться в плейсхолдер. Эти события должны уходить в презентеры, созданные в п.3 и вызывать в них отнаследованный метод plug(Place), физически вставляя виджеты в плейсхолдеры. Поскольку в данном случае view однозначен, переопределять в презентерах ничего не нужно. Затем, в LayoutBuilder-е, вызовите созданные события поочерёдно, передавая в параметре соответствующие идентификаторы плейсхолдеров. см. в примере page/user/layout/UserLayoutBuilder.java и page/user/UserEventBus.java. Для USER_SHOW это методы plugUserInfo, plugUserAvatar и plugUserDetails. Для USERS_LIST это методы plugUserInfo, plugUserAvatar и plugUsersList.