Widget в GWT проекте

Библиотека GXT предлагает большое количество разнообразных визуальных компонентов для представления информации в интерфейсе GWT-проекта. Но, не всегда этого оказывается достаточно. Часто возникает ситуация, когда требуется компонент, отсутствующий в библиотеке. Приходится создавать что-то своё, оригинальное, на основе имеющихся компонентов, расширяя их свойства. Т.е. необходимо создавать виджет.

Использование виджетов (widget) в интерфейсе WEB-проекта позволяет избежать дублирование кода созданием нескольких экземляров. Кроме этого, widget можно перемещать между страницами. Рассмотрим небольшой проект, в котором создадим виджет, позволяющий :

  • выбрать несколько записей из списка;
  • отобразить выбранные записи в компоненте.

В качестве базового компонента для данного виджета можно было бы использовать ComboBox из библиотеки GXT. Но в этом случае необходимо доработать выпадающий список, чтобы записи отображались в виде строк типа CheckBox. Кроме этого, в выпадаюющем списке необходимо дополнительно разместить 2 кнопки : «применить» выбранный набор записей (OK) и «отказ» от применения (Cancel). В результате получается, что переработать необходимо весь компонент ComboBox : интерфейс выпадающего списка, компонент отображения выбранной записи (нескольких записей), обработчик выбора записей и т.д.

Упростим себе задачу и создадим Widget на основе компонента TextArea, который уже позволяет отображать различный текст и определить «текст по умолчанию», если не выбрано ни одной записи. На следующем скриншоте представлены две реализации одного виджета, который нам предстоит разработать, т.е. описать.

Интерфейс нашего виджета наряду с компонентом отображения выбранных записей TextArea включает кнопку справа, используемую для открытия дополнительного окна со списком, который будем отображать в таблице Grid. Необходимо предусмотреть гибкую настройку геометрических размеров (ширины, высоты) виджета, и соответствие размера кнопки размеру виджета по высоте.

Раскрытое окно со списком, открываемое по нажатию на кнопку, представлено на следующем скриншоте. Необходимо предусмотреть, что виджет должен уметь обрабатывать разные списки данных.

Описание виджета WidgetMultiline

Исходный код виджета WidgetMultiline включает :

  • text - текстовое поле для отображения выделенных записей;
  • button - кнопку открытия окна выбора записей;
  • hlc - контейнер размещения визуальных компонентов text и button;
  • height - размер кнопки button по высоте (значение по умолчанию определяется);
  • list - список записей типа List<SimpleData>; виджет передает этот список в окно выбора chooser для отображения и выделения;
  • chooser - окно выбора записей.

Примечание : тип данных <SimpleData> может быть любой. Но для универсальности необходимо в него включить дополнительные поля, по значениям которых виджет узнавал бы выделенные записи и которые понимала бы таблица в окне выбора при отображении и выделении записей. Листинг <SimpleData> представлен ниже.

Листинг WidgetMultiline

Чтобы создать компонент типа Widget, необходимо в классе реализовать (implements) интерфейс IsWidget и переопределить метод asWidget(). Ниже представлен листинг виджета WidgetMultiline с минимально необходимым набором компонентов и методов для формирования интерфейса.

public class WidgetMultiline implements IsWidget
{
    private TextArea                  text     = null;
    private TextButton                button   = null;
    private int                       height   =   32;
    private  GridMultiLineChooser     chooser  = null;
    private  List<SimpleData>         list     = null;
    private HorizontalLayoutContainer hlc = null;

    public WidgetMultiline() {
        hlc = new HorizontalLayoutContainer();
        hlc.setHeight(height);

        createGUI();
        list = new ArrayList<SimpleData> ();
    }
    public WidgetMultiline(final int height) {
        this();
        hlc.setHeight(height);
        button.setPixelSize(20, height);
    }

    @Override
    public Widget asWidget() {
        return hlc;
    }
    private void createGUI() {
        text   = new TextArea();
        button = new TextButton("...");
        button.setPixelSize(20, height);

        Margins margins = new Margins(0, 1, 0, 0);
        hlc.add(text  , new HorizontalLayoutData( 1, 1, margins));
        margins = new Margins(0, 0, 0,-3);
        hlc.add(button, new HorizontalLayoutData(-1,-1, margins));

        chooser = new GridMultiLineChooser(this);
        button.addSelectHandler(new SelectHandler(){
            @Override
            public void onSelect(SelectEvent se) {
                chooser.show();
            }
        });
    }
}

Виджет WidgetMultiline включает перегруженные конструкторы для реализации возможности настройки размера компонента по высоте. В методе createGUI создается интерфейс компонента в виде горизонтального контейнера hlc, в котором размещаются компоненты text и button. К кнопке подключается обработчик события, который открывает окно выбора chooser.

Метод asWidget возвращает визуальный компонент (в нашем случае контейнер), который размещается в интерфейсе страницы. Необходимо отметить, что этот метод вызывается автоматически при размещении виджета в интерфейсе (в контейнере или панели). Поэтому, если возникает необходимость перемещать виджет в интерфейсе страниц, то желательно создавать компонент в отдельном методе (к примеру createGUI), а не в asWidget, как это представлено в листинге.

