logo
Методичка Java

Анонимные (anonimous) классы и слушатели событий (listeners)

Событие в Java (будем называть его программным событием, или, сокращённо, просто событием) – это объект, возникающий при наступлении какого-либо события в реальном мире при взаимодействии с ним компьютера (будем называть его физическим событием). Например, физическим событием может быть нажатие на клавишу клавиатуры. При наступлении некоторых физических событий возникают программные события – создаются объекты, имеющие тип, зависящий от того, какое событие наступило. Обработчики событий – подпрограммы, которые выполняют некоторый код при наступлении программного события. Например, код, который будет выполнен при нажатии пользователем на кнопку jButton1 во время работы приложения.

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

Классы и интерфейсы для работы с событиями заданы в пакетах java.awt, java.awt.event и javax.swing.event.

Важнейшие типы событий:

В пакете java.awt:

java.awt.AWTEvent – абстрактный класс, прародительский для всех классов событий.

В пакете java.awt.event:

ActionEvent – событие действия (как правило, нажатие).

AdjustmentEvent – изменение значения в линии прокрутки (для компонентов с линией прокрутки).

ComponentEvent – компонент переместился, изменил размер или видимость (visibility) -показался или был скрыт.

ContainerEvent – содержимое компонента-контейнера изменилось – какой-либо компонент был в него добавлен или из него убран.

FocusEvent – компонент получил или потерял фокус.

HierarchyEvent – изменение положения компонента в физической иерархии (иерархии агрегации). Например, удаление родительского компонента, смена компонентом родителя (перетаскивание с одного компонента на другой), и т.п.

InputEvent – произошло событие ввода. Базовый класс для классов событий ввода (KeyEvent, MouseEvent)

InputMethodEvent – произошло событие ввода. Содержит информацию об обрабатываемом тексте.

ItemEvent – событие, возникающее в случае, если пункт (item) был отмечен (selected) или с него была снята отметка (deselected).

KeyEvent – событие нажатия на клавишу.

MouseEvent – событие мыши.

PaintEvent – событие отрисовки. Служит для управления очередью событий и не может быть использовано для управления отрисовкой вместо методов paint или update.

TextEvent - событие, возникающее в случае, если текст в текстовом компоненте изменился.

WindowEvent – окно изменило статус (открылось, закрылось, максимизировалось, минимизировалось, получило фокус, потеряло фокус).

Также имеется большое количество событий в пакете javax.swing.event.

Для того, чтобы программа могла обработать событие какого-то типа, в приложение требуется добавить объект event listener (“слушатель события”) соответствующего типа. Этот тип - класс, который должен реализовать интерфейс слушателя, являющийся наследником интерфейса java.util.EventListener. Имя интерфейса слушателя обычно складывается из имени события и слова Listener.

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

ActionEvent – ActionListener.

AdjustmentEvent – AdjustmentListener.

ComponentEvent – ComponentListener - ComponentAdapter.

ContainerEvent – ContainerListener - ContainerAdapter.

FocusEvent – FocusListener - FocusAdapter.

HierarchyEvent – HierarchyBoundsListener - HierarchyBoundsAdapter.

InputEvent – нет интерфейсов и адаптеров.

InputMethodEvent – InputMethodListener.

ItemEvent – ItemListener.

KeyEvent – KeyListener - KeyAdapter.

MouseEvent - MouseListener - MouseAdapter.

- MouseMotionListener - MouseMotionAdapter. По-английски motion – “движение”. Событие возникает при движении мыши.

- MouseWheelListener-MouseWheelAdapter. По-английски wheel – “колесо”. Событие возникает при прокручивании колёсика мыши.

PaintEvent – нет интерфейсов и адаптеров.

TextEvent – TextListener.

WindowEvent - WindowListener - WindowAdapter.

- WindowFocusListener. Событие возникает при получении или потере окном фокуса.

- WindowStateListener. Событие возникает при изменении состояния окна.

Все компоненты Swing являются потомками javax.swing.JComponent. А в этом классе заданы методы добавления к компоненту многих из упомянутых слушателей:

addComponentListener, addFocusListener и т.д. В классах компонентов, обладающих специфическими событиями, заданы методы добавления слушателей этих событий.

Повторим теперь код, приведённый в предыдущем параграфе, с разъяснениями:

addMouseMotionListener(

new java.awt.event.MouseMotionAdapter(){

public void mouseDragged(java.awt.event.MouseEvent e){

System.out.println("Mouse dragged at: x="+

e.getX()+" y="+e.getY()

);

}

}

);

В качестве параметра метода addMouseMotionListener выступает анонимный класс типа java.awt.event.MouseMotionAdapter, переопределяющий метод mouseDragged.

В интерфейсе MouseMotionListener имеется два метода:

mouseDragged(MouseEvent e);

mouseMoved(MouseEvent e)

Поскольку мы не переопределили заглушку “ mouseMoved ”, наш объект-обработчик событий типа MouseEvent (движение мыши порождает события именно такого типа) не будет ничего делать для обычного движения. А вот метод mouseMoved в нашем объекте-обработчике переопределён, поэтому при перетаскиваниях (когда идёт движение мыши с нажатой кнопкой) будет выводиться текст в консольное окно.

Допустим, мы поместили код с добавлением слушателя в обработчик события нажатия на какую-либо кнопку (например, jButton1). После срабатывания обработчика наш компонент (главная форма приложения, если мы добавили слушателя ей), станет обрабатывать события типа MouseMotion. При этом на каждое такое событие в консольное окно будет осуществляться вывод позиции мыши, но только в том случае, если идёт перетаскивание – когда в пределах формы мы нажали кнопку мыши и не отпуская её перетаскиваем. Причём неважно, какая это кнопка (их можно при необходимости программно различить).

Если мы расположим на форме панель jPanel1, и заменим вызов addMouseMotionListener (реально это вызов this.addMouseMotionListener) на jPanel1.addMouseMotionListener – слушатель расположится не в форме (объект this), а в панели. В этом случае события перетаскивания будут перехватываться только панелью. При этом важно, в какой области началось перетаскивание, но не важно, в какой области оно продолжается – события от перетаскивания, начавшегося внутри панели, будут возникать даже при перемещении курсора мыши за пределы окна приложения.

Если нажать на кнопку добавления слушателя два раза – в списке слушателей станет два объекта-обработчика событий MouseEvent. Объекты из списка слушателей обрабатывают события по очереди, в порядке их добавления. Поэтому каждое событие будет обрабатываться дважды. Если добавить ещё объекты слушателей - будет обрабатываться соответствующее число раз. Конечно, в реальных ситуациях если добавляют более одного объекта-слушателя для события, они не повторяют одни и те же действия, а по-разному реагируют на это событие. Либо они являются экземплярами одного класса, но с разными значениями полей данных, либо, что чаще – экземплярами разных классов. Например, для события MouseEvent существует интерфейc MouseListener и реализующий его адаптер MouseAdapter, имеющий четыре метода:

mouseClicked(MouseEvent e)

mouseEntered(MouseEvent e)

mouseExited(MouseEvent e)

mouseReleased(MouseEvent e)

Экземпляры классов-наследников MouseAdapter будут совсем по-другому обрабатывать события MouseEvent. Аналогично, можно создать экземпляр наследника MouseMotionListener, который будет реагировать не на перетаскивание, а на простое движение мыши.

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