Содержание | Рекомендации по изучению | Задания

Занятие 1 | Занятие 2 | Занятие 3 | Занятие 4 | Занятие 6 | Занятие 7 | Занятие 8 | Занятие 9 | Занятие 10 | Занятие 11

Занятие 10

Обработка событий в Swing

Графический интерфейс пользователя включает в себя не только расположение в окне необходимых элементов управления, но и назначение реакции на пользовательские события. Большая честь действий в оконных программах выполняется в ответ на выбор пользователем команд меню, нажатие кнопок, а иногда даже просто в ответ на ввод нового символа в текстовое поле.

Таким образом, при разработке программы необходимо:

  1. Выявить события, в ответ на которые потребуется реакция программы.
  2. Написать код, реализующий эту реакцию (так называемый обработчик событий).
  3. Связать обработчик события с соответствующим событием.

Первый пункт зависит от логики работы приложения и находится всецело на усмотрении программиста. Обработчик события — это, как правило, обычный метод и ничего особенного в его написании нет.

Паттерн проектирования «наблюдатель»

В разных языках программирования и визуальных библиотеках используются самые различные способы привязки процедуры (метода), которая должна вызываться в ответ на некоторое событие, к самому этому событию. В частности, в Delphi процедура просто должна иметь особое название, состоящее из имени компонента и типа обрабатываемого события.

В библиотеке Swing для привязки обработчика события к вызывающему его компоненту используется паттерн проектирования «наблюдатель».

Паттерны проектирования — это стандартные приемы объектно-ориентированного программирования, позволяющие оптимальным образом справиться с нетривиальными, но часто возникающими в программировании ситуации. Паттерн проектирования описывает классы, которые необходимо ввести для разрешения такой ситуации и взаимодействие между классами. В частности, ранее мы рассматривали паттерн проектирования «итератор», позволяющий обойти все элементы коллекции с помощью двух простейших методов в порядке, скрытом в самом итераторе.

Паттерн проектирования «наблюдатель» применяется, когда один объект должен оповещать другие о произошедших с ним изменениях или внешних воздействиях. Такой объект называется наблюдаемым, а объекты, которые следует оповестить — наблюдателями.

Для того, чтобы подобное взаимодействие было возможным, объект-наблюдатель должен иметь метод (или несколько методов) с заранее определенной сигнатурой (именем и параметрами). Когда с наблюдаемым объектом произойдет ожидаемое событие, он вызовет соответствующий метод у своего наблюдателя. В этом методе и будет запрограммирована реакция на событие.

Для того, чтобы наблюдаемый объект мог вызвать метод наблюдателя, он должен знать о том, что такой наблюдатель существует. Поэтому у наблюдаемого предварительно должен быть вызван специальный метод, регистрирующий его наблюдателя.

Заметим, что в данной схеме один наблюдатель может быть зарегистрирован у нескольких объектов (т.е. одинаково реагировать на изменения в каждом из них), а у одного объекта может быть несколько наблюдателей (т.е. при возникновении события выполняется несколько независимых методов-обработчиков). Это весьма увеличивает гибкость программирования.

Механизм обработки событий библиотеки Swing

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

Наблюдателями должны являться объекты классов, поддерживающих специальные интерфейсы (вспомним, что в классе наблюдателя должны быть определенные методы, о которых «знает» наблюдаемый и вызывает их при наступлении события). Такие классы в терминологии Swing называются слушателями.

Интерфейс MouseListener и обработка событий от мыши

Мы рассмотрим технологию написания слушателей на примере слушателей событий мыши.

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

Так например, кнопка для входа в систему из примера в конце предыдущей главы должна реагировать на щелчок по ней, проверяя имя и пароль, введенные пользователем.

Слушатель событий от мыши должен реализовать интерфейс MouseListener. В этом интерфейсе перечислены следующие методы:

public void mouseClicked(MouseEvent event) — выполнен щелчок мышкой на наблюдаемом объекте

public void mouseEntered(MouseEvent event) — курсор мыши вошел в область наблюдаемого объекта

public void mouseExited(MouseEvent event) — курсор мыши вышел из области наблюдаемого объекта