Поскольку виджет включает набор визуальных компонентов, которые недоступны «снаружи» (private), то для полного «счастья» необходимо дополнительно определить методы управления доступом, размером, чтения значения и т.д. :

public void setEmptyText(final String emptyText) {
    text.setEmptyText(emptyText);
}
public void setReadOnly(final boolean mode) {
    text.setReadOnly(mode);
}
public void setAllowBlank(final boolean mode) {
    text.setAllowBlank(mode);
}
public void setWidgetWidth(final int width) {
    hlc.setWidth(width);
    hlc.forceLayout();
}
public String getText() {
    return text.getText(); 
}

Управление данными

Теперь осталось самое необходимое, для чего widget создается : загрузить данные, отобразить выделенные записи в компоненте text и вернуть их по требованию. Для этого создадим три метода :

  • setList - сохранение списка данных в list и их загрузка в chooser;
  • getSelected - получение выделенного списка данных;
  • selectionHandler - отображение выделенных записей в text.

Метод selectionHandler вызывается из окна выбора записей chooser.

public void setList(List<SimpleData>> data) {
    list.clear();
    if ((data != null) && !data.isEmpty())
        list.addAll(data);
    chooser.setList(list);
}
public List<SimpleData> getSelected() {
    List<SimpleData> selected = new ArrayList<SimpleData>();
    for (SimpleData rec : list) {
        if (rec.getSelected())
            selected.add(rec);
    }
    return selected;
}
public void selectionHandler() {
    String temp = "";
    if (list.size() > 0) {
        for (SimpleData rec : list) {
            if (rec.getSelected()) {
                if (temp.length() > 0)
                    temp += ", ";
                temp += rec.getLabel();
            }
        }
    }
    this.text.setText(temp);
}

На этом, можно сказать, описание виджета завершено. Осталось несколько слов сказать об окне выбора записей.

Окно выбора GridMultiLineChooser.java

Окно выбра записей, как было сказано выше и представлено на скриншоте, включает таблицу с CheckBox'ами. Описывать, как создать и настроить таблицу, отобразить и выделить записи не имеет смысла, чтобы не дублировать информацию примера GWT-GXT Grid. Заинтересованные могут скачать исходные коды примера в виде проекта. Здесь представим только поля класса и методы взаимодействия с виджетом :

  • setList - загрузка данных из виджета;
  • selectRecords - фомрирование списка выделенных записей;
  • onOkClick - обработка нажатия кнопки «Применить».

Все достаточно прозрачно. При загрузке данных хранилище таблицы сначала очищается. При подготовке списка выделенных записей сначала сбрасываются флаги выделения записи, после чего во втором проходе устанавливаются флаги выделенных записей. По нажатию на кнопку «Применить» сначала подготавливаем список выделенных записей, после чего вызываем обработчик в виджете.

private  WidgetMultiline        parent = null;
private  Grid<SimpleData>       grid  = null;
private  ListStore<SimpleData>  store = null;
. . .
public void setList(List<SimpleData> list) {
    store.clear();
    if ((list != null) && !list.isEmpty())
        store.addAll(list);
}

private void selectRecords() {
    for (SimpleData rec : store.getAll())
        rec.setSelected(false);
    for (SimpleData select : grid.getSelectionModel().getSelection()) {
        for (SimpleData rec : store.getAll()) {
            if (rec.getKey() == select.getKey()) {
                rec.setSelected(true);
            }
        }
    }
}

private void onOkClick() {
    selectRecords();
    parent.selectionHandler();
    hide();
}

Листинг класса SimpleData.java

Списки данных, используемые в виджетах и в окнах выбора записей, формируются на основе класса SimpleData.java, включающее два значащих поля id и name. Методы get/set для этих полей не представлены. Дополнительно класс включает методы getKey() и getLabel(), которые необходимы для загрузки списков в хранилище таблиц и отображения в интерфейсе таблицы. Поле selected используется для выделения записи.

public class SimpleData
{
    private int     id;
    private String  name;
    private boolean selected = false;

    public SimpleData() { }
    public SimpleData(int id, String name) {
        this.id   = id;
        this.name = name;
    }

    // методы get/set

    public int getKey() {
        return id;
    }
    public String getLabel() {
        return name;
    }

    public boolean getSelected() {
        return selected;
    }
    public void setSelected(boolean selected) {
        this.selected = selected;
    }
}

Скачать исходный код примера

Исходный код рассмотренного GWT примера с использованием компонента Widget в виде проекта Eclipse можно скачать здесь (7.82 Мб).

В архив примера библиотечный файл gwt-servlet.jar включен, GXT библиотека gxt-3.1.1.jar не включена. После инcталляции примера в IDE Eclipse необходимо настроить GWT SDK самостоятельно («Build Path/Configure Build Path»).

Примечание : библиотека gxt-3.1.1.jar входит в дистрибутив GWT-GXT-примера с использованием компонента Grid

  Рейтинг@Mail.ru