Материал: Портянкин И. Swing

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Модель событий

35

public class LowLevelEvents extends JFrame { // сюда мы будем выводить информацию private JTextArea out;

public LowLevelEvents() { super("LowLevelEvents");

//при закрытии окна - выход setDefaultCloseOperation(EXIT_ON_CLOSE);

//добавим текстовое поле

add(new JScrollPane(out = new JTextArea())); // и кнопку

JButton button = new JButton("Источник событий"); add(button, "South");

//регистрируем нащего слушателя

OurListener ol = new OurListener(); button.addKeyListener(ol); button.addMouseListener(ol); button.addMouseMotionListener(ol); button.addMouseWheelListener(ol); button.addFocusListener(ol);

//выводим окно на экран setSize(400, 300); setVisible(true);

}

// внутренний класс - слушатель событий

class OurListener implements MouseListener, KeyListener, MouseMotionListener, MouseWheelListener,

FocusListener {

public void mouseClicked(MouseEvent e)

{out.append(e.toString() + "\n"); } public void mousePressed(MouseEvent e)

{out.append(e.toString() + "\n"); } public void mouseReleased(MouseEvent e)

{out.append(e.toString() + "\n"); } public void mouseEntered(MouseEvent e)

{out.append(e.toString() + "\n"); } public void mouseExited(MouseEvent e)

{out.append(e.toString() + "\n"); } public void keyTyped(KeyEvent e)

{out.append(e.toString() + "\n"); } public void keyPressed(KeyEvent e)

36

ГЛАВА 2

{out.append(e.toString() + "\n"); } public void keyReleased(KeyEvent e)

{out.append(e.toString() + "\n"); } public void mouseDragged(MouseEvent e)

{out.append(e.toString() + "\n"); } public void mouseMoved(MouseEvent e)

{out.append(e.toString() + "\n"); } public void focusGained(FocusEvent e)

{out.append(e.toString() + "\n"); } public void focusLost(FocusEvent e)

{out.append(e.toString() + "\n"); }

public void mouseWheelMoved(MouseWheelEvent e) { out.append(e.toString() + "\n"); }

}

public static void main(String[] args) { SwingUtilities.invokeLater(

new Runnable() {

public void run() { new LowLevelEvents(); } });

}

}

Вэтом примере мы создаем окно, добавляем в центр текстовое поле (помещенное

впанель прокрутки JScrollPane, подробнее о ней мы узнаем в главе 13), а в нижнюю часть

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

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

После того как вы насладитесь этим примером, мы познакомимся с самыми важными высокоуровневыми событиями. Они обрабатываются точно так же, как низкоуровневые (помните, разделение событий на две категории условно, их обработка ничем не отличается, разными являются лишь источники этих событий). Высокоуровневые события создаются (запускаются) самими компонентами и обозначают наиболее важные изменения в этих компонентах. У каждого компонента чаще всего имеются собственные события (у кнопки — это нажатие, у флажка — установка, у списка — выбор нового элемента), поэтому высокоуровневых событий очень много. Рассматривать их в отдельности от компонентов не имеет смысла, так что каждый раз при знакомстве с новым компонентом мы будем говорить и о том, какие события могут в нем возникать.

Однако есть несколько высокоуровневых событий, которые настолько часто используются в компонентах, что мы можем рассмотреть их сейчас (табл. 2.2). Это, прежде всего, события, которые позволяют компонентам сообщать об изменениях своих свойств.

Модель событий

 

37

Таблица 2.2. Наиболее часто используемые высокоуровневые события

 

 

 

Краткое описание события

Методы слушателя

Источник события

Событие PropertyChange-

propertyChange

Практически все гра-

Event. Обеспечивает работу

(PropertyChangeEvent)

фические компоненты

механизма привязанных

 

JavaBeans (в том числе

свойств JavaBeans

 

все компоненты Swing)

Событие ChangeEvent. За-

stateChanged(ChangeEvent)

Некоторые компоненты

пускается некоторыми ком-

 

Swing. Многие модели

понентами и моделями для со-

 

используют это событие

общения о своих изменениях

 

для связи с UI-представи-

 

 

телями

СобытиеActionEvent. Сообщает

actionPerformed(ActionEvent)

Компоненты Swing, у ко-

о действии над компонентом

 

торых есть какое-то «глав-

 

 

ное» действие (например,

 

 

у кнопки — нажатие)

Описанные в таблице события PropertyChangeEvent и ChangeEvent в обычных программах почти не используются, однако именно с их помощью происходит взаимодействие между компонентами, их UI-представителями и моделями. Событие PropertyChangeEvent — это основополагающее событие архитектуры JavaBeans, оно позволяет следить за тем, как и какие свойства меняются в компоненте (так называемые привязанные свойства). Мы уже упоминали в главе 1, что все свойства компонентов Swing являются привязанными. Это не только позволяет использовать их в визуальных средствах, но и дает возможность моделям и UI-представителям эффективно взаимодействовать друг с другом. Более того, иногда и в стандартных программах только такие события позволяют отследить изменение некоторых свойств компонентов, когда отдельные события не предусмотрены. Событие ChangeEvent — это более простое событие, которое также дает знать об изменениях состояния компонента или модели. Довольно часто модели запускают это событие, сообщая об изменениях в данных.