public void mousePressed(MouseEvent event) — кнопка мыши нажата в момент, когда курсор находится над наблюдаемым объектом

public void mouseReleased(MouseEvent event) — кнопка мыши отпущена в момент, когда курсор находится над наблюдаемым объектом

о параметре типа MouseEnent

Каждый из этих методов получает параметр типа MouseEvent, через который передается дополнительная информация о возникшем событии. Например, можно получить объект, вызвавший данное событие, командой event.getSource() (не забывайте, что один и тот же слушатель может быть зарегистрирован в нескольких объектах и иногда ему может понадобиться узнать, какой именно из них вызвал данный метод). Метод getPoint() вернет координаты точки, в которой находился курсор мыши, когда было инициировано событие. Метод getClickCount() возвращает число сделанных подряд щелчков (имеет смысл только для mouseClicked() и позволяет особым образом обработать двойной, тройной и т.п. щелчок).

Чтобы обработать нажатие на кнопку, требуется описать класс, реализующий интерфейс MouseListener, причем метод mouseClicked() должен содержать обработчик события. Далее необходимо создать объект этого класса и зарегистрировать его как слушателя интересующей нас кнопки. Для регистрации слушателя используется метод addMouseListener(MouseListener listener).

Опишем класс слушателя в пределах класса окна SimpleWindow, после конструктора. Обработчик события будет проверять, ввел ли пользователь логин «Иван» (пароль проверять не будем) и выводить сообщение об успехе или неуспехе входа в систему (работа стандартных диалоговых окон обсуждается дальше в этой главе):

class MouseL implements MouseListener {   public void mouseClicked(MouseEvent event) { if (loginField.getText().equals("Иван")) JOptionPane.showMessageDialog(null, "Вход выполнен"); else JOptionPane.showMessageDialog(null, "Вход НЕ выполнен"); }   public void mouseEntered(MouseEvent event) {}   public void mouseExited(MouseEvent event) {}   public void mousePressed(MouseEvent event) {}   public void mouseReleased(MouseEvent event) {}   }

Мы сделали слушателя вложенным классом класса SimpleWindow, чтобы он мог легко получить доступ к его внутренним полям loginField и passwordField. Кроме того, хотя реально мы обрабатываем только одно из пяти возможных событий мыши, описывать пришлось все пять методов (четыре имеют пустую реализацию). Дело в том, что в противном случае класс пришлось бы объявить абстрактным (ведь он унаследовал от интерфейса пустые заголовки методов) и мы не смогли бы создать объект этого класса. А мы должны создать объект слушателя и прикрепить его к кнопке. Для этого в код конструктора SimpleWindow() необходимо добавить команду:

ok.addMouseListener(new MouseL());

Это можно сделать сразу после команды:

JButton ok = new JButton("OK");

Создание слушателей с помощью анонимных классов

Чтобы кнопка ok обрела слушателя, который будет обрабатывать нажатие на нее, нам понадобилось описать новый (вложенный) класс. Иногда вместо вложенного класса можно обойтись анонимным. Как вы помните, анонимный класс не имеет имени и в программе может быть создан только один объект этого класса (создание которого совмещено с определением класса). Но очень часто, как и в нашем примере, слушатель пишется для того, чтобы обрабатывать события единственного объекта — в нашем случае кнопки ok, а значит, используется в программе только однажды: во время привязки к этому объекту. Таким образом, мы можем заменить вложенный класс анонимным. Для этого описание класса MouseL можно просто удалить, а команду

ok.addMouseListener(new MouseL());

заменить на:

ok.addMouseListener(new MouseListener() {   public void mouseClicked(MouseEvent event) { if (loginField.getText().equals("Иван")) JOptionPane.showMessageDialog(null, "Вход выполнен"); else JOptionPane.showMessageDialog(null, "Вход НЕ выполнен"); }   public void mouseEntered(MouseEvent event) {}   public void mouseExited(MouseEvent event) {}   public void mousePressed(MouseEvent event) {}   public void mouseReleased(MouseEvent event) {}   });

