Элементы управления |
245 |
создать большое приложение с множеством функций и большую часть этих функций мы реализовали в виде разнообразных элементов управления: простых кнопок JButton, пунктов меню, всплывающих меню и т. д. Некоторые из этих элементов управления могут выполнять одну и ту же работу, но размещаться в разных местах: в диалоговых окнах, в меню, на панелях инструментов. Создав все нужные вам элементы управления, вы можете получить от одного из них модель (методом getModel()), и разделить ее между всеми элементами, выполняющими одно и то же действие (хотя внешний вид и расположение этих элементов управления могут быть разными). Разделение модели не таит в себе черной магии: вы просто передаете ссылку на одну и ту же модель разным элементам, используя метод setModel(). После этого, все элементы управления, имеющие одну и ту же модель, всегда будут находиться в одном и том же состоянии. К примеру, если некоторое действие становится недоступным, вы можете отключить одну из кнопок или сделать то же самое непосредственно с моделью, и все элементы управления, использующие эту модель, отключатся автоматически. Это сделает ваш код удивительно элегантным и чистым, легким в чтении и поддержке — в этом сама суть MVC. Чуть дальше мы узнаем об еще одном средстве удобной поддержки разных элементов управления с одинаковым предназначением — интерфейсе Action.
Модель кнопок описана в интерфейсе ButtonModel, а в качестве реализации Swing использует стандартный класс DefaultButtonModel. Писать собственную модель кнопок вам вряд ли стоит: данные в ней хранятся незамысловатые, стандартная модель работает хорошо, и никаких дивидендов при написании собственной модели не предвидится.
A теперь рассмотрим, о каких событиях может сообщать кнопка JButton и как в своей программе получать извещения об этих событиях.
Обработка событий от кнопок
После того как вы создали кнопку, настроили ее внешний вид и разместили в контейнере, остается соединить ее с деловой частью вашей программы, то есть определить, какие действия будут выполняться при определенном действии пользователя над кнопкой. С помощью модели событий библиотеки Swing можно получить сообщение о событии в свою программу многими способами, так, как вам удобно, и писать действительно элегантные программы. Убедимся в этом еще раз на примере кнопок:
//ButtonEvents.java
//Обработка событий от кнопок JButton import javax.swing.*;
import javax.swing.event.*; import java.awt.*;
import java.awt.event.*;
public class ButtonEvents extends JFrame { private JTextArea info;
public ButtonEvents() { super("ButtonEvents"); setDefaultCloseOperation( EXIT_ON_CLOSE );
//создаем кнопку и помещаем ее на север окна
JButton button = new JButton("Нажмите меня!"); add(button, "North");
//поле для вывода сообщений о событиях
info = new JTextArea("Пока событий не было\n");
246 |
ГЛАВА 9 |
add(new JScrollPane(info));
//привязываем к нашей кнопке слушателей событий
//слушатели описаны как внутренние классы button.addActionListener(new ActionL()); button.addChangeListener(new ChangeL());
//присоединение слушателя прямо на месте button.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) { info.append("Это вы все равно не увидите");
}
});
//выводим окно на экран
setSize(400, 300); setVisible(true);
}
class ActionL implements ActionListener { public void actionPerformed(ActionEvent e) {
info.append(
"Получено сообщение о нажатии кнопки! От — " + e.getActionCommand() + "\n");
}
}
class ChangeL implements ChangeListener { public void stateChanged(ChangeEvent e) {
info.append(
"Получено сообщение о смене состояния кнопки!\n"); // это источник события
Object src = e.getSource();
}
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new ButtonEvents(); } });
}
}
Пример сам по себе очень прост — создается окно, на север которого помещается кнопка JButton, а в центр — многострочное текстовое поле JTextArea, вложенное в панель прокрутки JScrollPane. В это поле мы будем помещать сообщения о событиях, принятых кнопкой от пользователя.
Элементы управления (в том числе кнопки), унаследованные от класса AbstractButton, могут посылать сообщения о трех типах событий (за исключением стандартных событий, общих для всех компонентов Swing). Эти события перечислены в табл. 9.3.
Элементы управления |
247 |
Таблица 9.3. События элементов управления, унаследованных от класса AbstractButton
Событие |
Описание |
|
|
ActionEvent (слушатель |
Самое понятное событие для любой кнопки и, пожалуй, самое |
ActionListener) |
простое и наиболее часто используемое событие вообще в лю- |
|
бом графическом Java-приложении. Посылается кнопкой, когда |
|
пользователь щелкает на ней (нажимает и отпускает), чтобы про- |
|
извести действие |
ChangeEvent (слушатель |
Это событие мало что означает собственно для кнопок; с его по- |
ChangeListener) |
мощью модель кнопок ButtonModel взаимодействует со своим |
|
UI-представителем. Модель при изменении хранящегося в ней |
|
состояния кнопки (это может быть изменение включенного состо- |
|
яния на выключенное, обычного на нажатое и т. п.) запускает со- |
|
бытие ChangeEvent. UI-представитель обрабатывает это событие |
|
и соответствующим образом перерисовывает кнопку. Впрочем, вы |
|
можете обрабатывать это событие и сами, в том случае если вас |
|
интересуют малейшие изменения в состоянии кнопки |
ItemEvent (слушатель Item- |
Это событие (мы рассмотрим его чуть позже) посылают компонен- |
Listener) |
ты, которые имеют несколько равноценных состояний (например, |
|
флажки и переключатели), чтобы сообщить о смене состояния |
|
|
После создания кнопки к ней присоединяются слушатели событий, по одному на каждое возможное событие. При этом также демонстрируются возможные способы присоединения слушателей. Для событий ActionEvent и ChangeEvent слушатели помещаются в отдельные внутренние классы, а слушатель события ItemEvent создается прямо на месте, как анонимный внутренний класс. Слушателей событий у кнопок может быть произвольное количество, поскольку кнопки относятся к компонентам с групповой (multicast) обработкой событий3.
Запустив программу, вы увидите, как появляются события, читая сообщения в текстовом поле.
3 На самом деле все компоненты в Swing поддерживают произвольное количество слушателей, благодаря использованию простого и эффективного средства для хранения слушателей — класса EventListenerList. Мы подробно обсуждали его в главе 2, когда создавали свои собственные типы событий.
248 |
ГЛАВА 9 |
Заметьте, что при щелчке на кнопке слушатель события определяет имя нажатой кнопки, используя метод getActionCommand() класса ActionEvent. Этот метод применяется для того, чтобы при обработке событий от нескольких кнопок в одном слушателе можно было их отличить. В качестве имени можно использовать произвольную строку символов, устанавливая ее методом setActionCommand() класса AbstractButton. Иногда этот механизм позволяет элегантно решать некоторые задачи. Например, если таким образом передавать имя класса, объект которого необходимо создать, то один слушатель будет способен обработать неограниченное количество кнопок.
События ActionEvent и ChangeEvent вообще несут не слишком много полезной информации, так как предполагается, что в приложении используется наиболее элегантный подход — каждому компоненту сопоставляется свой слушатель событий. Единственное, что можно себе позволить — это получить ссылку на источник события методом getSource(). Однако будьте осторожнее с вызовом этого метода, потому что это может привести к сильной связи между интерфейсом и деловой логикой, а это всегда нежелательно.
Запустив программу, вы также увидите, что событие ItemEvent не возникает вовсе. Это совсем не удивительно, потому что оно «работает» только с флажками, переключателями и другими компонентами, имеющими состояние, а кнопки его просто игнорируют.
Это все о событиях от кнопок JButton. У других элементов управления имеются дополнительные события, но в большинстве своем вам будет достаточно тех событий, которые мы только что обсудили. Говорить больше не о чем — остается лишь оценить простоту и мощь, которую способен привнести в программы продуманный объектноориентированный подход.
До сих пор вы, скорее всего, для выбора кнопок и других элементов управления в своих приложениях применяли мышь. В крайнем случае, вы могли воспользоваться встроенной в Swing системой перемещения фокуса ввода по компонентам путем нажатия клавиши Tab. Однако для настоящего приложения, претендующего на роль классного, этого мало.
Мнемоники
Хорошее приложение не должно полностью полагаться на то, что у пользователя есть мышь или подобное ей устройство. Пусть сейчас уже трудно себе представить компьютер без такого приспособления, все равно эту ситуацию нужно учитывать. Еще более важно сделать приложение доступным для людей с ограниченными возможностями, которые не могут использовать манипуляторы. Именно для этого в приложениях и появились упоминавшиеся в предыдущей главе мнемоники, которые позволяют получить доступ к компоненту, нажимая специальную клавишу (обычно Alt) вместе с клавишей символа, идентифицирующего этот компонент.
Разработчики Swing учли это и наделили библиотеку поддержкой мнемоник для всех элементов управления, а также для надписей JLabel и вкладок JTabbedPane, так что вам не придется вручную прослушивать клавиатуру и заниматься обработкой клавиш. Рассмотрим сначала, как можно включить мнемоники в свою программу, а затем обсудим проблемы, которые при этом возникают:
//ButtonMnemonics.java
//Поддержка кнопками клавиатурных мнемоник import javax.swing.*;
import java.awt.*;
public class ButtonMnemonics extends JFrame {
Элементы управления |
249 |
public ButtonMnemonics() { super("ButtonMnemonics"); setDefaultCloseOperation( EXIT_ON_CLOSE );
//используем последовательное расположение setLayout(new FlowLayout());
//создаем кнопку
JButton button = new JButton("Нажмите меня!");
//мнемоника (русский символ) button.setMnemonic('Н'); add(button);
//еще одна кнопка, только надпись на английском button = new JButton("All Right!"); button.setMnemonic('L'); button.setToolTipText("Жмите смело"); button.setDisplayedMnemonicIndex(2); add(button);
//выводим окно на экран
pack();
setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new ButtonMnemonics(); } });
}
}
В этом очень простом примере в контейнер помещается две кнопки — надпись на первой сделана по-русски, а на второй — по-английски. Для того чтобы позволить пользователю получить доступ к кнопке с клавиатуры, были использованы перечисленные ниже методы класса AbstractButton.
setMnemonic(). Позволяет указать мнемонику, то есть то, клавиша какого символа в сочетании с управляющей клавишей (Alt) будет вызывать нажатие кнопки. Можно просто указать символ (в одинарных кавычках, регистр не учитывается), а можно использовать константы из класса java.awt.event.KeyEvent, но первый подход проще и понятнее.
setDisplayedMnemonicIndex(). Этот метод дает нам возможность управлять тем, какой из символов надписи кнопки будет подчеркиваться, то есть символизировать наличие мнемоники (если в надписи есть несколько одинаковых символов). В нашем примере в слове «All» этим методом можно выделить не первую букву «l», а вторую4.
Запустив программу с примером, вы убедитесь, что символ мнемоники подчеркивается, и она работает… только для кнопки на английском языке.
4 Пользуясь этим методом, не забывайте, что отсчет символов строки ведется с нуля.