GWT-GXT пример с ComboBoxБиблиотека GXT позволяет расширить интерфейсные возможности фреймворка GWT дополнительными визуальными компонентами, различными вариантами их размещения, возможностью работы с моделями данных и кэширующей их подсистемой. Интерфейс WEB-приложения с совместным использованием GWT-GXT можно оформить в виде традиционных десктопных приложений. Подробности инсталляции и демонстрационные возможности библиотеки Sencha GXT рассмотрены здесь. На данной странице рассматривается GWT-GXT в среде разработки IDE Eclipse. В примере используется визуальный компонент в виде выпадающего списка типа ComboBox. Возможности компонента не ограничиваются выбором значения из простого списка записей. Можно в раскрывающемся окне рядом с записью отобразить иконку, либо отобразить информацию о записи в нескольких колонках. В примере будет представлено 3 различных варианта использования компонента ComboBox. На следующем скриншоте представлен один из вариантов использования ComboBox с дополнительным размещением иконок (флаги стран) в записях : Описание компонента с отображением иконок в выпадающем списке представлено здесь. Структура GWT-GXT примераПроцесс создания GWT-проекта в среде Eclipse здесь не рассматривается; он представлен на странице примера Welcome. При определении параметров данного проекта выбрано наименование gxt-combobox, определен корневой пакет для java-классов com.example, сняты флажки «Use Google App Engine» и «Generate project sample code». На следующем скриншоте представлена структура GWT-GXT проекта в среде разработки IDE Eclipse, включающая файл конфигурации Gxt_combobox.gwt.xml, главный модуль приложения Gxt_combobox.java, поддиректорию с файлами данных com.example.client.data, поддиректорию с widget'ом ComboboxStock, директорию war со стандартным набором файлов для WEB-приложения и графическими файлами поддиректории images/flags, используемые для отображения флагов стран. Формируемые GWT-компилятором коды скриптов (JavaScript) размещаются в поддиректории gwt-unitCache. Фреймворк GWT был подключен к проекту на этапе его создания. Библиотека GXT была подключена как пользовательская (User Library). Более подробно об инсталляции и подключении библиотеки GXT к проекту GWT можно прочитать здесь. Хранилище объектов компонентов ComboBoxДля компонентов ComboBox необходимо создать хранилище типа ListStore<T>. Тип объектов хранилища должен совпадать с типом объектов ComboBox. В примере используются два типа объектов (Stock, Country), для которых создаются два хранилища. Ниже представлено описание хранилищ и объектов, интерфейсы свойств объектов, файлы с данными и описан процесс создания хранилищ ListStore<T>. Описание хранилищ и объектовВ следующем коде представлено определение хранилищ и описание объектов (методы get/set не включены) : public static ListStore<Stock> STORE = null; private ListStore<Country> COUNTIRES = null; ... public class Stock { private int id; private String name; private String symbol; private static int COUNTER = 0; public Stock() { this.id = COUNTER++; } public Stock(String name, String symbol) { this(); this.name = name; this.symbol = symbol; } } public class Country { private String abbr ; private String name ; public Country() {} public Country(String abbr, String name) { setAbbr(abbr); setName(name); } } Поскольку хранилище STORE будет использоваться в двух компонентах ComboBox, то оно определено как статическое, чтобы можно было бы получить к нему удаленный доступ без передачи ссылки. Листинги интерфейсов свойств объектовДля создания хранилища необходимо определить интерфейсы свойств объектов (StockProperties, CountryProperties) типа PropertyAccess<T>. import com.google.gwt.editor.client.Editor.Path; import com.sencha.gxt.data.shared.LabelProvider; import com.sencha.gxt.data.shared.ModelKeyProvider; import com.sencha.gxt.data.shared.PropertyAccess; import com.sencha.gxt.core.client.ValueProvider; public interface StockProperties extends PropertyAccess<Stock> { @Path("id") ModelKeyProvider<Stock> key(); @Path("symbol") LabelProvider<Stock> symbolLabel(); ValueProvider<Stock, String> name (); ValueProvider<Stock, String> symbol(); } public interface CountryProperties extends PropertyAccess<Country> { @Path("abbr") ModelKeyProvider<Country> abbr(); @Path("name") LabelProvider<Country> name(); } Файл данных StockData.javaimport java.util.List; import java.util.ArrayList; public class StockData { public static List<Stock> getStocks() { List<Stock> stocks = new ArrayList<Stock>(); stocks.add(new Stock("Apple Inc." , "Apple" )); stocks.add(new Stock("Cisco Systems, Inc." , "Cisco" )); stocks.add(new Stock("Google Inc." , "Google" )); stocks.add(new Stock("Intel Corporation" , "Intel" )); stocks.add(new Stock("Microsoft Corporation", "Microsoft")); stocks.add(new Stock("Nokia Corporation" , "Nokia" )); stocks.add(new Stock("Oracle Corporation" , "Oracle" )); stocks.add(new Stock("eBay Inc." , "Ebay" )); stocks.add(new Stock("Avaya Inc." , "Avaya" )); return stocks; } } Файл данных CountryData.javaimport java.util.List; import java.util.ArrayList; public class CountryData { public static List<Country> getCountries() { List<Country> countries = new ArrayList<Country>(); countries.add(new Country("ad", "Andora" )); countries.add(new Country("ae", "Arab Emirates" )); countries.add(new Country("ag", "Antigua And Barbuda")); countries.add(new Country("ai", "Anguilla" )); countries.add(new Country("al", "Albania" )); countries.add(new Country("am", "Armenia" )); countries.add(new Country("an", "Neth. Antilles" )); countries.add(new Country("ao", "Angola" )); countries.add(new Country("ar", "Argentina" )); return countries; } } Листинг метода создания хранилищПри создании хранилищ сначала определяются интерфейсы свойств объектов (sprops, cprops). После этого создаются хранилища, в конструкторы которых передаются объекты типа ModelKeyProvider, определяющие уникальность записей. После создания хранилищ в них загружаются данные списком методом addAll. private StockProperties sprops = null; private CountryProperties cprops = null; private void createStores() { sprops = GWT.create(StockProperties .class); cprops = GWT.create(CountryProperties.class); STORE = new ListStore<Stock> (sprops.key()); STORE.addAll(StockData.getStocks()); COUNTIRES = new ListStore<Country>(cprops.abbr()); COUNTIRES.addAll(CountryData.getCountries()); } Создание простого ComboBoxСоздание простого объекта combo1 выполняется в методе createCombobox1. При создании выпадающего списка combo1 в конструктор передается хранилище STORE и объект типа LabelProvider<? super Stock>, определяющий отображаемое в компоненте поле объекта. private ComboBox<Stock> combo1 = null; private void createCombobox1() { combo1 = new ComboBox<Stock>(STORE, sprops.symbolLabel()); combo1.setEmptyText("Select a company..."); combo1.setName("сombo1"); combo1.setTypeAhead(true); combo1.setTriggerAction(TriggerAction.ALL); combo1.setWidth(180); combo1.addSelectionHandler(selectStock); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SelectionHandler<Stock> selectStock = new SelectionHandler<Stock>() { @Override public void onSelection(SelectionEvent<Stock> selectionEvent){ /* * Очистка фильтра, устанавливаемого вводом символов с клавиатуры */ if ((STORE != null) && (STORE.getFilters() != null) && (STORE.getFilters().size() > 0)) STORE.removeFilters(); } }; Свойство EmptyText позволяет отображать в компоненте текст, если не выбрано ни одной записи из списка. Метод setTypeAhead при истинном значении (true) помогает пользователю выбрать запись из списка при наборе начальных символов с клавиатуры - оставшаяся часть наименования записи «дописывается». Метод setTriggerAction позволяет исключить или установить взаимосвязь двух и более компонентов. Для этого можно использовать одно из значений :
Важно отметить, что выбор записи из списка по начальным символам с использованием клавиатуры сопровождается установкой соответствующего фильтра в хранилище данных. Если хранилище данных используется совместно с другим компонентом, то непроизвольная фильтрация данных также отражается и на другом компоненте. Что исключить влияние одного компонента на другой к компоненту combo1 подключается обработчик событий selectStock, который, после выбора значения, очищает фильтр записей, устанавливаемый при работе с клавиатуры. Можно заблокировать выбор записи по первым символам с использованием клавиатуры. Для этого необходимо использовать метод setEditable(boolean) со значением false. На следующем скриншоте представлен раскрытый список combo1. Если ни одной записи не выбрано, то в компоненте отображается подстановочная запись EmptyText («Select a company...»). Создание ComboBox с иконками в выпадающем спискеДля создания выпадающего списка ComboBox с отображением иконок необходимо использовать конструктор, которому в качестве параметров передается renderer типа SafeHtmlRenderer<T> для отображения значения в виде HTML строки : ComboBox<T>(ListStore<T> store, LabelProvider<? super Country> label, SafeHtmlRenderer<T> renderer);
Необходимо подготовить renderer. Для этого создается интерфейс ISafeHtml, расширяющий свойства класса XTemplates библиотеки GXT и включающий функцию SafeHtml country(SafeUri imageUri, String name). В качестве параметров в функцию необходимо передать адрес изображения URL и наименование соответствующей записи. Аннотация к методу @XTemplate позволяет определить формат представления записей в выпадающем списке. import com.google.gwt.safehtml.shared.SafeHtml; import com.sencha.gxt.core.client.XTemplates; . . . interface ISafeHtml extends XTemplates { @XTemplate("<img width=\"16\" src=\"{imageUri}\"> {name}") SafeHtml country(SafeUri imageUri, String name); } Для создания выпадающего списка combobox2 с иконками используется метод createCombobox2. Первоначально создается рендерер iSafeHtml стандартным для GWT методом create. После этого создается combo2. В методе render формируется URL изображения флага для каждой страны и вызывается метод country объекта iSafeHtml. При определении URL изображения используется GXT форматирование (Format.substitute), подробно описанное здесь. Методы setTypeAhead и setTriggerAction описаны выше. import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeUri; import com.google.gwt.safehtml.shared.UriUtils; import com.sencha.gxt.core.client.XTemplates; . . . private ISafeHtml iSafeHtml = null; private ComboBox<Country> combo2 = null; private final String URL_TEMPL = "{0}images/flags/{1}.png"; . . . private void createCombobox2() { iSafeHtml = GWT.create(ISafeHtml.class); combo2 = new ComboBox<Country>(COUNTIRES, cprops.name(), new AbstractSafeHtmlRenderer<Country>() { @Override public SafeHtml render(Country item){ String url = Format.substitute(URL_TEMPL, GWT.getHostPageBaseURL(), item.getAbbr()); SafeUri imageUri = UriUtils.fromString(url); return iSafeHtml.country(imageUri, item.getName()); } }); combo2.setWidth(180); combo2.setTypeAhead(true); combo2.setTriggerAction(TriggerAction.ALL); } Скриншот компонента ComboBox с иконками в выпадающем списке представлен наверху. Создание ComboBox с двумя колонками в выпадающем спискеДля создания выпадающего списка из двух и более колонок со значениями, определенными в модели данных, необходимо предварительно :
Формат представления данных опишем в файле stock.html. Класс StockCell.java будет формировать данные для представления в выпадающем списке. Листинг stock.htmlВ файле stock.html определены 2 колонки, размеры которых составляют 20% и 80% соответственно. В первой колонке будет представлено значение stock.id, во второй stock.name. <!-- stock.html --> <div style="width: 100%; overflow: hidden; white-space: nowrap;"> <div style="width: 20%; display: inline-block; overflow: hidden; vertical-align: middle;">{stock.id}</div> <div style="width: 80%; display: inline-block; overflow: hidden; vertical-align: middle;">{stock.name}</div> </div> Листинг StockCell.javaКласс StockCell наследует свойства AbstractCell и используется для описания представления ячейки выпадающего списка. Формирование записей для отображения выполнятся методом render. Интерфейс StockXTemplates, как и в предыдущем компоненте combo2, наследует свойства класса XTemplates. Аннотация метода renderCell определяет представление значения в интерфейсе раскрывающегося списка. import com.example.client.data.Stock; import com.google.gwt.core.client.GWT; import com.sencha.gxt.core.client.XTemplates; import com.google.gwt.cell.client.AbstractCell; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; public class StockCell extends AbstractCell<Stock> { private StockXTemplates tpl = GWT.create(StockXTemplates.class); public interface StockXTemplates extends XTemplates { @XTemplate(source = "stock.html") SafeHtml renderCell(Stock stock); } @Override public void render(Context context, Stock stock,SafeHtmlBuilder sb){ sb.append(tpl.renderCell(stock)); } } Листинг ComboboxStock.javaТретий компонент выпадающего списка выполнен в виде отдельного widget'а. Конструктор компонента в качестве параметра получает ссылку на обработчик события, возникающего при выборе записи в компоненте; обработка выполняется «родителем». public class ComboboxStock implements IsWidget { private ComboBox <Stock> comboBox = null; private StockProperties properties = null; public ComboboxStock (SelectionHandler<Stock> handler) { properties = GWT.create(StockProperties.class); createComboBox(handler); } ComboBox<Stock> createComboBox(SelectionHandler<Stock> handler) { ListView<Stock, Stock> listView = null; IdentityValueProvider<Stock> identity = null; StockCell stockCell = null; ComboBoxCell<Stock> comboCell = null; identity = new IdentityValueProvider<Stock>(); stockCell = new StockCell(); listView = new ListView<Stock, Stock>(Gxt_combobox.STORE, identity, stockCell); comboCell = new ComboBoxCell<Stock> (Gxt_combobox.STORE, properties.symbolLabel(), listView); comboBox = new ComboBox<Stock>(comboCell); comboBox.setEditable(false); comboBox.setTriggerAction(TriggerAction.ALL); comboBox.setWidth(80); comboBox.addSelectionHandler(handler); return comboBox; } @Override public Widget asWidget() { return comboBox; } } В методе createComboBox создается третий компонент с выпадающим списком. Сначала подготавливается объект представления listView, после этого создается ячейка представления записи comboCell. Конструктор ComboBox получает в качестве единственного параметра объект comboCell. Настройка свойств объекта (setTriggerAction, setEditable) описана выше. Важно знать, что метод asWidget вызывается при размещении компонента в интерфейсе. Если widget используется неоднократно, то нежелательно в этом методе создавать объект. В примере объект создается в конструкторе компонента. Интерфейс компонента с раскрытым списком из двух колонок представлен на следующем скриншоте. Главный модуль проектаГлавный модуль Gxt_combobox.java реализует интерфейсы IsWidget и EntryPoint, включающие методы asWidget (widget интерфейса) и onModuleLoad (точка входа). Листинг главного модуля приложения представлен в усеченном виде. Все остальное представлено выше и не составит особого труда «слепить весь код воедино», либо скачать исходный код. public class Gxt_combobox implements IsWidget, EntryPoint { SelectionHandler<Stock> handler = new SelectionHandler<Stock>() { @Override public void onSelection(SelectionEvent<Stock> selectionEvent) { Stock stock = selectionEvent.getSelectedItem(); Info.display("SelectionHandler", stock.getName()); } }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override public Widget asWidget() { ContentPanel panel = new ContentPanel(); panel.setHeadingText("GXT ComboBox"); panel.setPixelSize(240, 180); panel.addStyleName("margin-5"); createStores (); createCombobox1(); createCombobox2(); ComboboxStock combo3 = new ComboboxStock(handler); Margins margins = new Margins(10, 6, 0, 6); VerticalLayoutContainer vlc = new VerticalLayoutContainer(); vlc.add(combo1, new VerticalLayoutData(-1, -1, margins)); vlc.add(combo2, new VerticalLayoutData(-1, -1, margins)); vlc.add(combo3, new VerticalLayoutData( 1, -1, margins)); panel.add(vlc); return panel; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public void onModuleLoad() { RootPanel.get("wrapper").add(asWidget()); } } Обработчик событий handler будет вызываться при выборе одной из записи выпадающего списка combo3 для отображения наименования в кратковременно всплывающем окне Info . В методе asWidget создается панель типа ContentPanel - определяется размер панели и стиль "margin-5". Чтобы компоненты равномерно разместить в интерфейсе используется контейнер вертикального размещения VerticalLayoutContainer. Интерфейс главной панели создается как widget и размещается в секции «wrapper» (Gxt_toolbar.html). При формирование интерфейса страницы были использованы компоненты VerticalLayoutContainer и Margins, подробно описанные здесь. Листинг Gxt_combobox.htmlТело страницы содержит секцию wrapper, в которую помещается создаваемая главным модулем панель с компонентами. <!doctype html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link type="text/css" rel="stylesheet" href="Gxt_combobox.css"> <title>GXT ComboBox</title> <script type="text/javascript" src="gxt_combobox/gxt_combobox.nocache.js" ></script> </head> <body> <div id="wrapper"></div> </body> </html> Конфигурационный файл проекта Gxt_combobox.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_combobox'> <inherits name='com.google.gwt.user.User' /> <!-- Other module inherits --> <inherits name="com.sencha.gxt.ui.GXT" /> <inherits name="com.sencha.gxt.theme.gray.Gray" /> <!-- inherits name="com.sencha.gxt.theme.blue.Blue" --> <!-- inherits name='com.google.gwt.user.theme.standard.Standard'--> <!-- inherits name='com.google.gwt.user.theme.dark.Dark' --> <!-- inherits name='com.google.gwt.user.theme.chrome.Chrome' --> <!-- Entry point class --> <entry-point class='com.example.client.Gxt_combobox' /> <!-- Specify the paths for translatable code --> <source path='client'/> <source path='shared'/> </module> В конфигурационном файле Gxt_combobox.gwt.xml определяем библиотеку GXT, выбираем тему (стиль интерфейса) Gray бибилиотеки GXT (Gray, Blue). Можно выбрать тему и из набора GWT (Standard, Dark, Chrome). В качестве главного модуля приложения, включающего точку входа в приложение (метод onModuleLoad), определен com.example.client.Gxt_combobox. Скачать исходный код примераИсходный код рассмотренного GWT-GXT примера с использованием ComboBox в виде проекта Eclipse можно скачать здесь (7.85 Мб). В архив примера включен библиотечный файл 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 |