В таблицу наиболее часто используемых событий также попало событие ActionEvent. Оно на самом деле встречается практически в каждом приложении, в основном благодаря тому, что почти в каждом приложении есть кнопки и/или меню. Впрочем, это событие возникает не только при щелчке на кнопке, оно часто используется и тогда, когда нужно сообщить о каком-то важном действии (выбор элемента в раскрывающемся списке, конец ввода в текстовом поле, срабатывание таймера и т. п.)

Содержимое таблиц 2.1 и 2.2 не нужно запоминать, но эта информация может пригодиться, если вам понадобится обработать событие, которое прежде обрабатывать не приходилось. Тогда вы сможете заглянуть в таблицы и посмотреть, какой слушатель требуется для обработки этого события.

Техника написания слушателей

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

38

ГЛАВА 2

Адаптеры

Если вы посмотрите на интерфейсы некоторых слушателей, то обнаружите в них не один, а несколько методов. В некоторых слушателях методов довольно много (например, в слушателе оконных событий WindowListener). Это вполне логично — каждый метод отражает свое небольшое изменение в компоненте. Создание для каждого простого события отдельного слушателя привело бы к появлению невообразимого количества интерфейсов с расплывчатым предназначением, а обработка сложного события в одном методе неудобна — придется предварительно выяснять, что же именно произошло.

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

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

//Adapters.java

//Использование адаптеров вместо интерфейсов import javax.swing.*;

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

public class Adapters extends JFrame { public Adapters() {

super("Adapters");

//при закрытии окна - выход setDefaultCloseOperation(EXIT_ON_CLOSE);

//регистрируем слушателя addMouseListener(new MouseL());

//выводим окно на экран

setSize(200, 200); setVisible(true);

}

// наследуем от адаптера

class MouseL extends MouseAdapter { // следим за щелчками мыши в окне

@Override

public void mouseClicked(MouseEvent e) {

Модель событий

39

System.out.println(e);

}

}

public static void main(String[] args) { SwingUtilities.invokeLater(

new Runnable() {

public void run() { new Adapters(); } });

}

}

В примере создается небольшое окно, к которому добавляется слушатель событий от мыши MouseListener. Однако реализовывать интерфейс слушателя мы не стали, а создали новый внутренний класс MouseL, унаследованный от класса адаптера MouseAdapter. Вместо того чтобы определять все методы слушателя (а их ни много, ни мало — целых пять, можете убедиться в этом, заглянув в табл. 2.1), мы переопределили только один. Здесь нас интересовало только, когда пользователь щелкает кнопками мыши в окне, так что нам пригодился метод mouseClicked(). Остальные четыре метода остались в стороне, как будто их и не было.

Для всех низкоуровневых событий из пакета java.awt.event, слушатели которых состоят более чем из одного метода, имеются адаптеры. Чаще всего они и используются при обработке событий, и только в тех редких случаях, когда программу интересуют все события определенного рода, задействуются интерфейсы. Узнать название класса адаптера очень просто: если слушатель называется XXXListener, то имя адаптера выглядит как XXXAdapter4. Но вот для высокоуровневых событий компонентов Swing имеются только слушатели, адаптеров для них вы не найдете. Видимо, разработчики решили, что если хоть какое-то событие от компонента обрабатывается, то информация о нем нужна полная. Довольно странное решение.

Единственный недостаток адаптеров — меньший контроль со стороны компилятора и отсюда большая вероятность возникновения ошибки при работе с ними. Если методы интерфейса должны быть определены (иначе программа не будет компилироваться), то при наследовании от класса адаптера никаких ограничений нет, например, в только что рассмотренном примере в классе MouseL можно было определить следующий метод:

MouseClicked(MouseEvent e)

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

аона оказывается скрытой в названии метода — первая буква оказалась прописной,

анадо было писать строчную. До появления в Java аннотаций это могло быть реальной проблемой, однако аннотация @Override прекрасно справляется с задачей, не давая ко-

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

Каждому событию — по слушателю

Хорошо известно, что программа читается гораздо чаще, чем пишется. Процесс поддержки программ вообще занимает львиную долю времени разработки, поэтому удобочитаемость кода очень важна. А одним из важнейших условий удобочитае-

4 Это похоже на схему именования, но на самом деле адаптеры не являются частью спецификации JavaBeans, это просто удобный способ сократить объем ненужного кода. У слушателя с несколькими методами вполне может не быть адаптера.

Источник: https://studfile.net/preview/15935977/