Новый вариант выглядит более громоздко, чем первый. Злоупотребление анонимными классами может сделать программу плохо читаемой. Однако в результате все действия с кнопкой (создание, настройка ее внешнего вида и команды обработки щелчка по ней) не разнесены, как в случае вложенных классов, а находятся рядом, что облегчает сопровождение (внесение изменений) программы. В случае простых (в несколько строк) обработчиков разумно делать выбор в пользу анонимных классов.

Вообще, можно реализовать интерфейс слушателя в любом классе, добавив к его описанию директиву implements MouseListener и определив в нем необходимые методы. В частности, это может быть сам класс окна SimpleWindow. Но чаще всего пользуются анонимными и вложенными классами (не в последнюю очередь благодаря наличию адаптеров (см. далее).

Класс MouseAdapter

Программа стала выглядеть загроможденной главным образом из-за того, что помимо полезного для нас метода mouseClicked() нам пришлось определять пустые реализации всех остальных, не нужных методов. В принципе, этого можно избежать.

Класс MouseAdapter реализует интерфейс MouseListener, определяя пустые реализации для каждого из его методов. Можно унаследовать своего слушателя от этого класса и переопределить те методы, которые нам нужны.

В результате предыдущее описание слушателя будет выглядеть более компактно:

ok.addMouseListener(new MouseAdapter() {   public void mouseClicked(MouseEvent event) { if (loginField.getText().equals("Иван")) JOptionPane.showMessageDialog(null, "Вход выполнен"); else JOptionPane.showMessageDialog(null, "Вход НЕ выполнен"); }   });

Общая структура слушателей

Кроме слушателей MouseListener визуальные компоненты Swing поддерживают целый ряд других слушателей (заметим, что в разных компонентах набор возможных слушателей может отличаться).

Каждый слушатель должен реализовывать интерфейс ***Listener, где *** — тип слушателя. Практически каждому из этих интерфейсов (за исключением тех, в которых всего один метод) соответствует пустой класс-заглушка ***Adapter. Каждый метод интерфейса слушателя принимает один параметр типа ***Event, в котором собрана вся информация, относящаяся к событию. Мы не будем подробно рассматривать методы получения этой информации. Как правило их немного и их назначение легко понять, а перечень методов даст контекстная подсказка Eclipse.

Чтобы привязать слушателя к объекту (который поддерживает соответствующий тип слушателей) используется метод add***Listener(***Listener listener).

Например, слушатель MouseListener должен реализовать интерфейс с таким же именем, которому соответствует класс-заглушка MouseAdapter. Методы этого интерфейса обрабатывают параметр типа MouseEvent, а регистрируется слушатель методом addMouseListener(MouseListener listener). Мы не будем повторять эту информацию для других типов слушателей, они работают аналогично.

Слушатель фокуса FocusListener

Слушатель FocusListener отслеживает моменты, когда объект получает фокус (то есть становится активным) или теряет его. Концепция фокуса очень важна для оконных приложений. В каждый момент времени в окне может быть только один активный (находящийся в фокусе) объект, который получает информацию о нажатых на клавиатуре клавишах (т.е. реагирует на события клавиатуры), о прокрутке колесика мышки и т.д. Пользователь активирует один из элементов управления нажатием мышки или с помощью клавиши Tab (переключаясь между ними).

Интерфейс FocusListener имеет два метода:

public void focusGained(FocusEvent event) — вызывается, когда наблюдаемый объект получает фокус

public void focusLost(FocusEvent event) — вызывается, когда наблюдаемый объект теряет фокус.

Слушатель колесика мышки MouseWheelListener

Слушатель MouseWheelListener оповещается при вращении колесика мыши в тот момент, когда данный компонент находится в фокусе. Этот интерфейс содержит всего один метод:

public void mouseWheelMoved(MouseWheelEvent event).

Слушатель клавиатуры KeyListener

Слушатель KeyListener оповещается, когда пользователь работает с клавиатурой в тот момент, когда данный компонент находится в фокусе. В интерфейсе определены методы:

public void mouseKeyTyped(KeyEvent event) — вызывается, когда с клавиатуры вводится символ

public void mouseKeyPressed(KeyEvent event) — вызывается, когда нажата клавиша клавиатуры

public void mouseKeyReleased(KeyEvent event) — вызывается, когда отпущена клавиша клавиатуры.

Аргумент event этих методов способен дать весьма ценные сведения. В частности, команда event.getKeyChar() возвращает символ типа char, связанный с нажатой клавишей. Если с нажатой клавишей не связан никакой символ, возвращается константа CHAR_UNDEFINED. Команда event.getKeyCode() возвратит код нажатой клавиши в виде целого числа типа int. Его можно сравнить с одной из многочисленных констант, определенных в классе KeyEvent: VK_F1, VK_SHIFT, VK_D, VK_MINUS и т.д. Методы isAltDown(), isControlDown(), isShiftDown() позволяют узнать, не была ли одновременно нажата одна из клавиш-модификаторов Alt, Ctrl или Shift.

Упражнение.

В окне входа в систему добавьте возможность, чтобы при нажатии клавиши Esc в окне ввода логина его содержимое очищалось.

Слушатель изменения состояния ChangeListener

Слушатель ChangeListener реагирует на изменение состояния объекта. Каждый элемент управления по своему определяет понятие «изменение состояния». Например, для панели со вкладками JTabbedPane это переход на другую вкладку, для ползунка JSlider — изменение его положения, кнопка JButton рассматривает как смену состояния щелчок на ней. Таким образом, хотя событие это достаточно общее, необходимо уточнять его специфику для каждого конкретного компонента. В интерфейсе определен всего один метод:

public void stateChanged(ChangeEvent event).

Слушатель событий окна WindowListener

Слушатель WindowListener может быть привязан только к окну и оповещается о различных событиях, произошедших с окном:

public void windowOpened(WindowEvent event) — окно открылось.

public void windowClosing(WindowEvent event) — попытка закрытия окна (например, пользователя нажал на крестик). Слово «попытка» означает, что данный метод вызовется до того, как окно будет закрыто и может воспрепятствовать этому (например, вывести диалог типа «Вы уверены?» и отменить закрытие окна, если пользователь выберет «Нет»).

public void windowClosed(WindowEvent event) — окно закрылось.

public void windowIconified(WindowEvent event) — окно свернуто.

public void windowDeiconified(WindowEvent event) — окно развернуто.

public void windowActivated(WindowEvent event) — окно стало активным.

public void windowDeactivated(WindowEvent event) — окно стало неактивным.

Слушатель событий компонента СomponentListener

Слушатель ComponentListener оповещается, когда наблюдаемый визуальный компонент изменяет свое положение, размеры или видимость. В интерфейсе четыре метода:

public void componentMoved(ComponentEvent event) — вызывается, когда наблюдаемый компонент перемещается (в результате вызова команды setLocation(), работы менеджера размещения или еще по какой-то причине).

public void componentResized(ComponentEvent event) — вызывается, когда изменяются размеры наблюдаемого компонента.

public void componentHidden(ComponentEvent event) — вызывается, когда компонент становится невидимым.

public void componentShown(ComponentEvent event) — вызывается, когда компонент становится видимым.

Слушатель выбора элемента ItemListener

Слушатель ItemListener реагирует на изменение состояния одного из элементов, входящих в состав наблюдаемого компонента. Например, выпадающий список JComboBox состоит из множества элементов и слушатель реагирует, когда изменяется выбранный элемент. Также данный слушатель оповещается при выборе либо отмене выбора флажка JCheckBox или переключателя JRadioButton, изменении состояния кнопки JToggleButton и т.д. Слушатель обладает одним методом:

public void itemStateChanged(ItemEvent event).

Универсальный слушатель ActionListener

Среди многочисленных событий, на которые реагирует каждый элемент управления (и о которых он оповещает соответствующих слушателей, если они к нему присоединены), есть одно основное, вытекающее из самой сути компонента и обрабатываемое значительно чаще, чем другие. Например, для кнопки это щелчок на ней, а для выпадающего списка — выбор нового элемента.

Для отслеживания и обработки такого события может быть использован особый слушатель ActionListener, имеющий один метод:

public void actionPerformed(ActionEvent event).

У использования ActionListener есть небольшое преимущество в эффективности (так, при обработке нажатия на кнопку не надо реагировать на четыре лишних события — ведь даже если методы-обработчики пустые, на вызов этих методов все равно тратятся ресурсы). А кроме того очень удобно запомнить и постоянно использовать один класс с одним методам и обращаться к остальным лишь в тех относительно редких случаях, когда возникнет такая необходимость.

Обработка нажатия на кнопку ok в нашем примере легко переписывается для ActionListener:

ok.addMouseListener(new ActionListener() {   public void actionPerformed(ActionEvent event) { if (loginField.getText().equals("Иван")) JOptionPane.showMessageDialog(null, "Вход выполнен"); else JOptionPane.showMessageDialog(null, "Вход НЕ выполнен"); }   });

Работа с меню в библиотеке Swing

Неотъемлемой частью современных оконных программ является меню, представляющее собой удобно сгруппированный набор команд. Меню бывает двух типов: главное и контекстное. Главное меню располагается вдоль верхней границы окна и содержит команды, относящиеся ко всему приложению (точнее, все команды, которые можно выполнить, работая с данным окном). Контекстное меню вызывается нажатием правой кнопки мышки на конкретном объекте и содержит команды, которые могут быть применены именно к этому объекту.

Создание главного меню

Главное меню окна представлено в Swing классом JMenuBar. По сути своей этот класс представляет собой панель с менеджером расположения BoxLayout (по горизонтали), в которую можно добавлять не только элементы меню, но и что угодно: хоть выпадающие списки, хоть панели с закладками. Однако для удобства пользования программой предпочтительнее использовать «традиционные» возможности меню.

Главное меню должно быть присоединено к окну методом setJMenuBar(JMenuBar menuBar).

Элементами главного меню являются обычные меню — выпадающие прямоугольные блоки команд — объекты класса JMenu. Конструктор JMenu(String title) принимает один параметр: название меню, которое будет отображаться в строке главного меню.*

Меню, в свою очередь, состоит из пунктов меню, представленных классом JMenuItem. По логике работы пункты меню аналогичны кнопке JButton, то есть, при нажатии на него пользователем выполняется какое-то действие.

об особых разновидностях пунктов меню

У класса JMenuItem есть наследники. Класс JCheckBoxMenuItem по логике работы аналогичен флажку JCheckBox — то есть, прямо в меню можно установить или сбросить какой-либо параметр логического типа. Класс JRadioButtonMenuItem эмулирует переключатель, позволяя выбрать только одну из нескольких альтернатив.

Создать элемент меню можно пустым конструктором JMenuItem либо (что более востребовано) одним из конструкторов, в которые передается текст и/или значок элемента меню: JMenuItem(String text), JMenuItem(Icon icon), JMenuItem(String text, Icon icon). Конечно, в любой момент текст и значок можно сменить методами setText(String text) и setIcon(Icon icon) соответственно.

Элемент добавляется в меню методом add(JMenuItem item) класса JMenu. Чтобы отделить группы взаимосвязанных элементов меню, можно добавить между ними разделитель методом addSeparator() класса JMenu.

Кроме того, в меню можно добавить и другое меню. В этом случае образуется последовательность вложенных друг в друга подменю, что довольно часто встречается в современных программах. Но не следует увлекаться: глубина вложенности более трех уровней скорее всего приведет к неудобствам пользования программой.

Создадим главное меню окна, состоящее из двух подменю: «Файл» и «Правка», причем в меню «Правка» поместим выпадающее подменю. Кроме того, воспользуемся знаниями о менеджере расположения главного меню, чтобы добавить с правого края значок (наподобие того, как это сделано в браузере Internet Explorer).

SimpleWindow(){ super("Окно с меню"); setDefaultCloseOperation(EXIT_ON_CLOSE); JMenuBar menuBar = new JMenuBar(); JMenu fileMenu = new JMenu("Файл"); fileMenu.add(new JMenuItem("Новый")); fileMenu.add(new JMenuItem("Открыть", new ImageIcon("1.gif"))); fileMenu.add(new JMenuItem("Сохранить")); fileMenu.addSeparator(); fileMenu.add(new JMenuItem("Выйти")); JMenu editMenu = new JMenu("Правка"); editMenu.add(new JMenuItem("Копировать")); JMenu pasteMenu = new JMenu("Вставить"); pasteMenu.add(new JMenuItem("Из буфера")); pasteMenu.add(new JMenuItem("Из файла")); editMenu.add(pasteMenu); menuBar.add(fileMenu); menuBar.add(editMenu); menuBar.add(Box.createHorizontalGlue()); menuBar.add(new JLabel(new ImageIcon("2.gif"))); setJMenuBar(menuBar); setSize(250,150); }

Обработка команд меню

Как было сказано, каждый элемент меню по сути представляет собой кнопку, поэтому реагирует на те же события и позволяет присоединять к себе тех же слушателей, что и кнопка.* Поэтому мы можем назначить реакцию на команды меню точно также, как делали это выше. Например, чтобы при выборе в меню элемента «Выйти» программа прекращала свою работу, следует заменить в примере команду

fileMenu.add(new JMenuItem("Выйти"));

на последовательность команд:

JMenuItem exit = new JMenuItem("Выйти"); exit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.exit(0); } }); fileMenu.add(exit);

