|
AWT клиент для веб-приложения. Часть 2
(Николай Зайкин)
3. Клиентская часть.
В качестве клиентской части словаря можно использовать как обычный браузер (MSIE, NC,
Opera, и др.), так и Java приложение, которое является собственно основной темой данной
статьи.
Для написания графического интерфейса была выбрана библиотека (пакеты классов
java.awt.*) AWT (Abstract Windows Toolkit). Стоит упомянуть, что наряду с ним в Java 2
появилась еще библиотека Swing (java.swing.*), которая имеет расширенную коллекцию
классов графических элементов интерфейса, и которая предоставляет программисту гораздо
большие возможности, такие как: панели со вкладками, панели с прокруткой, деревья и
таблицы. Все графические компоненты библиотеки Swing написаны на Java и являются
платформенно-независимыми (в отличие от AWT, компоненты которой написаны на
платформо-специфичном коде).
Но для приложения по ряду причин (даже используя Swing, нам необходимы классы событий
из пакетов java.awt.event.*; Swing еще не является полной заменой классам AWT - например
в AWT есть готовый класс FileDialog для сохранения файлов; и для избежания неразберихи
начинающими программистами) была выбрана библиотека AWT. Что впрочем не мешает вам после
прочтения статьи безболезненно переписать приложение с использованием аналогичных Swing
классов.
Что должно представлять из себя клиентское приложение? Во-первых, иметь строку ввода
для переводимого слова. Во-вторых, иметь кнопку или другой элемент управления, вызывающий
отправку HTTP запроса. И, в-третьих, текстовое поле для отображения результата поиска
перевода, полученный от удаленного сервера.
Также желательны следующие функции, не являющиеся обязательными, но все же которые
strongly recommended :)
- Настройка алгоритма типа поиска (см. 2. Серверная часть). Раз эта функция уже
присутствует на сервере, почему же ее не использовать.
- Возможность работать через HTTP-proxy (в большинстве современных http клиентов эта
возможность присутствует).
- Механизм сохранения настроек, чтобы при следующем запуске пользователь не вводил имя
прокси-сервера и порт снова, а чтобы использовались настройки последнего сеанса
работы.
Графическое Java приложение обычно является подклассом Frame (или
JFrame), который, являясь подклассом Window, инкапсулирует окно
cо строкой заголовка, строкой меню, обрамлением и способностью менять свой размер
растягиванием за угол окна. public class MobileDictionary extends Frame implements
ActionListener, ItemListener
Класс Frame имеет два типа конструкторов: Frame() - без
параметров, создает окно с пустым заголовком и Frame(String s) - создает
окно с заданным заголовком. Мы создаем окно в методе main(String[] args), в
который передается управление JVM (Java Virtual Machine - Виртуальной Машиной Java) после
запуска приложения.
public static void main(String[] args) {
// main
new MobileDictionary(TITLE);
}
Что же происходит в конструкторе (кстати, конструктор - особый метод, имя конструктора
всегда совпадает с именем класса; конструкторов может быть несколько в теле класса, тогда
они отличаются друг от друга количеством и (или) типом аргументов)? Так как приложение
графическое, стоит ожидать, что в конструкторе создаются графические элементы интерфейса
и задаются их позиции в главном окне. На 99%, так оно и есть. Рассмотрим
конкретнее...
Получив в качестве аргумента строку (будущий заголовок окна), выполняем конструктор
родителя (класса Frame) с аналогичным синтаксисом для установления заголовка
окна.
public MobileDictionary(String title) {
super(title);
Выше, в описании желаемой функциональности приложения, была упомянута возможность
прозрачного сохранения настроек пользователя от предыдущего запуска программы. Для
получения старых данных самое лучшее место - в конструкторе
MobileDictionary. Ведущие стоматологи и собаководы всего мира рекомендуют
использовать класс Properties (наследник класса Hashtable, не
мне объяснять Perl-программистам для чего используются хэши) для сохранения и
восстановления каких-либо текстовых настроек программы, они аргументируют свой выбор не
только тем, что ваши волосы будут густыми и шелковистыми, но и еще тем, что этот класс
уже содержит все методы для описанных действий (setProperty,
load, store, setProperty или эквивалентный
put, наследованный от Hashtable). Кстати, если вы используете эту программу
не один, не стоит узнавать перевод слова "crap" последним, в противном случае
следующий пользователь может неправильно вас понять...
// set default values
prop = new Properties();
// try to load values from disc
try {
fis = new FileInputStream("MobileDictionary.dat");
} catch (FileNotFoundException e) {
// ignore exception
}
try {
if(fis != null) {
prop.load(fis);
fis.close();
useProxy = prop.getProperty("useProxy").equals("true");
proxyURL = prop.getProperty("proxyURL");
proxyPort = prop.getProperty("proxyPort");
wordDefault = prop.getProperty("wordDefault");
typeDefault = prop.getProperty("typeDefault", typeDefault);
hostURL = prop.getProperty("hostURL", hostURL);
}
} catch (IOException e) {
System.out.println("Error reading properties file!");
System.out.println(e);
}
Сначала создаем панели - контейнеры для графических компонентов интерфейса,
устанавливаем для панелей алгоритмы размещения элементов внутри (так называемые
LayoutManagers).
// set layout of main frame
setLayout(new BorderLayout(5, 5));
// create new outerPanel
Panel outerPanel = new Panel() {
// set space between panel and frame
public Insets getInsets() {
return new Insets(3, 3, 3, 3);
}
};
// set layout to panel
outerPanel.setLayout(new BorderLayout(3, 3));
// add outerPanel to frame into the center
add(outerPanel, BorderLayout.CENTER);
// create new wordPanel
Panel wordPanel = new Panel();
// add wordPanel to outerPanel into North
outerPanel.add(wordPanel, BorderLayout.NORTH);
// create new optionsPanel
Panel optionsPanel = new Panel();
optionsPanel.setLayout(new FlowLayout());
// add optionsPanel to outerPanel into Center
outerPanel.add(optionsPanel, BorderLayout.CENTER);
// create new resultingPanel
Panel resultingPanel = new Panel();
resultingPanel.setLayout(new BorderLayout(3, 3));
// add resultingPanel to outerPanel into South
outerPanel.add(resultingPanel, BorderLayout.SOUTH);
Создаем и размещаем графические элементы. Регистрируем блоки прослушивания событий для
каждого источника (элемента). Например, мы создаем блок прослушивания, который реагирует
на нажатие клавиши Enter, когда пользователь набирает слово в текстовом поле, и который
запускает процедуру перевода слова. Обработка событий будет подробно рассмотрена ниже (в
следующей части статьи).
// add the elements to panels ...
// ********** wordPanel
wordPanel.add(new Label("Word:"));
wordField = new TextField(wordDefault, WORD_WIDTH);
wordField.setSelectionStart(0);
wordField.setSelectionEnd(wordDefault.length());
wordField.addActionListener(this);
KeyAdapter dictKeyAdapter = new KeyAdapter() {
public void keyPressed(java.awt.event.KeyEvent ke) {
int key = ke.getKeyCode();
if (key == KeyEvent.VK_ENTER) {
performTranslation();
}
}
};
wordField.addKeyListener(dictKeyAdapter);
wordPanel.add(wordField);
translate = new Button(" Translate! ");
translate.addActionListener(this);
wordPanel.add(translate);
// ********** optionsPanel
optionsPanel.add(new Label("Options:", Label.LEFT));
// create new radioPanel
Panel radioPanel2 = new Panel();
CheckboxGroup cbg = new CheckboxGroup();
cb1 = new Checkbox(CB_LABEL1, cbg, (typeDefault.equals("1")));
cb2 = new Checkbox(CB_LABEL2, cbg, (typeDefault.equals("2")));
cb3 = new Checkbox(CB_LABEL3, cbg, (typeDefault.equals("3")));
radioPanel2.setLayout(new GridLayout(3, 1));
radioPanel2.add(cb1);
radioPanel2.add(cb2);
radioPanel2.add(cb3);
cb1.addItemListener(this);
cb2.addItemListener(this);
cb3.addItemListener(this);
optionsPanel.add(radioPanel2);
// ********** resultingPanel
resultArea = new TextArea("", 12, 37, 1);
resultArea.setEditable(false);
resultingPanel.add(resultArea);
// create menu bar and add it to frame
MenuBar mbar = new MenuBar();
setMenuBar(mbar);
// create the menu items
Menu file = new Menu("File");
MenuItem item10;
file.add(item10 = new MenuItem("Exit"));
mbar.add(file);
Menu edit = new Menu("Edit");
MenuItem item20;
edit.add(item20 = new MenuItem("Settings..."));
mbar.add(edit);
Menu help = new Menu("Help");
MenuItem item30;
help.add(item30 = new MenuItem("About..."));
mbar.add(help);
// create an object to handle action events
MenuHandler handler = new MenuHandler(this);
// register it to receive those events
item10.addActionListener(handler);
item20.addActionListener(handler);
item30.addActionListener(handler);
Устанавливаем размеры окна в точности "подгоняя" по размерам находящихся в
нем элементов, основываясь на их менеджерах компоновки. После чего отображаем окно на
экране (создаваемое окно всега по умолчанию невидимо), запрещаем пользователю изменять
размер окна растягиванием за нижний угол. Добавлям блок прослушивания к окну, который
будет отслеживать событие, передаваемое перед закрытием окна, предварительно сохранять
текущие настройки прозрачно для пользователя и только потом завершать приложение.
pack();
setSize(getPreferredSize());
setVisible(true);
// fix the frame size
setResizable(false);
addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e) {
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
storeSettings();
// exit the application
System.exit(0);
}
}
}
);
}
Все, основное окно приложения создано !
TOC | Часть 3

|