Списки |
335 |
public void removeActionListener( ActionListener e) { }
}
Новый редактор для раскрывающегося списка с поддержкой HTML мы разместили в пакете com.porty.swing, так что при случае вам будет несложно использовать его в собственных приложениях. Для того чтобы некий класс смог действовать как редактор для списка JComboBox, ему необходимо реализовать интерфейс ComboBoxEditor. Интерфейс этот несложен, в нем четыре основных метода и еще пара методов служит для присоединения слушателей ActionListener. Наш новый объект для редактирования реализует данный интерфейс и работает следующим образом: метод getEditorComponent() возвращает компонент, который раскрывающийся список выведет на экран рядом с кнопкой, открывающей выпадающее меню, именно этот компонент и должен выполнять редактирование. В нашем классе это текстовый компонент JEditorPane, мы создаем и настраиваем его в конструкторе (указываем, что редактироваться будет HTML-текст и применяем специальную рамку, чтобы он не выглядел чересчур бледно). Метод setItem() вызывается раскрывающимся списком при смене элемента для редактирования, получаемый элемент мы преобразуем в строку и сразу же передаем компоненту JEditorPane, подразумевая, что все элементы списка записаны с помощью HTML-кода. Метод selectAll() является сигналом редактору выделить всю область редактирования и приступить к работе. В нем мы выделяем весь текст и на всякий случай запрашиваем в редактор фокус ввода, чтобы пользователь видел, что поле готово к вводу данных. Наконец, метод getItem() предназначен для возвращения набранного пользователем значения, мы возвращаем текст, находящийся в данный момент в редакторе.
Интерфейс ComboBoxEditor обязывает нас реализовать еще два метода, служащие для присоединения и отсоединения слушателей ActionListener. Данные слушатели должны оповещаться об окончании ввода в редакторе, так что программист-клиент раскрывающегося списка сможет узнать, когда пользователь заканчивает ввод нового значения. Стандартный объект для редактирования использует текстовое поле JTextField, а оно, как мы узнаем из главы 16, позволяет присоединять к себе слушателей ActionListener, которые оповещаются об окончании ввода. Эта возможность поля JTextField и применяется стандартным объектом для поддержки слушателей ActionListener. Увы, но текстовый редактор JEditorPane не позволяет легко узнать об окончании ввода, потому что он рассчитан на ввод неограниченного количества текста, так что мы оставляем наш редактор без поддержки слушателей. Это не слишком страшно — слушатели ActionListener применяются при работе с JComboBox не так уж и часто, к тому же вы будете знать о том, что на них не следует полагаться при работе с нашим новым редактором. Более того, при потере выпадающим списком фокуса ввода он сам генерирует искусственное событие ActionEvent, говорящее о новом значении в списке, что тоже неплохо. Событие не будет возникать лишь от самого редактора.
Ну а теперь посмотрим, как будет работать созданный своими руками объект для редактирования:
//HTMLComboEditorTest.java
//Пример использование специального объекта для
//редактирования
import javax.swing.*;
import com.porty.swing.HTMLComboBoxEditor;
import java.awt.*;
import java.awt.event.ActionListener;
336 |
ГЛАВА 11 |
import java.awt.event.ActionEvent; |
|
public class HTMLComboEditorTest |
|
extends JFrame { |
|
// данные для раскрывающегося списка |
|
private String[] data = { |
|
"<html><font color=yellow>Желтый", |
|
"<html><strike>Зачеркнутый", |
|
"<html><font color=green>Зеленый", |
|
"<html><em>С наклоном" }; |
|
public HTMLComboEditorTest() { |
|
super("HTMLComboEditorTest"); |
|
setDefaultCloseOperation(EXIT_ON_CLOSE); |
|
// создаем список |
|
final JComboBox combo = new JComboBox(data); |
|
combo.setPrototypeDisplayValue("11223344556677"); |
|
combo.setEditable(true); |
|
combo.setEditor(new HTMLComboBoxEditor()); |
|
//добавляем список в окно setLayout(new FlowLayout()); add(combo);
//кнопка для добавления нового элемента в список
JButton addButton = new JButton("Добавить"); addButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { combo.addItem(combo.getSelectedItem());
}
});
add(addButton);
//выводим окно на экран
setSize(330, 200); setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new HTMLComboEditorTest(); } });
}
}}
Мы создаем раскрывающийся список, используя для записи элементов HTML-код, и устанавливаем для списка новый объект для редактирования (не забывая включить редактирование свойством editable). Обратите внимание на маленький нюанс: длина
Списки |
337 |
элемента раскрывающегося списка с помощью свойства prototypeCellValue делается довольно большой. Этому есть простое объяснение: HTML-текст, выводимый в выпадающем меню, выглядит гораздо компактнее, чем в редакторе JEditorPane, и если не сделать размер списка побольше (с помощью подходящего менеджера расположения или, как в примере, с помощью свойства prototypeCellValue), некоторые элементы при редактировании рискуют оказаться на нескольких строках, а это может быть неожиданностью для пользователя. Запустив программу с примером, вы сможете оценить новый объект для редактирования. Без сомнения, он прибавляет раскрывающемуся списку красок, к тому же с ним вы можете использовать для элементов все возможности HTML, не опасаясь неприятностей при редактировании.
Пример также демонстрирует, как получать только что набранную пользователем информацию, если выпадающий список редактируемый. При потере списком фокуса ввода (переходе его на другие компоненты) значение из редактора автоматически становится текущим элементом списка, которое мы можем получить методом getSelectedItem(). Чтобы убедиться в этом, мы тут же добавляем новое, только что набранное, значение в сам список. Но, как мы уже упоминали, наш редактор не поддерживает события ActionEvent, поэтому если вы хотите получить текущее значение из редактора без потери фокуса ввода списком (к примеру, проверяя его на корректность), лучше воспользоваться методом редактора getItem().
Аналогичным образом вы сможете применять для редактирования и другие компоненты, хотя чаще всего все сводится к использованию тех или иных текстовых компонентов Swing. Так вы сможете хранить, отображать и редактировать данные любых типов, и сделаете свои раскрывающиеся списки по-настоящему приятными в работе, а комфортная работа с пользовательским интерфейсом — одна из основных задач при его создании.
События раскрывающегося списка
Раскрывающийся список JComboBox обладает куда более интересным набором событий в сравнении с обычным списком JList, где они происходят в основном в моделях. Прежде всего стоит обратить внимание на событие ItemEvent, которое происходит при смене выбранного элемента списка. С помощью этого события можно узнать не только о том, что выбранный элемент сменился, но и быстро получить данный элемент, а также узнать, какой элемент был выбран перед ним. Далее следует уже знакомое нам событие ActionEvent, которое должно сообщать о новом выбранном элементе или конце редактирования (если список допускает редактирование). Правда, после написания собственных редакторов для раскрывающегося списка мы видели, что правильная работа этого
338 |
ГЛАВА 11 |
события в значительной степени зависит от реализации объекта для редактирования, который может и не поддерживать слушателей окончания ввода (а раскрывающийся список присоединяет слушателей ActionListener именно к объекту для редактирования). При использовании нестандартных редакторов следует иметь это в виду и при необходимости применять для подтверждения ввода отдельные элементы интерфейса (как правило, кнопки).
Применение событий раскрывающегося списка проиллюстрирует небольшой пример:
//ComboBoxEvents.java
//События раскрывающихся списков import javax.swing.*;
import java.awt.event.*; import java.awt.*;
public class ComboBoxEvents extends JFrame { // данные для списков
private String[] data = { "США", "Италия", "Швейцария", "Таиланд" };
public ComboBoxEvents() { super("ComboBoxEvents"); setDefaultCloseOperation(EXIT_ON_CLOSE); // первый список
JComboBox combo1 = new JComboBox(data); // слушатель смены выбранного элемента
combo1.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) {
// выясняем, что случилось if ( e.getStateChange() == ItemEvent.SELECTED ) {
// покажем выбранный элемент
Object item = e.getItem(); JOptionPane.showMessageDialog(
ComboBoxEvents.this, item);
}
}
}); // список, позволяющий редактирование
final JComboBox combo2 = new JComboBox(data); combo2.setEditable(true);
// слушатель окончания редактирования combo2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Списки |
339 |
// покажем выбор пользователя
Object item = combo2.getModel(). getSelectedItem();
JOptionPane.showMessageDialog( ComboBoxEvents.this, item);
}
});
//добавим списки в окно setLayout(new FlowLayout()); add(combo1);
add(combo2);
//выведем окно на экран setSize(350, 250); setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new ComboBoxEvents(); } });
}
}
В примере мы создаем пару раскрывающихся списков, используя в качестве данных массив строк String. Один из списков будет поддерживать редактирование, именно к нему мы присоединяем слушателя событий ActionListener, чтобы узнавать об окончании редактирования (здесь задействован стандартный объект для редактирования, так что это событие будет обрабатываться). К первому списку мы присоединяем слушателя событий ItemListener, метод itemStateChanged() этого слушателя будет вызываться при смене выбранного элемента списка, причем происходить такой вызов будет два раза: первый раз событие сообщит о том, что прежде выбранный элемент больше таковым не является, а второй раз оно позволит узнать все о новом выбранном элементе. Различить два типа события позволяет метод getStateChange(), возвращающий целое число, которое и указывает на тип события: первому типу соответствует константа DESELECTED из класса ItemEvent, второму типу соответствует константа SELECTED. Нас интересует выбранный элемент, так что мы выбираем событие типа SELECTED. Далее остается получить выбранный элемент методом getItem() и вывести его на экран (используя стандартное диалоговое окно JOptionPane, подробнее о нем рассказывается в главе 14). Экспериментируя с примером, попробуйте выбрать уже выбранный элемент — событие ItemEvent при этом не рассылается, хотя вы заново выбираете его в списке. Это некоторого рода оптимизация.
Ко второму списку (тому, что допускает редактирование) мы присоединяем слушателя ActionListener, он дает возможность узнать, когда пользователь заканчивает ввод нового значения, или когда он выбирает его из списка. После работы со вторым раскрывающимся списком вы убедитесь в том, что слушатель ActionListener оповещается не только об окончании ввода (окончанием ввода считается нажатие клавиши Enter в поле для редактирования), но и о смене выбранного элемента. В слушателе мы, как и для события ItemEvent, показываем новый выбранный элемент в стандартном диалоговом окне