Spec-Zone .ru
спецификации, руководства, описания, API
След: Создание GUI С JFC/Swing
Урок: Пишущие Слушатели События
Общая информация о Пишущих Слушателях События
Домашняя страница > Создание GUI С JFC/Swing > Пишущие Слушатели События

Общая информация о Пишущих Слушателях События

Этот раздел обсуждает несколько конструктивных соображений, чтобы иметь в виду, реализовывая обработчики событий в Вашем приложении. Мы тогда представляем Вас объектам-событиям маленькие объекты, которые описывают каждое событие. В частности мы говорим о 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 для пакета JavaBeans™ и Бобового урока Персистентности в следе JavaBeans.


Внутренние классы работают, даже если Ваш слушатель события нуждается в доступе к частным переменным экземпляра от включения class. Пока Вы не объявляете, что внутренний class static, внутренний class может обратиться к переменным экземпляра и методам так же, как если бы его код находится в содержании class. Чтобы сделать локальную переменную доступной для внутреннего class, только сохраните копию переменной как a final локальная переменная.

Чтобы обратиться к экземпляру включения, можно использовать EnclosingClass.this. Для получения дополнительной информации о внутренних классах, см. Вложенные Классы.

Класс EventHandler

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

Создание EventHandler вручную является трудным. EventHandler должен быть тщательно создан. Если бы Вы делаете ошибку, Вы не были бы уведомлены во время компиляции, она выдаст неясное исключение во времени выполнения. Поэтому EventHandlers лучше всего создаются разработчиком GUI. EventHandlers должен быть тщательно задокументирован. Иначе Вы рискуете производить твердый к чтению код.

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

Проблемы с примерами? Попытайтесь Компилировать и Выполнить Примеры: FAQ.
Жалобы? Поздравление? Предложения? Дайте нам свою обратную связь.

Предыдущая страница: Введение в Слушателей События
Следующая страница: Слушатели, Поддерживаемые Компонентами Swing