Командой System.exit(0) прекращается работа текущего приложения.

Создание контекстного меню

Контекстное (или всплывающее) меню реализовано в классе JPopupMenu, очень похожему на класс JMenu. Отличительным методом этого класса является метод show(Component comp, int x, int y), отображающий меню в точке с заданными координатами относительно границ заданного компонента.

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

Рассмотрим пример, в котором к уже созданному нами окну добавляется метка с надписью и контекстное меню из двух элементов, связанное с этой меткой. Необходимо добавить в конструктор перед последней командой (setSize) следующий код:

label = new JLabel("КНИЖКА", new ImageIcon("1.gif"), JLabel.RIGHT); JPanel panel = new JPanel(); panel.add(label); popup = new JPopupMenu(); popup.add(new JMenuItem("Прочитать")); popup.add(new JMenuItem("Сжечь")); label.addMouseListener(new MouseAdapter(){ public void mouseClicked(MouseEvent event) { if (SwingUtilities.isRightMouseButton(event)) popup.show(label, event.getX(), event.getY()); } }); setContentPane(panel);

При этом в определении класса окна следует ввести два поля:

JPopupMenu popup; JLabel label;

Это необходимо для того, чтобы анонимный класс-слушатель мог обратиться к метке и к контекстному меню.

Приведенный пример также иллюстрирует применение полезного метода isRightMouseButton(MouseEvent event), определенного в классе вспомогательных утилит SwingUtilities. Метод получает информацию о событии мыши и отвечает на вопрос, была ли нажата именно правая кнопка мыши. Методы getX() и getY() возвращают координаты курсора мыши относительно наблюдаемого компонента.

