Мы уже несколько раз сталкивались со слушателями событий и обработчиками событий.
В отличие от процедурных программ, для программ с графическим интерфейсом пользователя требуется управляемая событиями модель, в которой низлежащее окружение сообщает вашей программе, когда что-то происходит.
Например, когда пользователь нажимает на мышь, низлежащая среда выполнения генерирует событие, которое оно отправляет в программу.
Затем программа должна выяснить, что означает щелчок мыши и действовать соответствующим образом.
Процедурное программирование, которое рассматривается как традиционное программирование, определяет процесс программирования как разработку процедур, которые прямо управляют потоком данных и элементами контроля.
Программирование, управляемое событиями, определяет процесс программирования как разработку процедур, которые реагируют на поток данных и элементы контроля в соответствии с действиями пользователя, программы или операционной системы.
Эти модели программирования отличаются потоком выполнения и структурой.
В AWT существуют две разные модели событий или способы обработки событий.
В Java 1.0 и ранее события отправлялись непосредственно соответствующим компонентам.
Сами события были инкапсулированы в один класс Event.
Для обработки таких событий нужно было переопределить метод action или handleEvent компонента, который вызывался при получении события компонентом.
Таким образом, в этой ранней модели обработчики событий, такие как action и handleEvent, были реализованы классом Component.
И версии этих методов по умолчанию ничего не делали и возвращали false.
Эта модель событий была заменена моделью Java 1.1.
Java 1.1 реализует модель делегирования, в которой события передаются только объектам, зарегистрированным для получения события.
Эта модель позволяет любому объекту получать события, генерируемые компонентом.
И это означает, что вы можете отделить сам пользовательский интерфейс от кода обработки событий, в отличие от ранней модели, где вы должны были для обработки события переопределять метод самого компонента пользовательского интерфейса.
В модели событий Java 1.1 вся функциональность обработки событий содержится в пакете java.awt. event.
Внутри этого пакета подклассы абстрактного класса AWTEvent представляют собой различные виды событий.
Класс AWTEvent и его подклассы заменяют Event предыдущей модели событий.
Пакет также включает в себя ряд интерфейсов EventListener, которые реализуются классами, которые хотят получать различные виды событий.
Эти интерфейсы определяют методы, вызываемые при возникновении событий соответствующего типа.
Пакет также содержит ряд классов адаптеров.
Они реализуют интерфейсы EventListener и являются абстрактными классами, которые предоставляют нулевые реализации методов соответствующего прослушивателя.
Эти классы удобны для создания объектов-слушателей.
Вместо того, чтобы объявлять, что ваш класс реализует определенный интерфейс EventListener, вы можете объявить, что ваш класс расширяет соответствующий адаптер.
Эта модель требует, чтобы объекты регистрировались для получения событий.
Затем, когда происходит событие, об этом уведомляются только те объекты, которые были зарегистрированы.
Эта модель называется «делегирование».
Она реализует шаблон проектирования Observer.
Таким образом, эта модель обеспечивает четкое разделение между компонентами GUI и обработкой событий.
Важно, чтобы любой объект, а не только компонент, мог получать события.
Таким образом, вы можете отделить свой код обработки событий от вашего графического интерфейса.
Один набор классов может реализовать пользовательский интерфейс, а другой набор классов может реагировать на события, генерируемые интерфейсом.
Это означает, что, если вы разработали хороший интерфейс, вы можете повторно использовать его в разных приложениях, изменив обработку событий.
Модель делегирования важна для JavaBeans, которые позволяют взаимодействовать между Java и другими платформами.
Чтобы обеспечить такое взаимодействие, необходимо было отделить источник события от получателя.
В модели делегирования события могут передаваться нескольким получателям; любое количество классов может быть зарегистрировано для получения события.
В старой модели обработчик событий мог заявить, что он полностью не обработал событие, передавая событие контейнеру, или обработчик событий мог сгенерировать новое событие и доставить его другому компоненту.
В любом случае разработчику приходилось планировать, как передавать события другим получателям.
В Java 1.1 это не требуется.
Событие будет передано каждому объекту, зарегистрированному в качестве слушателя для этого события, независимо от того, что другие объекты делают с событием.
Наконец, эта модель событий представляет идею очереди событий.
Вместо того, чтобы переопределять handleEvent для просмотра всех событий, вы можете заглянуть в очередь событий системы, используя класс EventQueue.
В Java 1.1 каждый компонент является источником события, который может генерировать определенные типы событий, которые являются подклассами класса AWTEvent.
Объекты, которые интересуются событием, называются слушателями.
Каждый тип события соответствует интерфейсу прослушивателя, который определяет методы, вызываемые при возникновении события.
Чтобы получить событие, объект должен реализовать соответствующий интерфейс слушателя и должен быть зарегистрирован в источнике события, путем вызова метода «add listener» компонента, который генерирует событие.
И мы это уже видели.
Здесь мы создаем кнопку и присоединяем к ней слушателя, как экземпляр анонимного класса, который реализует интерфейс слушателя.
В этом классе мы переопределяем обработчик событий, метод интерфейса слушателя.
Как только объект зарегистрирован, метод actionPerformed будет вызываться всякий раз, когда пользователь делает что-либо в компоненте, который генерирует событие действия.
В некотором роде метод actionPerformed очень похож на метод action старой модели событий, за исключением того, что он не привязан к иерархии Component, а он является частью интерфейса, который может быть реализован любым объектом, который заинтересован в получении событий.
Вместо реализации интерфейсов, можно расширять классы адаптеров, которые реализуют интерфейсы слушателей, переопределяя абстрактные методы адаптеров.
Некоторые интерфейсы слушателей предназначены для работы с несколькими типами событий.
Например, интерфейс MouseListener объявляет пять методов обработки различных типов событий мыши: мышь вниз, мышь вверх, щелчок, вход мыши в компонент и выход мыши.
Строго говоря, это означает, что объект, интересующийся событиями мыши, должен реализовывать MouseListener и поэтому должен переопределять все пять методов всех возможных действий мыши.
Это звучит как создание излишнего кода; большую часть времени вас интересует только одно или два из этих событий.
К счастью, вам этого делать не нужно.
Вы можете использовать класс адаптера, который обеспечивает нулевую реализацию всех методов интерфейса.
И если вы хотите написать класс обработки событий, который имеет дело только с щелчками мыши, вы можете объявить, что ваш класс расширяет MouseAdapter.
И ваша единственная задача программирования состоит в том, чтобы переопределить единственный метод, который вам нужен – это mouseClicked.
Таким образом, резюмируя.
Компоненты генерируют AWTEvents, когда что-то происходит.
Различные подклассы AWTEvent представляют различные типы событий.
Например, события мыши представлены классом MouseEvent.
И каждый компонент может генерировать определенные подклассы класса AWTEvent.
Обработчики событий регистрируются для приема событий с помощью метода «add listener» в компоненте, который генерирует событие.
Существуют различные методы «add listener» для каждого вида событий AWTEvent, которые может генерировать компонент.
Например, чтобы заявить о своем интересе к событию мыши, вы вызываете метод addMouseListener компонента.
Каждый тип события имеет соответствующий интерфейс прослушивателя, который определяет методы, вызываемые при возникновении этого события.
Чтобы иметь возможность принимать события, обработчик событий должен реализовать соответствующий интерфейс прослушивателя.
Например, MouseListener определяет методы, вызываемые при возникновении событий мыши.
Большинство типов событий также имеют класс адаптера.
Например, события MouseEvent имеют класс MouseAdapter.
Класс адаптера реализует соответствующий интерфейс прослушивателя, но обеспечивает реализацию заглушки каждого метода, т. е. метод просто возвращает без каких-либо действий.
Классы адаптеров используются, когда нужны только некоторые из методов в интерфейсе слушателя.
Например, вместо реализации всех пяти методов интерфейса MouseListener класс может расширить класс MouseAdapter и переопределить один или два метода, которые нужны.
Класс EventQueue позволяет напрямую управлять событиями Java 1.1.
Обычно вам не нужно самостоятельно управлять событиями; система сама заботится о доставке событий.
Однако, если вам нужно, вы можете получить очередь событий системы, вызвав Toolkit.getSystemEventQueue, затем вы можете заглянуть в очередь событий, вызвав peekEvent или опубликовать новые события, вызвав postEvent.
EventQueue – платформо-независимый класс, представляющий собой очередь событий, получаемых как из классов-помощников peer, которые организуют взаимодействие классов AWT с операционной системой, так и из классов приложения.
Данный класс обеспечивает диспетчеризацию событий, т.е. извлечение событий из очереди и отправки их вызовом внутреннего метода dispatchEvent (AWTEvent event), который в качестве параметра принимает объект класса AWTEvent, представляющий собой AWT события.
Таким образом, класс EventQueue обеспечивает последовательную обработку событий в порядке очереди.
Метод dispatchEvent класса EventQueue определяет, к какому графическому компоненту относится данное событие и производит вызов метода dispatchEvent соответствующего компонента.
Метод dispatchEvent наследуется каждым компонентом от базового класса java.awt.Component.
Далее событие передается из метода dispatchEvent методу processEvent (AWTEvent e), который в свою очередь передает событие методу process <event type> Event, определенному для каждого класса событий.
После этого метод process <event type> Event передает событие объекту интерфейса <event type> Listener, зарегистрированному соответствующим слушателем add <event type> Listener, где событие и обрабатывается методом, определенном в интерфейсе.
Объект класса EventQueue автоматически создается виртуальной машиной Java, когда приложением создается объект – наследник класса java.awt.Component.
При этом автоматически создается также объект класса java.awt. EventDispatchThread, который представляет собой поток, работающий параллельно основному потоку программы.
Данный поток собственно и осуществляет диспетчеризацию событий, хранящихся в очереди.
Методом pumpEvents класса EventDispatchThread события извлекаются из очереди и передаются методу dispatchEvent класса EventQueue.
Таким вот образом события передаются от источника слушателю.
Каждый пользовательский интерфейс состоит из трех основных аспектов.
Это элементы пользовательского интерфейса.
Они являются основными визуальными элементами, которые пользователь в конечном итоге видит и взаимодействует с ними.
Макеты или компоновки.
Они определяют, как элементы пользовательского интерфейса должны быть организованы на экране и обеспечивать окончательный внешний вид графического интерфейса пользователя.
И поведение.
Это события, которые происходят, когда пользователь взаимодействует с элементами пользовательского интерфейса.
Элементы управления, с помощью которых создается AWT графический интерфейс, наследуют от класса Component.
Класс Button представляет кнопку, элемент управления, который имеет метку и генерирует событие при нажатии.
Когда кнопка нажата и отпущена, AWT отправляет экземпляр ActionEvent события к кнопке, вызывая метод processEvent кнопки.
Метод processEvent кнопки получает все события для кнопки, и он передает событие, вызывая собственный метод processActionEvent.
Этот метод передает событие любому слушателю, который зарегистрировал свой интерес к событиям, сгенерированным этой кнопкой.
Если приложение хочет выполнить какое-либо действие на основе нажатия и отпускания кнопки, оно должно реализовать интерфейс ActionListener и зарегистрировать нового слушателя для приема событий этой кнопки, с помощью метода addActionListener кнопки.
Элемент Сheckbox используется для включения опции (true) или ее выключения (false).
Для каждого флажка есть метка, обозначающая, что делает флажок.
И состояние флажка можно изменить, щелкнув по нему.
Объект флажка создается с помощью конструктора, которому передается метка флажка.
Состояние флажка, выбран он или нет, устанавливается с помощью метода setState, или сразу указав в конструкторе состояние флажка.
Здесь используется слушатель ItemListener, а не ActionListener.
Он слушает изменение состояния компонента, а не действия, предоставляя метод itemStateChanged.
Превратить флажок в радио кнопку, можно создав группу флажков.
При создании каждого флажка, эта группа передается в конструктор, тем самым добавляя флажок в группу флажков.
Радио кнопка отличается от флажка тем, что одновременно может быть выбрана только одна радио кнопка, а флажки могут быть отмечены сразу несколько.
Класс CheckboxGroup имеет метод getSelectedCheckbox, который возвращает выбранную радио кнопку.
Компонент выбора Choice используется для отображения всплывающего меню выбора.
Выбранный элемент отображается в верхней части меню.
Класс Choice имеет метод getSelectedItem, который возвращает выбранный элемент в виде строки.
Label – это пассивный элемент управления, так как он не создает никакого события пользователя.
Метка просто отображает одну строку текста, доступную только для чтения.
Текст метки может быть изменен программным способом, но никак не может быть изменен конечным пользователем.
Список List представляет собой список текстовых элементов.
Список может быть настроен так, что пользователь может выбрать один или несколько элементов, с помощью второго аргумента конструктора.
В этом отличие списка от выбора Choice.
Также List – это статический список, а не выпадающий список выбора, как Choice.
Компоненты List, TextArea и ScrollPane поставляются с готовыми полосами прокрутки.
Однако, если вы хотите прокрутить любой другой объект, вам придется использовать полосу прокрутки Scrollbar.
Полосы прокрутки в большинстве случаев используются для перемещения видимой области.
Они также могут использоваться для установки значения между двумя числами.
Или они могут использоваться для пролистывания нескольких экранов.
В этом примере мы создаем пользовательский компонент, который расширяет панель и реализует интерфейс AdjustmentListener, для получения событий прокрутки Scrollbar.
При создании пользовательского компонента мы должны расширить класс Component или один из его подклассов.
Также мы должны определить методы расчета размеров компонента и метод paint отрисовки компонента.
В конструкторе этого класса мы создаем горизонтальную и вертикальную полосы прокрутки.
Устанавливаем их размеры на основе размеров пользовательского компонента.
И добавляем эти полосы прокрутки в пользовательский компонент.
Также мы определяем, что данный пользовательский компонент является слушателем событий прокрутки полос.
Полоса прокрутки генерирует событие настройки, когда изменяется значение полосы прокрутки.
Для обработки этого события определяется метод adjustValueChanged интерфейса AdjustmentListener.
В этом методе мы получаем значения полос прокрутки и перерисовываем наш компонент.
В методе paint мы рисуем круг с диаметром, на основе значений полос прокрутки.
Таким образом, перемещая ползунки полос прокрутки, мы изменяем диаметр круга.
Элемент управления TextArea в AWT предоставляет многострочную область редактора.
Пользователь может вводить здесь столько, сколько он хочет.
Когда текст в текстовой области становится больше, чем область просмотра, автоматически появляется полоса прокрутки, которая позволяет прокручивать текст вверх, вниз, вправо и влево.
При создании компонента TextArea указывается количество строк и столбцов видимой области текста.
Компонент TextField позволяет пользователю редактировать одну строку текста.
Когда пользователь вводит символ в текстовое поле, в поле отправляется событие нажатия клавиши.
Событие нажатия клавиши передается зарегистрированному слушателю KeyListener.
Также можно обрабатывать событие ActionEvent, которое запускается нажатием клавиши enter.
В этом примере, после ввода пароля и нажатия клавиши enter, текст одного и другого поля будут выведены в метку.
При создании поля можно указать количество видимых символов.
Метод setEchoCharacter указывает символ, который будет отображаться вместо символов, вводимых пользователем.
Этот метод используется для ввода паролей.
Обычно, окно верхнего уровня имеет связанную с ним панель меню.
Эта панель меню состоит из различных вариантов меню, доступных конечному пользователю.
Для создания меню, пакет java.awt предоставляет четыре основных класса – MenuBar, Menu, MenuItem и CheckboxMenuItem.
Все эти четыре класса не являются компонентами AWT, поскольку они не являются подклассами класса Component.
На самом деле, они являются подклассами класса MenuComponent, который никак не связан в иерархии с классом Component.
Панель меню MenuBar содержит само меню.
MenuBar добавляется к Frame с помощью метода setMenuBar.
По умолчанию, панель меню добавляется сверху окна Frame.
MenuBar не может быть добавлена к другим сторонам окна.
Меню Menu содержит пункты меню.
Меню добавляется в панель меню с помощью метода add.
В меню можно добавить подменю.
Элемент MenuItem отображает опцию, которую может выбрать пользователь.
Элементы меню добавляются в меню с помощью метода addMenuItem.
Между пунктами меню может быть добавлен разделитель с помощью метода addSeparator.
Элемент CheckboxMenuItem отличается от элемента MenuItem тем, что он отображается вместе с флажком.
В этом примере сначала мы создаем панель меню.
Затем создаем меню.
Далее создаем элементы меню и присоединяем к ним слушателей событий.
Метод setActionCommand устанавливает имя команды для события действия, которое генерируется этим пунктом меню.
По умолчанию для команды действия, имя устанавливается как метка элемента меню.
Затем элемент меню добавляется в меню.
Меню добавляется в панель меню.
А панель меню устанавливается для окна верхнего уровня.
Всплывающее меню PopupMenu представляет собой меню, которое можно динамически показывать в указанной позиции внутри компонента.
Для этого класс PopupMenu предоставляет метод show, который в качестве аргумента получает компонент, в котором отображается всплывающее меню, и координаты позиции для отображения меню.
В этом примере мы создаем всплывающее меню.
И в слушателе клика мыши панели, показываем всплывающее меню.
Компонент Canvas представляет собой прямоугольную область, где приложение может рисовать что-либо, или может служить основой для пользовательского компонента, который не содержит другой компонент, например, кнопка с изображением.
Для использования Canvas создается класс, расширяющий Canvas, и переопределяется метод paint, отвечающий за отрисовку холста.
Этот метод автоматически вызывается средой выполнения при создании объекта класса, и получает в качестве аргумента объект Graphics.
Класс Graphics обеспечивает основу для всех графических операций в AWT.
Он играет две разные, но связанные роли.
Во-первых, это графический контекст.
Графический контекст – это информация, которая влияет на операции рисования.
Эта информация включает в себя цвета фона и переднего плана, шрифт, а также расположение и размеры отсекающего прямоугольника – область компонента, в котором можно рисовать графику.
Во-вторых, класс Graphics предоставляет методы для рисования простых геометрических фигур, текста и изображений.
Так как класс Graphics является абстрактным базовым классом, он не может быть создан непосредственно.
Экземпляр Graphics создается при создании компонента и передается в качестве аргумента методам update и paint компонента.
При использовании класса Graphics, фактическая работа выполняется конкретными его реализациями, которые тесно связаны с конкретной платформой.
Виртуальная машина Java предоставляет необходимые конкретные классы для вашей среды.
Вам не нужно беспокоиться о классах, специфичных для платформы; как только у вас есть объект Graphics, вы можете вызывать все методы класса Graphics, и быть уверенными, что классы, специфичные для платформы, будут работать правильно, где бы ни работала ваша программа.
Класс Graphics позволяет нарисовать линию.
Закрасить цветом овал и прямоугольник.
Нарисовать строку текста и другое.
Также можно нарисовать изображение, которое можно получить с помощью инструмента Toolkit, который обеспечивает связь с нативной платформой.
Здесь вызов метода drawImage запускает новый поток, который загружает запрошенное изображение.
Последний аргумент метода this – это наблюдатель изображения, который отслеживает процесс загрузки изображения.
Поток, который загружает изображение, уведомляет наблюдателя изображения, когда появляются новые данные.
Класс Component реализует интерфейс ImageObserver, поэтому можно использовать this в качестве наблюдателя изображения при вызове метода drawImage.
Объект Toolkit представляет собой абстрактный класс, который предоставляет интерфейс для получения специфических для платформы деталей, таких как размер окна, доступные шрифты и печать.
Каждая платформа, поддерживающая Java, должна предоставить конкретный класс, который расширяет класс Toolkit.
Вы можете использовать объект Toolkit, если вам нужно получить изображение в приложении, получить информацию о шрифтах, получить цветовую модель, получить параметры экрана и так далее.