Контейнеры компонентов GXT

При разработке интерфейса сайта желательно использовать шаблоны страниц. Однако в отдельных случаях страница может иметь нестандартный вид. Фреймворк GWT в сочетании с библиотекой GXT позволяет создать интерфейс/шаблон страницы необходимой формы. В этом случае можно использовать контейнеры компонентов. Контейнеры горизонтального HorizontalLayoutContainer и VerticalLayoutContainer расположения компонентов позволяют создать любые замысловатые интерфейсы страниц.

Здесь мы рассмотрим пример разработки шаблона страницы, который используем для размещения ссылки на сайт в интернете. При разработке шаблона не будем оригинальничать и используем стандартный вид страницы, включающий панель заголовка header, тело страницы body и нижнюю панель footer. Тело страницы body разделим на три части : левая панель, центральная панель и правая панель. Левая и правая панели будут иметь определенный размер. Центральная часть тела страницы будет занимать все оставшееся место. В центральной панели разместим ссылку на страницу компании Sencha, по нажатию на которую будет открываться соответствующая страница на новой вкладке. Интерфейс шаблона/страницы представлен на следующем рисунке.

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

GWT-GXT пример

Создание проекта gxt-container и подключение библиотеки GXT рассмотрены здесь. Cтруктура GWT-GXT примера в среде разработки IDE Eclipse представлена на следующем скриншоте.

Пример gxt-container включает :

  • Gxt_container.gwt.xml - модуль конфигурации;
  • Gxt_container.java - главный модуль приложения;
  • TemplatePage.java - шаблон страницы;
  • MainPage.java - главную страницу;
  • HyperLink.java - модуль гиперссылки;
  • поддиректорию war с набором стандартных файлов WEB-приложения.

Листинг модуля конфигурации Gxt_container.gwt.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.7.0//EN"
  "http://gwtproject.org/doctype/2.7.0/gwt-module.dtd">
<module rename-to='gxt_container'>
  <!-- Inherit the core Web Toolkit stuff  -->
  <inherits name='com.google.gwt.user.User' />

  <!-- Other module inherits               -->
  <inherits name="com.sencha.gxt.ui.GXT"    />
  
  <!-- inherits name='com.google.gwt.user.theme.clean.Clean'       -->
  <!-- inherits name='com.google.gwt.user.theme.standard.Standard' -->
  <!-- inherits name='com.google.gwt.user.theme.chrome.Chrome'     -->
  <!-- inherits name='com.google.gwt.user.theme.dark.Dark'         -->

  <inherits name="com.sencha.gxt.theme.gray.Gray"/>
  <!--  inherits name="com.sencha.gxt.theme.blue.Blue" -->

  <!-- Eentry point class                              -->
  <entry-point class='com.common.client.Gxt_container'  />

  <source path='client' />
  <source path='shared' />
</module>

В модуле конфигурации необходимо подключить модули (GWT, GXT), определить точку входа и тему (стиль интерфейса).

Листинг главного модуля Gxt_container.java

public class Gxt_container implements EntryPoint
{
    public void onModuleLoad()
    {
        RootPanel.get("wrapper").add(new MainPage().asWidget());
    }
}

Главный модуль приложения создает только интерфейс страницы MainPage и никаких дополнительных функций не выполняет.

Шаблон страницы TemplatePage.java

При разработке шаблона страницы необходимо определить и создать контейнеры шаблона.

Контейнеры шаблона

protected  HorizontalLayoutContainer hlcHeader = null;
protected  HorizontalLayoutContainer hlcBody   = null;
protected  HorizontalLayoutContainer hlcFooter = null;

protected  HorizontalPanel           hpLeft    = null;
protected  HorizontalPanel           hpCenter  = null;
protected  HorizontalPanel           hpRight   = null;

private    VerticalLayoutContainer   vlc       = null;
private    ContentPanel              panel     = null;

Шаблон страницы включает три основных горизональных контейнера hlcHeader, hlcBody, hlcFooter, которые помещаются в вертикальный контейнер vlc. Горизонтальный контейнер тела страницы hlcBody включает дополнительно три горизонтальные панели hpLeft, hpCenter, hpRight. Контейнеры шаблона имеют модификаторы protected, чтобы быть доступными в наследниках.

Контейнер vlc помещается в тело основной панели страницы panel типа ContentPanel. Этот объект отображается в интерфейсе страницы и является основой для размещения компонентов. Таким образом, формируется дерево компонентов, в котором контейнеры играют роль ветвей.

Создание верхнего и нижнего контейнера