Интерфейс Action (действие) и класс AbstractAction

Очень часто одна и та же команда дублируется в графическом интерфейсе в нескольких местах. Например, команда «Копировать» может присутствовать в главном меню окна, в контекстном меню некоторого объекта, а также в виде кнопки на инструментальной панели. Это необходимо для удобства пользования программой. Однако, привязывать ко всем этим объектам (кнопкам и командам меню) одинаковых слушателей довольно утомительно. Более того, все эти команды одинаково должны реагировать на изменения состояния программы: если копирование по каким-то причинам становится невозможным, то все элементы управления, представляющие данную команду, должны сделаться неактивными.

Для упрощения работы в таких ситуациях предусмотрен интерфейс Action. Он собирает в одном месте все, что относится к некоторому действию: обработчик, показатель активности, всплывающую подсказку, значок и т.д.

Интерфейс Action унаследован от интерфейса ActionListener, поэтому его главный метод — actionPerformed(ActionEvent event). Именно здесь программируется само действие. Но помимо этого посредством метода putValue(String key, Object value) можно задать одно из дополнительных свойств действия. Первый параметр — строковый идентификатор свойства. Он может принимать одно из значений, описанных в константах интерфейса Action. Второй параметр — объект, представляющий собой значение свойства.

Основные свойства интерфейса Action (точнее, соответствующие им константы):

