Rambler's Top100IT • archiv

rus / eng | Логин | Комментарий к колонке | Печать | Почта | Клуб




Колонки


AWT клиент для веб-приложения. Часть 3

 
(Николай Зайкин)
3. Клиентская часть (продолжение).

После создания окна (экземпляра класса MobileDictionary), оно "начнинает жить собственной жизнью". То есть цель метода main - просто создать объект класса MobileDictionary. А далее работа приложения зависит от событий, которые получает приложение от своих компонентов (элементов управления).

Давайте теперь остановимся на рассмотрении модели событий в Java 2. Начиная с Java 1.1 метод обработки событий существенно изменился. Старый метод (из Java 1.0) все еще поддерживается, но не рекомендуется к использованию. Современный метод обработки событий основан на модели делегирования событий. Вкратце схема такова: источник генерирует событие, и посылает его одному или нескольким блокам прослушивания. Блок прослушивания конкретного вида событий должен быть предварительно зарегистрирован в источнике события. В этом и есть отличие от модели Java 1.0, в которой событие распространялось по ограниченной иерархии компонентов, независимо от того, "хотят" ли они принимать это событие или нет, и даже не рассматривая, способен ли компонент вообще обрабатывать данное событие. Распространение события среди всех компонентов приводило к усложнению кода и увеличивало временные затраты. Однако, Java 2. Продолжим. Получив событие, блок прослушивания обрабатывает его и передает упавление назад.

Событие - это специальный объект (как и все в Java :), описывающий изменение состояния источника, его породившего. Событие может генерироваться как пользователем (через элменты управления), так и таймером, счетчиком и пр.

В данной программе используются только события от элементов управления (кнопки интерфейсы, меню окна, клавиатура) пользователя.

  1. Поле ввода слова (регистрирует блок прослушивания клавиатуры, событие KeyEvent);
  2. Кнопка перевода (регистрирует блок прослушивания объекта события класса ActionEvent);
  3. Меню (регистрирует блок прослушивания объекта события класса ActionEvent);
  4. Radio buttons (или, если угодно, Взаимоисключающие Флажки, о, Великий и Могучий...) (регистрируют блок прослушивания объекта события класса ItemEvent);

Источник события, как уже упоминалось, должен зарегистрировать блок прослушивания, чтобы последний мог получать события. Общая форма данной процедуры такова:

public void addListener (Listener l);

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

Теперь коснемся блока прослушивания (в народе, listener). Это объект, который обрабатывает какое-либо событие. Основное требование к нему - он должен реализовывать соответствующие интерфейсы (и соответственно, иметь в своем составе соответствующие методы), позволяющие принимать и обрабатывать события.

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

Создаем меню. Объектами, генерирующими события, будут item10, item20, item30, экземпляры класса MenuItem.

  // 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);

Создаем блок прослушивания - объект класса, реализующего интерфейс ActionListener(так как обычный пункт меню генерирует событие типа ActionEvent).

  // create an object to handle action and item events
   MenuHandler handler = new MenuHandler(this);

И, наконец, регистрируем блок прослушивания для всех пунктов меню.

  // register it to receive those events
   item10.addActionListener(handler);
   item20.addActionListener(handler);
   item30.addActionListener(handler);

Просто, не правда ли !?

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

В переменных класса определяем переменную типа MobileDictionary, чтобы иметь доступ к "родительскому" приложению при обработке событий (обратите внимание на dictFrame.storeSettings(); ниже). Конструктор получает ссылку на "родительский" объект и сохраняет ее в поле класса.

import java.awt.*;
import java.awt.event.*;

class MenuHandler implements ActionListener {
    MobileDictionary dictFrame;
    public MenuHandler(MobileDictionary dictFrame) {
        this.dictFrame = dictFrame;
    }

Собственно, класс имеет всего один метод public void actionPerformed(ActionEvent ae), определенный в интерфейсе ActionListener. Если бы интерфейс содержал больше методов, мы должны были бы обязательно их определить.

// handle action events
public void actionPerformed(ActionEvent ae) {
    String arg = (String)ae.getActionCommand();

    if(arg.equals("Exit")) {
        dictFrame.storeSettings();
        // exit the application
        System.exit(0);
    } else if(arg.equals("About...")) {
        // show about dilog
        AboutDialog ad = new
           AboutDialog(dictFrame, "About");
        ad.setVisible(true);
    } else if(arg.equals("Settings...")) {
        // show settings dialog
        SettingsDialog sd = new
           SettingsDialog(dictFrame, "Settings");
        sd.setVisible(true);
    }
}
}

