Spec-Zone .ru
спецификации, руководства, описания, API
|
Этот раздел обсуждает несколько конструктивных соображений, чтобы иметь в виду, реализовывая обработчики событий в Вашем приложении. Мы тогда представляем Вас объектам-событиям маленькие объекты, которые описывают каждое событие. В частности мы говорим о EventObject
, суперкласс для всего AWT и событий Swing. Затем, мы представляем понятие низкоуровневых событий и семантических событий, рекомендуя, чтобы Вы предпочли семантические события когда возможный. Остаток от этого раздела обсуждает методы реализации, которые Вы могли бы использовать в некоторых слушателях события или видеть в слушателях события, создаваемых другими людьми или разработчиками GUI.
Самое важное правило иметь в виду о слушателях события состоит в том, что они должны выполниться очень быстро. Поскольку весь рисунок и слушающие событие методы выполняются в том же самом потоке, медленный метод слушателя события может заставить программу казаться безразличной и медленной, чтобы перекрасить себя. Если Вы должны выполнить некоторую долгую работу как результат события, сделайте это, запуская другой поток (или так или иначе отправляя запрос другому потоку), чтобы выполнить работу. Для справки при использовании потоков см. Параллелизм в Swing.
У Вас есть много вариантов на том, как реализовать слушателя события. Мы не можем рекомендовать определенный подход, потому что одно решение не удовлетворило бы всем ситуациям. Однако, мы можем дать Вам некоторые подсказки и показать Вам некоторые методы, что Вы могли бы видеть, даже если Вы не используете того же самого решения в своей программе.
Например, Вы могли бы хотеть реализовывать отдельные классы для различных видов слушателей события. Это может быть легкой архитектурой, чтобы поддержать, но много классов могут также означать уменьшенную производительность.
Разрабатывая Вашу программу, Вы могли бы хотеть реализовать своих слушателей события в class, который не общедоступен, но где-нибудь более скрыт. Частная реализация является более безопасной реализацией.
Если у Вас есть очень определенный вид простого слушателя события, Вы могли бы быть в состоянии избежать создавать class вообще при использовании EventHandler
class.
У каждого метода слушателя события есть единственный параметр объект, который наследовался от EventObject
class. Хотя параметр всегда убывает от EventObject
, его тип обычно определяется более точно. Например, параметр за методы, что события от нажатия мыши дескриптора являются экземпляром MouseEvent
, где MouseEvent
косвенный подкласс EventObject
.
EventObject
class определяет один очень полезный метод:
Object getSource()
Отметьте что getSource
метод возвращается Object
. Классы событий иногда определяют методы, подобные getSource
, но это более ограничило типы возврата. Например, ComponentEvent
class определяет a getComponent
метод это точно так же как getSource
возвращает объект, который запустил событие. Различие - это getComponent
всегда возвраты a Component
. Каждая страница с практическими рекомендациями для слушателей события упоминает, следует ли использовать getSource
или другой метод, чтобы получить источник события.
Часто, событие class определяет методы, которые возвращают информацию о событии. Например, можно запросить a MouseEvent
объект для информации о том, где событие имело место, сколько щелчков сделанный пользователь, какие модифицирующие клавиши были нажаты и так далее.
События могут быть разделены на две группы: низкоуровневые события и семантические события. Низкоуровневые события представляют возникновения оконной системы или низкоуровневый ввод. Все остальное - семантическое событие.
Примеры низкоуровневых событий включают события от нажатия мыши и ключевые события, оба из которых происходят непосредственно от ввода данных пользователем. Примеры семантических событий включают события элемента и действие. Семантическое событие могло бы быть инициировано вводом данных пользователем; например, кнопка обычно запускает событие действия, когда пользователь щелкает по ней, и текстовое поле запускает событие действия, когда пользователь нажимает, Входят. Однако, некоторые семантические события не инициированы низкоуровневыми событиями, вообще. Например, событие табличной модели могло бы быть запущено, когда табличная модель получает новые данные от базы данных.
Когда бы ни было возможно следует прислушаться к семантическим событиям, а не низкоуровневым событиям. Тем путем можно сделать свой код столь же устойчивым и переносимым насколько возможно. Например, прислушивание к событиям действия на кнопках, а не событиям от нажатия мыши, означает, что кнопка будет реагировать соответственно, когда пользователь попытается активировать кнопку, используя альтернативу клавиатуры или стиль определенный жест. Имея дело с составным компонентом, таким как поле комбинированного списка, обязательно, чтобы Вы придерживались семантических событий, так как у Вас нет никакого надежного способа зарегистрировать слушателей на всем стили определенные компоненты, которые могли бы использоваться, чтобы сформировать составной компонент.
Некоторые интерфейсы слушателя содержат больше чем один метод. Например, MouseListener
интерфейс содержит пять методов: mousePressed
, mouseReleased
, mouseEntered
, mouseExited
, и mouseClicked
. Даже если Вы заботитесь только о щелчках мышью, если Ваш class непосредственно реализует MouseListener
, тогда следует реализовать все пять MouseListener
методы. У методов для тех событий, о которых Вы не заботитесь, могут быть пустые тела. Вот пример:
//An example that implements a listener interface directly. public class MyClass implements MouseListener { ... someObject.addMouseListener(this); ... /* Empty method definition. */ public void mousePressed(MouseEvent e) { } /* Empty method definition. */ public void mouseReleased(MouseEvent e) { } /* Empty method definition. */ public void mouseEntered(MouseEvent e) { } /* Empty method definition. */ public void mouseExited(MouseEvent e) { } public void mouseClicked(MouseEvent e) { ...//Event listener implementation goes here... } }
Получающийся набор пустых тел метода может сделать код тяжелее, чтобы считать и поддержать. Чтобы помочь Вам избежать реализовывать пустые тела метода, API обычно включает адаптер class для каждого интерфейса слушателя больше чем с одним методом. (Таблица API Слушателя приводит всех слушателей и их адаптеры.) Например, MouseAdapter
class реализует MouseListener
интерфейс. Адаптер class реализует пустые версии методов всего своего интерфейса.
Чтобы использовать адаптер, Вы создаете подкласс этого и переопределяете только методы интереса, вместо того, чтобы непосредственно реализовать все методы интерфейса слушателя. Вот пример изменения предыдущего кода, чтобы расшириться MouseAdapter
. Расширяясь MouseAdapter
, это наследовало пустые определения всех пяти из методов это MouseListener
содержит.
/* * An example of extending an adapter class instead of * directly implementing a listener interface. */ public class MyClass extends MouseAdapter { ... someObject.addMouseListener(this); ... public void mouseClicked(MouseEvent e) { ...//Event listener implementation goes here... } }
Что, если Вы хотите использовать адаптер class, но не хотеть, чтобы Ваш общедоступный class наследовал от адаптера class? Например, предположите, что Вы пишете апплет, и Вы хотите Ваш Applet
подкласс, чтобы содержать некоторый код, чтобы обработать события от нажатия мыши. Так как язык Java не разрешает множественное наследование, Ваш class не может расширить обоих Applet
и MouseAdapter
классы. Решение состоит в том, чтобы определить внутренний class class в Вашем Applet
подкласс, который расширяется MouseAdapter
class.
Внутренние классы могут также быть полезными для слушателей события, которые реализуют один или более интерфейсов непосредственно.
//An example of using an inner class. public class MyClass extends Applet { ... someObject.addMouseListener(new MyAdapter()); ... class MyAdapter extends MouseAdapter { public void mouseClicked(MouseEvent e) { ...//Event listener implementation goes here... } } }
Рассматривая, использовать ли внутренний class, имейте в виду, что время запуска приложения и объем потребляемой памяти обычно прямо пропорциональны к числу классов, которые Вы загружаете. Чем больше классов, которые Вы создаете, тем дольше Ваша программа берет, чтобы запустить и больше памяти, которую потребуется. Как разработчик приложений необходимо сбалансировать это с других конструктивных ограничений, которые Вы можете иметь. Мы не предлагаем, чтобы Вы превратили свое приложение в единственный монолитный class в надежде на сокращение времени запуска и объема потребляемой памяти, это привело бы к ненужным головным болям и трудностям обслуживания.
Можно создать внутренний class, не определяя имя, это известно как анонимный внутренний class. В то время как это могло бы выглядеть странным на первый взгляд, анонимные внутренние классы могут сделать Ваш код легче читать, потому что class определяется, где на это ссылаются. Однако, Вы должны взвесить удобство против возможных импликаций производительности увеличивания числа классов.
Вот пример использования анонимного внутреннего class:
//An example of using an anonymous inner class. public class MyClass extends Applet { ... someObject.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { ...//Event listener implementation goes here... } }); ... } }
Один недостаток анонимных внутренних классов - то, что они не могут быть замечены долгосрочным механизмом персистентности. Для получения дополнительной информации см. документацию API для
Внутренние классы работают, даже если Ваш слушатель события нуждается в доступе к частным переменным экземпляра от включения class. Пока Вы не объявляете, что внутренний class static
, внутренний class может обратиться к переменным экземпляра и методам так же, как если бы его код находится в содержании class. Чтобы сделать локальную переменную доступной для внутреннего class, только сохраните копию переменной как a final
локальная переменная.
Чтобы обратиться к экземпляру включения, можно использовать EnclosingClass.this
. Для получения дополнительной информации о внутренних классах, см. Вложенные Классы.
EventHandler
class поддерживает динамическую генерацию простых, слушателей события с одним оператором. Хотя EventHandler
только полезно для определенного типа чрезвычайно простых слушателей события, он стоит упомянуть по двум причинам. Это полезно для:
Создание EventHandler
вручную является трудным. EventHandler
должен быть тщательно создан. Если бы Вы делаете ошибку, Вы не были бы уведомлены во время компиляции, она выдаст неясное исключение во времени выполнения. Поэтому EventHandler
s лучше всего создаются разработчиком GUI. EventHandler
s должен быть тщательно задокументирован. Иначе Вы рискуете производить твердый к чтению код.
EventHandler
class предназначается, чтобы использоваться интерактивными инструментами, такими как разработчики приложений, которые позволяют разработчикам делать соединения между бобами. Обычно соединения делаются из боба пользовательского интерфейса (источник события) к бобу логики приложения (цель). Самые эффективные соединения этого вида изолируют логику приложения от пользовательского интерфейса. Например, EventHandler
для соединения от JCheckBox до метода, который принимает, булево значение может иметь дело с извлечением состояния флажка и передачи этого непосредственно к методу так, чтобы метод был изолирован от уровня пользовательского интерфейса.
Внутренние классы - другой, более общий способ обработать события от пользовательских интерфейсов. EventHandler
class обрабатывает только подмножество того, что является возможными использующими внутренними классами. Однако, EventHandler
работы лучше с долгосрочной схемой персистентности чем внутренние классы. Кроме того, использование EventHandler
в больших приложениях, в которых тот же самый интерфейс реализуется много раз, может уменьшить диск и объем потребляемой памяти приложения.
Примеры Использования EventHandler
Самое простое использование EventHandler
должен установить слушателя, который вызывает метод на целевом объекте без параметров. В следующем примере мы создаем ActionListener, который вызывает toFront метод на экземпляр javax.swing.JFrame
.
myButton.addActionListener( (ActionListener)EventHandler.create(ActionListener.class, frame, "toFront"));
Когда myButton будет нажат, оператор frame.toFront () будет выполняться. Можно было получить тот же самый эффект, с некоторой дополнительной безопасностью типов времени компиляции, определяя новую реализацию интерфейса ActionListener и добавляя экземпляр этого к кнопке:
//Equivalent code using an inner class instead of EventHandler
.
myButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
frame.toFront();
}
});
Следующее самое простое использование EventHandler
должен извлечь значение свойства из первого параметра метода в интерфейсе слушателя (обычно объект-событие) и использовать это, чтобы установить значение свойства в целевом объекте. В следующем примере мы создаем ActionListener, который устанавливает nextFocusableComponent свойство цели (myButton) объект к значению "исходного" свойства события.
EventHandler.create(ActionListener.class, myButton, "nextFocusableComponent", "source")
Это соответствовало бы следующей внутренней реализации class:
//Equivalent code using an inner class instead of EventHandler
.
new ActionListener() {
public void actionPerformed(ActionEvent e) {
myButton.setNextFocusableComponent((Component)e.getSource());
}
}
Также возможно создать EventHandler
это только передает входящий объект-событие к действию цели. Если четвертое EventHandler.create
параметром является пустая строка, тогда событие было только проведено:
EventHandler.create(ActionListener.class, target, "doActionEvent", "")
Это соответствовало бы следующей внутренней реализации class:
//Equivalent code using an inner class instead of EventHandler
.
new ActionListener() {
public void actionPerformed(ActionEvent e) {
target.doActionEvent(e);
}
}
Вероятно, наиболее популярный способ использования EventHandler
должен извлечь значение свойства из источника объекта-события и установить это значение как значение свойства целевого объекта. В следующем примере мы создаем ActionListener, который устанавливает свойство "метки" целевого объекта к значению "текстового" свойства источника (значение "исходного" свойства) события.
EventHandler.create(ActionListener.class, myButton, "label", "source.text")
Это соответствовало бы следующей внутренней реализации class:
//Equivalent code using an inner class instead of EventHandler
.
new ActionListener {
public void actionPerformed(ActionEvent e) {
myButton.setLabel(((JTextField)e.getSource()).getText());
}
}