NAME — имя действия,

SMALL_ICON — значок, соответствующий действию,

SHORT_DESCRIPTION — краткое описание действия (для всплывающей подсказки).

Метод setEnabled(boolean enabled) позволяет сделать действие активным или неактивным.

На основе созданного действия можно создавать некоторые элементы управления, передавая это действие в качестве единственного параметра конструктора. К таким элементам управления, в частности, относятся элементы меню и кнопки.

Недостаток интерфейса Action — в нем слишком много вспомогательных абстрактных методов (их семь, в том числе setEnabled() и putValue()) и программировать их достаточно утомительно. Поэтому обычно используется реализующий данный интерфейс класс AbstractAction, в котором «не заполнен» единственный метод — actionPerformed(), а его все равно необходимо определить для программирования сути действия.

Рассмотрим пример.

  public class SimpleWindow extends JFrame {   private ExitAction exitAction;   SimpleWindow(){ super("Окно с меню"); setDefaultCloseOperation(EXIT_ON_CLOSE); exitAction = new ExitAction(); DeactivateAction deactivateAction = new DeactivateAction(); JMenuBar menuBar = new JMenuBar(); JMenu fileMenu = new JMenu("Файл"); fileMenu.add(new JMenuItem("Новый")); fileMenu.addSeparator(); fileMenu.add(deactivateAction); fileMenu.add(exitAction); menuBar.add(fileMenu); setJMenuBar(menuBar); JToolBar toolBar = new JToolBar("Панель инструментов"); toolBar.add(exitAction); toolBar.add(deactivateAction); getContentPane().add(toolBar, BorderLayout.NORTH); JPanel panel = new JPanel(); panel.add(new JButton(exitAction)); panel.add(new JButton(deactivateAction)); getContentPane().add(panel); setSize(250,250); }   class ExitAction extends AbstractAction {   ExitAction(){ putValue(Action.NAME, "Выйти"); putValue(Action.SHORT_DESCRIPTION, "Программа перестанет работать, а окно исчезнет с экрана."); putValue(Action.SMALL_ICON, new ImageIcon("2.gif")); }   public void actionPerformed(ActionEvent event) { System.exit(0); }   }   class DeactivateAction extends AbstractAction {   DeactivateAction(){ putValue(Action.NAME, "Запретить выход"); putValue(Action.SMALL_ICON, new ImageIcon("1.gif")); }   public void actionPerformed(ActionEvent event) { if (exitAction.isEnabled()) { exitAction.setEnabled(false); putValue(Action.NAME, "Разрешить выход");   } else { exitAction.setEnabled(true); putValue(Action.NAME, "Запретить выход"); } }   }   }