Совсем не обязательно создавать отдельный класс для обработчика события. Его можно реализовать в главном классе приложения (MobileDictionary). Рассмотрим подробнее...

Не забываем, что блок прослушивания событий должен реализовывать соответствующие интерфейсы: public class MobileDictionary extends Frame implements ActionListener, ItemListener {

Регистрируем для источников событий блок прослушивания как указатель на данный объект:

  translate = new Button(" Translate! ");
   translate.addActionListener(this);

... или ...

  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")));
   cb1.addItemListener(this);
   cb2.addItemListener(this);
   cb3.addItemListener(this);

И, наконец, реализуем методы, описанные в интерфейсах ActionListener и ItemListener:

public void actionPerformed(ActionEvent event) {
   Object source = event.getSource();
   if (source == translate) {
       performTranslation();
   }
}

public void itemStateChanged(ItemEvent event) {
   Checkbox source = (Checkbox) event.getSource();
   if (source == cb1) {
       typeDefault = "1";
   } else if (source == cb2) {
       typeDefault = "2";
   } else if (source == cb3) {
       typeDefault = "3";
   }
}

Обратите внимание, что всего в приложении мы имеем 2 блока прослушивания событий ActionEvent - MenuHandler и MobileDictionary. За счет новой системы делегирования событий мы имеем возможность разделять логику обработки события для каждого элемента управления приложения, и за счет этого получать более понятную структуру кода. То есть, один блок, прослушивающий события ActionEvent от меню, вынесен в отдельный класс MenuHandler, а второй блок, прослушивающий события такого же класса, но от элементов Checkbox находится в классе MobileDictionary.

Описание методики обработки событий было бы неполным, если бы мы не упомянули о классах-адаптерах...

Адаптер (adapter class) - это специальный класс, имеющий пустые реализации всех методов интерфейса прослушивания какой-либо группы событий. Этот класс полезен, когда мы хотим обрабатывать только одно событие из группы, и для того, чтобы не писать реализации методов для других, ненужных нам событий, мы просто наследуем соответствующий класс адаптера при реализации своего блока прослушивания. Это сокращает код программы за счет исключения написания пустых методов.

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

  • keyTyped (клавиша нажата и отпущена),
  • keyPressed (клавиша нажата),
  • keyReleased (клавиша отпущена),
из которых для нас представляет интерес только keyPressed. И для того, чтобы не писать пустые реализации двух других методов, мы наследуем наш блок прослушивания от класса-адаптера KeyAdapter, который уже имеет описания этих всех трех методов, и переопределяем только метод keyPressed.
  wordField = new TextField(wordDefault, WORD_WIDTH);

   [skipped]

   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);


Вышеуказанный пример использует внутренний класс. Современная методика программирования на Java часто использует эту технологию. Этот пример можно еще более сократить, используя анонимный класс (класс, которому не назначено имя).
  wordField = new TextField(wordDefault, WORD_WIDTH);

   [skipped]

   wordField.addKeyListener(new KeyAdapter() {
       public void keyPressed(java.awt.event.KeyEvent ke) {
           int key = ke.getKeyCode();
           if (key == KeyEvent.VK_ENTER) {
               performTranslation();
           }
       }
   });

Итак, мы выяснили, при нажатии клавиши "Enter" запускается процедура перевода слова - основная функция приложения. Что же происходит между клиентом и сервером? Давайте, рассмотрим теперь ту часть программы, которая отвечает за коммуникацию с внешним миром...

TOC | Часть 4 >




Справка | Условия Copyright © 1999 — 2008, IT • archiv.
В начало | Логин | Комментарий к колонке | Поиск | Почта