Monday, July 2, 2007

GWT: XML-driven user interface

Вчера довёл свой проект gwt-ui до стадии, когда можно его выкладывать. Получился довольно интересный инструмент, который позволяет сильно упростить построение пользовательских интерфейсов для GWT.

Идея появилась после того, как я заметил, что приходится писать много похожего кода при построении интерфейса: панели, внутри ещё панели, внутри которых виджеты и т.д. В то же время, сейчас всё более и более популярны "описательные" способы построения интерфейсов (напр. XAML).

Такой подход более гибкий, наглядный и удобный, чем "последовательное" создание.

В GWT же сейчас используется именно "последовательное" создание интерфейса: созаются панели, внутрь который добавляются виджеты.

Вспомнив о генераторах в GWT, о которых я недавно писал, подумалось - почему бы не использовать их, для генерации хотя бы базового кода генерации интерфейса? Вот и взялся за реализацию этой идеи.

Как результат и появился проект gwt-ui. Что же он из себя представляет? Фактически, это jar-файл, который можно подключить к проекту, и после этого появится возможность воспользоваться генератором, который находится в этом проекте. 

Приведу список возможностей, которые сейчас доступны:

  1. Построение интерфейса используя XML-описание
  2. Простой доступ к сгенерированным объектам (сейчас - к контейнерам)
  3. Простое переключение "состояний" (вызов одного метода)
  4. "Ленивое" создание виджетов: виджеты создаются только тогда, когда они становятся видимыми
  5. Наследование состояний: любое состояние может наследоваться от другого. Можно написать некое "основное" состояние, которое описывает базовое расположение виджетов, а остальные просто описывают изменения.
  6. Возможность задания визуальных свойств контейнеров (напр. "align='right' width='100%' style='bar'")
  7. Нотификация виджетов о новом состоянии: Если виджет имплементирует интерфейс UIStateListener и он добавлен в интерфейс, то ему будет сообщатся о том, что состояние поменялось.

Вот небольшое how-to создания простейшего интерфейса с помощью gwt-ui:

1. Файл описания интерфейса.

Для начала создадим файлик, рядом с файлом описания модуля (который <ModuleName>.gwt.xml), в котором опишем структуру простейшего интерфейса (более сложный пример можно посмотреть в коде примера, доступного в svn):

<gwtui>
<layout>
<container id='main' type='FlowPanel'/>
</layout>
<states widgetpackage='client'>
<state id='fiststate' default='true' >
<content container='main'><widget type='MyWidget1' /></content>
</state>
<state id='anotherstate'>
<content container='main'><widget type='MyWidget2' /></content>
</state>
</states>
</gwtui>

В этом файле определены:



  • Контейнер, имеющий тип FlowPanel под именем 'main'

  • Два состояния:


    • Состояние 'firststate', которое будет использоватся по умолчанию (т.е. при первоначальной загрузке и/или если произведенна попытка изменить состояние, которое не описанно в документе. При установке этого состояния, в контейнер под именем 'main' помещается виджет с типом 'MyWidget1' (находищийся в пакете 'client')

    • Состояние 'anotherstate'. При установке этого состояния, в контейнер под именем 'main' помещается виджет с типом 'MyWidget2' (находищийся в пакете 'client'). При этом, виджеты, которые были помещенны в этот контейнер другими состояниями пропадают.

Думаю, синтаксис достаточно прост.  Более сложное описание примера можно посмотреть на страничке демо-примера, где используется и наследие состояний и визуальные свойства контейнеров.


2. Подключение GWTUI


Для работы gwt-ui, требуется подключение модуля в файле определения модуля GWT. Это можно сделать просто добавив такую строчку в файл определения (<ModuleName>.gwt.xml):

<inherits name='org.olostan.gwtui.GWTUI'/>

 


3. Создание интерфеса-маркера


Для получения объектов, созданных gwt-ui и изменения состояний интерфейса, необходимо определить в своём проекте интерфейс, который будет наследоватся от  интерфейса "UIManager", например:

public interface MyUI extends UIManager {
public Panel getMainContainer();
}

Данный интерфейс позволяет получить екземпляр контейнера 'main', описанного в файле описания интерфейса. Для того, например, чтоб добавить его на страничку.


4. Создание и добавление пользовательского интерфейса


Для того, чтобы создать сгенерированный пользовательский интерфейс, достаточно воспользоватся статическим методом GWT.create(). После этого с помощью полученного интерфейса можно будет получить доступ к созданным объектам и поменять текушее состояние. Например, при инициализации модуля:

public class MyModule implements EntryPoint {
MyUI ui;
public void onModuleLoad() {
ui = (MyUI) GWT.create(MyUI.class);
RootPanel.get().add(ui.getMainContainer());
ui.setState("fiststate");
}
}

В последствии, когда надо поменять текущее состояние, можно вызвать метод setState, например:

ui.setState('anotherstate');.

 


Вот таким способом можно создавать интерфейсы на основе XML-описания интерфейса и потом переключать состояния интерфейса.

4 comments:

n0mer said...

Маладца

Yury Skaletskiy said...

Хорошая идея, декларативный подход рулит.

Чего не хватает - так это подключать какой-нибудь HTML template engine, чтобы отделить дизайн от представления

shaman.sir said...

а через пару лет в GWT появился UIBinder... и очень похоже, кстати :)

Olostan said...

Но прошу заметить что в UIBinder-е нет поддержки смены состояний :)

А в остальном да. Даже имплементация чем-то похожа ;)