Здесь в окне описаны два внутренних класса-действия, одно из которых — это выход из программы. Объект этого класса, exitAction, является внутренним полем класса-окна и на его основе создается пункт меню и две кнопки. Второй класс-действие активирует и деактивирует объект exitAction (при этом изменяется соответствующий действию текст). Запустите пример и пронаблюдайте за тем, как выглядят и ведут себя объекты на основе Action, а затем внимательно проанализируйте его.

Стандартные диалоговые окна

Класс JOptionPane

Неотъемлемой частью большинства программ являются небольшие диалоговые окна: для вывода пользователю сообщения (например, сообщения об ошибке), для вопроса, ответ на который важен для выполнения текущего действия (например, просьба подтвердить или отменить запрашиваемую операцию). Эти диалоги могут быть запрограммированы вручную на основе класса JFrame. Однако ввиду того, что они являются типичными для многих программ, Swing предоставляет в распоряжение программиста несколько готовых классов для работы с ними.

Чаще всего используется класс JOptionPane, содержащий несколько статических методов, отображающих стандартные диалоги.

Метод showMessageDialog() выводит на экран диалоговое окно, информирующее пользователя. Оно содержит надпись, значок и кнопку ОК. Существует несколько разновидностей этого метода с разными наборами параметров. Самый простой из них showMessageDialog(Component component, Object content) требует указания компонента, над которым должно появиться диалоговое окно и содержимого окна. Чаще всего содержимым окна является некоторая строка, а вместо первого параметра передается null — тогда окно появляется по центру экрана. Более «продвинутый» вариант showMessageDialog(Component component, Object content, String title, int type) позволяет задать также свой заголовок окна и выбрать его тип (влияет на иконку в окне): сообщение об ошибке (ERROR_MESSAGE), предупреждение (WARNING_MESSAGE), информация (INFORMATION_MESSAGE).