private void createHeaderPanel()
{
    hlcHeader = new HorizontalLayoutContainer();
    hlcHeader.setHeight(24);
    hlcHeader.setBorders(true);
    hlcHeader.getElement().getStyle().setBackgroundColor("yellow"); 
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private void createFooterPanel()
{
    hlcFooter = new HorizontalLayoutContainer();
    hlcFooter.setHeight(32);
    hlcFooter.setBorders(true);
    hlcFooter.getElement().getStyle().setBackgroundColor("cyan"); 
}

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

Создание центрального контейнера страницы

Для центрального контейнера установим флаг прорисовки контура. В качестве компонентов контейнера тела выберем объекты типа HorizontalPanel. Крайние панели ограничим по ширине, панели подсветим серым цветом разных оттенков.

Для размещения панелей в центральном контейнере используем HorizontalLayoutData, устанавливающий местоположение компонента в интерфейсе. Параметры HorizontalLayoutData (width, height), определяющие положение компонента по горизонтали и вертикали соответственно, имеют следующие значения :

  • -1 : размер компонента в соответствующем направлении (ширине/высоте) не изменять;
  • 1 : растянуть компонент по возможно максимальному размеру (ширине/высоте).
Листинг метода создания центральной панели
private void createBodyPanel()
{
    hlcBody = new HorizontalLayoutContainer();
    hlcBody.setBorders(true);
		
    hpLeft   = new HorizontalPanel(); 
    hpCenter = new HorizontalPanel(); 
    hpRight  = new HorizontalPanel(); 
		
    hpLeft  .setWidth("75px" );
    hpRight .setWidth("120px");

    hpLeft  .getElement().getStyle().setBackgroundColor("#CCC");
    hpCenter.getElement().getStyle().setBackgroundColor("#EEE");
    hpRight .getElement().getStyle().setBackgroundColor("#CCC");
	
    Margins margins = new Margins(0, 2, 0, 2);
    hlcBody.add(hpLeft  , new HorizontalLayoutData(-1, 1));
    hlcBody.add(hpCenter, new HorizontalLayoutData( 1, 1, margins));
    hlcBody.add(hpRight , new HorizontalLayoutData(-1, 1));
}

Размеры панелей по вертикали растягиваются по максимальному размеру контейнера. По горизонтали размеры крайних панелей не изменяются. Центральная панель растягивается по горизонтали по максимальному оставшемуся размеру контейнера после размещения левой и правой панелей. Параметр margins определяет отступы панели [top, right, bottom, left]. Т.е. центральная панель, как и крайние панели по вертикали занимает максимальный размер, а слева и справа отступает от боковых панелей на 2 пикселя.

Листинг шаблона TemplatePage.java

public class TemplatePage implements IsWidget
{
    // контейнеры шаблона
	// . . .
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    public TemplatePage()
    {
        panel = new ContentPanel();
        panel.setBodyBorder   (false);
        panel.setBorders      (false);
        panel.setHeaderVisible(false);
        panel.setPixelSize (400, 180);

        vlc = new VerticalLayoutContainer();
        vlc.setBorders(true);
        vlc.getElement().getStyle().setBackgroundColor("white"); 

        createHeaderPanel();
        createBodyPanel  ();
        createFooterPanel();

        Margins margins1 = new Margins(2, 2, 0, 2);
        Margins margins2 = new Margins(2, 2, 2, 2);
        vlc.add (hlcHeader, new VerticalLayoutData( 1,-1, margins1));
        vlc.add (hlcBody  , new VerticalLayoutData( 1, 1, margins1));
        vlc.add (hlcFooter, new VerticalLayoutData( 1,-1, margins2));
        panel.add(vlc);
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    protected void createHeaderPanel(){ . . . }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    protected void createBodyPanel() { . . . }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    protected void createFooterPanel(){ . . . }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    @Override
    public Widget asWidget()
    {
        return panel;
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}

В конструкторе шаблона TemplatePage.java создается панель panel определенного размера. Контуры и титульная полоса не прорисовываются. В панель помещается вертикальный контейнер vlc, в котором размещаем контейнеры компонентов интерфейса. При размещении контейнеров hlcHeader, hlcBody, hlcFooter используем класс VerticalLayoutData, назначение параметров которого соответствует параметрам класса HorizontalLayoutData, описанного выше.

Шаблон страницы реализует интерфейс IsWidget, согласно которому в компоненте необходимо переопределить метод asWidget(). В примере метод asWidget возвращает панель страницы, которая отображается в интерфейсе.

Примечание : метод asWidget вызывается автоматически при размещении компонента в интерфейсе (добавлении компонента в контейнер, панель и т.п.).

Главная страница MainPage.java

Главная страница наследует свойство шаблона TemplatePage. Метод createBody формирует интерфейс центральной панели тела страницы, в которой отображаем гиперссылку. Для размещения гиперссылки в интерфейсе страницы используем вертикальный контейнер и объект определения отступов Margins.

public class MainPage extends TemplatePage
{
    final String URL = "https://www.sencha.com";
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    public MainPage() 
    {
        super();
        createBody();
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    private void createBody()
    {
        VerticalLayoutContainer vCenter = new VerticalLayoutContainer();
        hpCenter.add(vCenter);
        Margins margins = new Margins(10, 0, 0, 5);

        VerticalLayoutData vld = new VerticalLayoutData(1,-1, margins);
        vCenter.add(new HyperLynk (URL), vld);
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}

На этом можно было бы и закончить повествование, использовав вместо гиперссылки простой объект типа кнопки или метки. Но выбор на гиперссылку «пал» не случайно. Если Вы «дошли» до этого места, то хочется дополнительно показать Вам механизм использования JavaScript Native Interface (JSNI) на примере гиперссылки.

Листинг гиперссылки Hyperlink.java

Объект гиперссылки HyperLynk оформлен как Widget, в котором метод asWidget переопределен. В описании объекта все прозрачно - в конструктор передается адрес URL, создается гиперссылка, к гиперссылке подключается обработчик событий ClickListener. Но вот с обработчиком событий не все так просто. Дело в том, что все переходы с одной страницы на другую должны попадать в «историю» браузера, чтобы он мог вернуться назад при нажатии на соответствующую кнопку. Поэтому для гиперссылки, используемой для смены страницы, необходимо подключать другие обработчики событий. Но в нашем примере по нажатию на гиперссылку выполняется открытие новой вкладки. Поэтому мы используем метод ClickListener, несмотря на то, что он Deprecated.

Что касается обработчика события гиперссылки ClickListener, то в документации можно увидеть следующую информацию.

@Deprecated
void com.google.gwt.user.client.ui.Hyperlink.addClickListener(ClickListener listener)

Use Anchor.addClickHandler instead and call History.newItem from the handler if you need to process the click before the history token is set.

В листинге соответствующие объекты представлены зачеркнутым стилем. Чтобы IDE Eclipse меньше «ругался» использована аннотация @SuppressWarnings("deprecation").

В обработчике события ClickListener сначала создается объект вкладки, после чего её можно сразу же открыть, вызвав метод setWindowTarget (закомментированный вызов метода). Более правильный подход, реализованный в коде, открыть вкладку через асинхронный обратный вызов, чтобы не подвешивать программу в случае существенной задержки при обработке события в браузере.

Методы создания вкладки newWindow и открытия вкладки в браузере setWindowTarget оформлены как «нативные» JSNI.

import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.Hyperlink;
import com.google.gwt.user.client.ui.ClickListener;

import com.google.gwt.core.client.JavaScriptObject;

import com.google.gwt.user.client.rpc.AsyncCallback;

@SuppressWarnings("deprecation")
public class HyperLynk implements IsWidget
{
    private Hyperlink hyperlink = null;
	
    public HyperLynk(final String url)
    {
        hyperlink = new Hyperlink(url, "");
        hyperlink.addClickListener (new ClickListener(){
            @Override
            public void onClick(Widget sender) {
                final JavaScriptObject window = newWindow(url, "", "");
//              setWindowTarget(window, URL);
                new AsyncCallback<Object>() {
                    public void onSuccess(Object o)  {
                        setWindowTarget(window, url);
                    }
                    @Override
                    public void onFailure(Throwable caught) {}
                };            
            }
        });

    }
    @Override
    public Widget asWidget()
    {
        return hyperlink;
    }

    /**
     * Процедуры создания новой вкладки с заданным URL
     * @param url адрес страницы в интернете
     * @param name наименование
     * @param features свойства
     * @return JavaScriptObject объект вкладка браузера
     */
    private static native JavaScriptObject newWindow(String url, String name, String features)/*-{
        var window = $wnd.open(url, name, features);
        return window;
    }-*/;

    /**
     * Процедуры открытия новой вкладки с заданным URL
     * @param window окно браузера
     * @param url адрес страницы
     */
    private static native void setWindowTarget(JavaScriptObject window, String url)/*-{
        window.location = target;
    }-*/;
}

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

Исходный код рассмотренного GWT-GXT примера с использованием контейнеров горизонтального и вертикального размещения компонентов, а также гиперссылки в виде проекта Eclipse можно скачать здесь (46 Кб).

В архив примера не включены библиотечный файл 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