330 |
ГЛАВА 11 |
}
}
Роль базы данных у нас играет некоторая гипотетическая база данных JDBC для библиотеки книг без имени пользователя и пароля. Для присоединения к ней нам нужно, прежде всего, загрузить драйвер JDBC, мы задействуем стандартный драйвер JDBCODBC, поставляющийся вместе с пакетом JDK. После загрузки драйвера мы можем воспользоваться услугами класса DriverManager для создания соединения Connection с базой данных, и в случае успеха через созданное соединение выполнять запросы и получать данные. Полученные в результате запроса данные (в виде объекта ResultSet) мы передаем в нашу новую модель DatabaseComboBoxModel (дополнительно нужно указать номер столбца, данные которого станут элементами списка), после чего остается присоединить ее к раскрывающемуся списку, добавить последний в окно и вывести его на экран. Для запуска программы со своей базой данных вам нужно соответствующим образом изменить название базы данных, имя пользователя и пароль, указать другой драйвер, если это необходимо для подключения к вашей базе данных, и выбрать таблицу, в которой имеется столбец с текстовыми данными, указав ее в тексте запроса. Запустив правильно настроенную программу с примером, вы сможете увидеть, как раскрывающийся список выводит элементы из базы данных, на самом деле даже и не подозревая об этом: все детали по сбору и обработке данных, как это и должно быть, остаются в модели. Не забудьте, что манипулировать любыми моделями в Swing после их присоединения к компонентам можно только из потока рассылки событий, так как они напрямую влияют на визуальное состояние компонентов.
Продуманноеиспользованиеспециальныхмоделей,такихкакDatabaseComboBoxModel, придает вашему приложению удивительную гибкость и легкость в расширении и поддержке. Вы сможете тщательно отделить части, работающие с данными (это части, соединяющиеся с базами данных и передающие результаты моделям), от частей, заведующих пользовательским интерфейсом (это компоненты пользовательского интерфейса, отображающие данные моделей и незаметно для вас позволяющие пользователю полностью изменять их, если это необходимо). Одну и ту же модель можно задействовать в нескольких частях сложного пользовательского интерфейса, не заботясь о правильном отображении данных, а при обработке итоговых данных знать, что все нужное скрыто именно в ней.
Внешний вид списка
Внешний вид элементов, хранящихся в раскрывающемся списке JComboBox, также легко и полно настраивается, как и внешний вид элементов обычного списка JList. Более того, для вывода элементов обоих списков используется отображающий объект ListCellRenderer, знакомый нам по списку JList. На самом деле, было бы странно вводить для раскрывающегося списка новый отображающий объект — как и в случае со списком обычным, от него требуется лишь вывести некоторый элемент, находящийся на определенной позиции списка (про этот элемент также известно, выделен ли он и есть ли у него фокус ввода). Более того, список элементов в выпадающем списке выводит именно обычный список JList, обернутый в панель прокрутки и выведенный на экран во всплывающем окне.
Таким образом, все, что мы говорили про внешний вид обычных списков JList, в равной степени относится и к раскрывающимся спискам JComboBox. Поэтому мы со спокойной душой можем считать, что уже в совершенстве владеем самой изощренной техникой отображения элементов раскрывающегося списка (если вы пропустили раздел, посвященный внешнему виду списков JList, самое время к нему вернуться). Давайте рассмотрим пример, в котором используем возможности как стандартного отобра-
Списки |
331 |
жающего объекта списков DefaultListCellRenderer, так и нашего специального объекта ImageListCellRenderer, способного отображать одновременно значки и текст. Итак:
//ComboBoxRendering.java
//Отображение элементов раскрывающегося списка import javax.swing.*;
import com.porty.swing.*;
import java.awt.*;
public class ComboBoxRendering extends JFrame { // данные для первого списка
private String[] textData = { "<html><code>Первый", "<html><b>Жирный", "<html><font color=red>Красный", "<html><em>Выразительный" };
// значки
private |
ImageIcon bullet1 = |
new |
ImageIcon("Server16.gif"); |
private |
ImageIcon bullet2 = |
new |
ImageIcon("Host16.gif"); |
// данные для второго списка со значками |
|
private |
ImageListElement[] iconData = { |
new |
ImageListElement(bullet1, "Основной Сервер"), |
new |
ImageListElement(bullet1, "Дополнительный"), |
new |
ImageListElement(bullet2, |
|
"<html><b><em>Машина директора") |
};
public ComboBoxRendering() { super("ComboBoxRendering"); setDefaultCloseOperation(EXIT_ON_CLOSE);
//создаем пару раскрывающихся списков
JComboBox combo1 = new JComboBox(textData); JComboBox combo2 = new JComboBox(iconData);
//наш специальный отображающий объект combo2.setRenderer(new ImageListCellRenderer());
//добавляем списки в окно
setLayout(new FlowLayout()); add(combo1);
add(combo2);
// выводим окно на экран setSize(350, 200); setVisible(true);
332 |
ГЛАВА 11 |
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new ComboBoxRendering(); } });
}
}
В данном примере мы создаем два несложных раскрывающихся списка JComboBox, используя для хранения данных два массива textData и iconData. Первый список, создаваемый на основе массива строк textData, демонстрирует возможности стандартного отображающего объекта списков DefaultListCellRenderer. Как вы помните, стандартный отображающий объект унаследован от обычной надписи JLabel и способен отображать не только обычный текст, но и текст в формате HTML, а также значки. Значки мы для первого списка не используем, оставляя их для списка второго, а вот возможности HTML применяем «что есть сил», так что элементы первого списка спутать друг с другом будет непросто. Второй список использует в качестве данных объекты ImageListElement, созданные нами в разделе «Списки JList» и позволяющие хранить вместе и значок, и текст элемента списка, причем текст все также может быть приукрашен всеми доступными нам средствами HTML. Не забывайте только при применении объектов ImageListElement устанавливать подходящий отображающий объект ImageListCellRenderer
методом setRenderer()9.
После создания списков и незамысловатой настройки мы добавляем их в окно и выводим его на экран. Запустив программу с примером, вы сможете убедиться, что элементы списка теперь выглядят совсем иначе.
У вашей фантазии нет никаких ограничений: все, что связано с выводом элементов списка на экран, прорисовывается в отображающем объекте. Написав собственный отображающий объект, примерно так, как мы это сделали для объекта с текстом и значками ImageListCellRenderer, вы сможете любым образом расцветить свои списки. Более того, отображающие объекты «развязывают вам руки» при работе с моделями списка, позволяя хранить в них данные совершенно произвольных типов, даже самых сложных и запутанных. Предоставив в дополнение к таким данным отображающий объект, который знает, что именно из этих данных следует вывести на экран, вы получаете возможность
9 И снова путаница: почему в обычном списке метод для смены отображающего объекта называется setCellRenderer(), а в списке JComboBox — setRenderer()? Похоже, что при создании двух похожих списков их разработчики не удосужились как следует скооперироваться. К счастью, в библиотеке Swing таких «проколов» мало, и работать с ней все же приятно.
Списки |
333 |
использовать модель не только как «подносчик снарядов» для отображения элементов списка, но и как универсальное хранилище любых данных (которые к тому же легко разделять между видами и показывать пользователю).
Редактирование
Как мы уже знаем, раскрывающийся список JComboBox позволяет пользователю не только выбирать что-либо в списке заранее имеющихся альтернатив, но и вводить собственные значения — это его основное отличие от обычного списка JList и его основное преимущество. Правда, по умолчанию раскрывающийся список редактирования не допускает, но в нескольких предыдущих примерах мы уже увидели, как включить для списка режим редактирования: этим управляет свойство editable. Установив последнее в true, вы предоставляете пользователю возможность самого широкого выбора: он сможет либо выбрать один из представленных в списке элементов, либо ввести собственное значение. Обратите внимание на маленький нюанс: редактировать элементы раскрывающегося списка нельзя, они всегда остаются теми, какими были при создании списка, можно лишь дополнительно к ним вводить новые значения.
Редактированием нового значения в списке JComboBox занимается отдельный объект, обязанности его описаны в интерфейсе ComboBoxEditor. Стандартная реализация этого интерфейса — класс с названием BasicComboBoxEditor из пакета javax.plaf.basic. В данном классе для редактирования применяется простое текстовое поле JTextField, которое прекрасно подходит для обычных строк10. Чаще всего возможностей стандартного объекта для редактирования вполне достаточно для обычных раскрывающихся списков, не использующих в качестве элементов какие-либо экзотические данные. Вы могли убедиться в этом на предыдущих примерах, где в дополнение к имеющимся в списке строкам можно было ввести собственную произвольную строку текста. Увы, но стандартный объект для редактирования хорошо справляется лишь с простыми строками: стоит только элементам списка стать немного сложнее, как идиллия нарушается. Даже такая несложная возможность, как обработка HTML-текста, уже не поддерживается стандартным объектом, который выводит его в виде кода с тегами, а внезапное предложение заняться написанием HTML-текста вручную вряд ли восхитит пользователя. Поэтому, если вы храните в раскрывающемся списке нестандартные данные, особым образом показываете их (даже если это «до боли» знакомый нам HTML-текст) и планируете сделать список редактируемым, придется заняться написанием своего объекта для редактирования, благо это не слишком сложно. В качестве варианта можно попробовать потоньше настроить стандартный отображающий объект BasicComboBoxEditor, «вырезая» из элемента списка все HTML-теги при передаче его для редактирования. Для этого будет достаточно унаследовать от него и обрабатывать проходящие через редактор элементы.
Впрочем, никто не заставляет нас оставаться в рамках стандартного объекта для редактирования и лишь немного настраивать его работу. Совсем несложно написать собственный объект для редактирования с нуля; давайте так и поступим, предоставив в качестве альтернативы простому HTML-коду, «очищенному» от тегов, объект для редактирования, позволяющий редактировать именно HTML-текст, со всеми его красочными атрибутами и широкими возможностями. С редактированием HTML нам поможет текстовый компонент JEditorPane, специально для этого и предназначенный. Нам остается только «втиснуть» его в рамки интерфейса ComboBoxEditor. Оказывается, это очень просто. Итак:
10 Нетрудно понять, что объект для редактирования занимается не только собственно редактированием, но и отображает выбранный элемент, постоянно находясь на экране. За отображение остальных элементов, находящихся в выпадающем меню, как и прежде отвечает объект
ListCellRenderer.
334 |
ГЛАВА 11 |
//com/porty/swing/HTMLComboBoxEditor.java
//Полнофункциональный редактор для списка
//JComboBox, использующего HTML
package com.porty.swing; import javax.swing.*; import java.awt.*; import java.awt.event.*;
public class HTMLComboBoxEditor implements ComboBoxEditor {
// редактор для HTML
private JEditorPane htmlEditor; public HTMLComboBoxEditor() {
htmlEditor =
new JEditorPane("text/html", ""); htmlEditor.setBorder(BorderFactory.
createEtchedBorder());
}
// возвращает редактор
public Component getEditorComponent() { return htmlEditor;
}
//сигнал выбрать весь текст и приступить
//к редактированию
public void selectAll() { htmlEditor.selectAll(); htmlEditor.requestFocus();
}
//возвращает редактируемый элемент public Object getItem() {
return htmlEditor.getText();
}
//получает новый элемент для редактирования public void setItem(Object item) {
htmlEditor.setText(item.toString());
}
//методы для присоединения слушателей
//ActionListener, который оповещаются
//об окончании редактирования
public void addActionListener( ActionListener e) { }