Диалоговое окно является модальным. Это значит, что пока пользователь не нажмет в этом окне кнопку OK, программа окажется заблокирована — пользователь не сможет работать с другими окнами.

Пронаблюдаем действие метода на простом примере:

public class SimpleWindow extends JFrame {   private JButton button;   SimpleWindow(){ super("Предупреждающий диалог"); setDefaultCloseOperation(EXIT_ON_CLOSE); button = new JButton("Информация"); button.addActionListener(new ActionListener() {   public void actionPerformed(ActionEvent event) { JOptionPane.showMessageDialog(button, "Не надо было нажимать на эту кнопку", "Информация", JOptionPane.WARNING_MESSAGE); }   }); getContentPane().setLayout(new FlowLayout()); getContentPane().add(button); setSize(200,150); } }

Кнопка button сделана полем класса окна, чтобы можно было получить к ней доступ из анонимного класса-слушателя.

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

Данное окно отображается методом showConfirmDialog(Component component, Object content). Параметры этого метода идентичны по смыслу параметрам showMessageDialog(), но в диалоговом окне появится не одна кнопка, а три: «Yes», «No» и «Cancel». Более того, метод возвращает значение, которое можно сравнить с константами YES_OPTION, NO_OPTION и CANCEL_OPTION. Логику работы с этим методом проиллюстрирует пример:

public class SimpleWindow extends JFrame {   JButton button;   SimpleWindow(){ super("Предупреждающий диалог"); setDefaultCloseOperation(EXIT_ON_CLOSE); button = new JButton("Выход"); button.addActionListener(new ActionListener() {   public void actionPerformed(ActionEvent event) { if (JOptionPane.showConfirmDialog(button, "Вы уверены, что хотите выйти?") == JOptionPane.YES_OPTION) System.exit(0); }   }); getContentPane().setLayout(new FlowLayout()); getContentPane().add(button); setSize(200,150); } }

Метод имеет еще четыре разновидности с разным набором параметров, позволяющих изменять заголовок и значок окна, а также набор кнопок.

Класс JFileChooser

Swing содержит готовое окно для выбора файла (полезное, например, для программирования пункта меню Файл --> Открыть). Объект класса JFileChooser создается простым конструктором без параметров, после чего может выводиться на экран методом showOpenDialog(). Этот метод возвращает результат действий пользователя по выбору файла, который сравнивается с одной из следующих констант:

APPROVE_OPTION — выбор файла прошел успешно. Теперь можно методом getFile() получить выбранный файл.

CANCEL_OPTION — пользователь отменил выбор файла, щелкнув на кнопке Cancel.

ERROR_OPTION — при выборе файла произошла ошибка, либо пользователь закрыл диалоговое окно крестиком.

Метод showSaveDialog() отображает то же самое окно, но теперь оно работает в режиме сохранения. Пользователь выбирает директорию для сохранения файла и может ввести его имя. Метод возвращает результат того же типа, что и showOpenDialog(). Если выбор пути для сохранения прошел успешно, вызов метода getFile() вернут путь, куда пользователь желает сохранить файл.

Следует иметь в виду, что сам класс JFileChooser ничего не открывает и не сохраняет. Он только возвращает путь к выбранному пользователем файлу. А открыть его или сохранить файл в заданном месте должна уже сама программа.

Метод setDialogTitle(String title) позволяет задать окну заголовок.

Содержание | Рекомендации по изучению | Задания

Занятие 1 | Занятие 2 | Занятие 3 | Занятие 4 | Занятие 6 | Занятие 7 | Занятие 8 | Занятие 9 | Занятие 10 | Занятие 11