Таблицы |
535 |
dsn = "jdbc:odbc:Library", uid = "",
pwd = "";
public static void main(String[] args) throws Exception {
//инициализация JDBC Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
//объект-соединение с БД
Connection conn = DriverManager.getConnection(dsn, uid, pwd); Statement st = conn.createStatement();
// выполняем запрос
ResultSet rs = st.executeQuery( "select * from readers.csv");
// наша модель
final DatabaseTableModel dbm =
new DatabaseTableModel(true);
//считываем данные в таблицу dbm.setDataSource(rs);
//таблица и окно
SwingUtilities.invokeLater( new Runnable() {
public void run() {
JTable table = new JTable(dbm);
JFrame frame = new JFrame("DatabaseTable"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 300);
frame.add(new JScrollPane(table)); frame.setVisible(true);
}});
rs.close();
conn.close();
}
}
Впрограмме мы непосредственно в методе main() создаем небольшое окно JFrame
иразмещаем в нем таблицу, которая будет получать данные из только что созданной модели для работы с базами данных. Конечно же, мы работаем с компонентами из потока рассылки событий, так как хорошо знаем правила Swing. Для правильной работы модели нам нужно настроить и открыть подключение к базе данных JDBC. Здесь используется все то же подключение к базе данных некой гипотетической библиотеки, мы уже применяли его в главе 11, когда создавали модели списков JList и JComboBox для работы
с базами данных. В качестве драйвера задействован стандартный мост JDBC-ODBC, годящийся в большинстве случаев жизни. Для работы программы с вашей базой данных вам нужно сменить лишь идентификатор базы данных, имя пользователя и пароль, а также, если это необходимо, драйвер JDBC, в том случае если вы используете специальную базу данных с собственным драйвером. Открыв соединение с базой данных, программа вы-
536 |
ГЛАВА 17 |
полняет запрос (в примере запрос получает список всех читателей библиотеки), и если последний успешен, то результат запроса возвращается в виде объекта ResultSet. Заметьте, что мы передаем объект ResultSet нашей модели еще до того, как таблица присоединяется к ней, добавляется в окно, и последнее выводится на экран. Это позволяет нам загрузить данные из отдельного потока выполнения метода main(), так как таблица пока не обращается к модели. В противном случае, если модель уже присоединена к таблице, необходимо выполнять все действия из потока рассылки событий, иначе конфликты неизбежны.
Запустив программу с примером, предварительно настроив любую вашу базу данных
ивыполнив запрос по получению любых, даже самых сложных данных, вы увидите, что они правильно появляются в таблице, и структура таблицы имеет нужный вид (название
итип столбцов), несмотря на всю простоту написанной нами модели. Действительно, JDBC и таблица JTable словно созданы друг для друга.
Конечно, рассмотренная нами модель на самом деле простовата для работы с реальными базами данных, в которых, как правило, хранится информация впечатляющего размера. Она будет занимать слишком много места в памяти, так как вычитывает всю таблицу в списки и будет вызывать замедление в интерфейсе, поскольку процесс считывания данных необходимо выполнять в потоке рассылки событий одним вызовом, что на время остановит прорисовку и отзывчивость интерфейса. Можно развить наш пример в различных направлениях:– например, вычитывать ограниченный набор данных, видимый в данный момент на экране, а при прокрутке таблицы читать недостающие строки, либо применять объект ResultSet с поддержкой прокрутки по записям таблицы в произвольном направлении, что позволит вообще не хранить данных в модели, но может быть не слишком производительным. Эта увлекательная тема, однако, относится скорее к работе с базами данных, чем к библиотеке Swing. Способы добиться максимальной производительности мы уже неоднократно обсуждали при работе с другими сложными компонентами Swing, списками JList и деревьями JTree — точечное обновление данных, работа с блоками данных и взаимодействие с потоком рассылки событий вы можете найти в главах 11 и 15. Все эти техники в одинаковой мере относятся ко всем ситуациям с большим количеством данных в модели.
Мы можем подвести предварительный итог: основная модель таблицы TableModel удивительно изящна и гибка; она позволяет быстро описать даже самые сложные двухмерные данные с учетом всей их специфики. Удачной следует признать и концепцию типа столбца (представленного объектами Class) — вы можете быстро указать, как следует отображать (и при необходимости редактировать) данные этого столбца, потому что при наличии встроенной поддержки таких фундаментальных типов, как значки, булевы значения и числа, таблица даже без специализированной настройки становится мощным инструментом в руках создателя пользовательского интерфейса.
Таблицы |
537 |
Модель столбцов таблицы
Столбцы таблицы JTable обладают по-настоящему гибким поведением: вы можете менять их размеры, менять их местами друг с другом, настраивать объекты для отображения в столбце и для редактирования ячеек столбца, тонко управлять заголовком столбца и делать многое другое — все аспекты поведения столбцов таблицы настраиваются. В тоже время в модели данных таблицы TableModel столбцы представлены только своим названием и типом хранящихся в них данных. Остальная информация о столбцах, а ее немало, хранится в специальной предназначенной специально для них модели
TableColumnModel.
Модель TableColumnModel отвечает за то, какие столбцы, в каком порядке и с какими параметрами выводит на экран таблица JTable. Грубо говоря, это специализированный контейнер данных, который хранит список столбцов таблицы в том порядке, в каком они представлены на экране (в отличие от модели данных TableModel, в которой порядок следования столбцов всегда один и тот же независимо от того, как столбцы располагаются на экране). Каждый столбец представлен объектом TableColumn. С помощью этого объекта вы можете указать размеры столбца (у столбца, как и у обычного компонента, может быть предпочтительный, максимальный и минимальный размеры), индекс данного столбца в модели данных TableModel (по этому индексу таблица определяет, какой набор данных отображает столбец), объекты для отображения и редактирования данных, хранящихся в столбце (об этих объектах мы вскоре поговорим подробнее, когда доберемся до детального обсуждения внешнего вида ячеек таблицы), и настроить многие другие параметры, в том числе и модель выделения столбцов.
По умолчанию в таблице для хранения информации о столбцах используется стандартная модель DefaultTableColumnModel, и ее возможностей хватает в подавляющем большинстве ситуаций. Работа модели столбцов таблицы не слишком сложна: она хранит список столбцов TableColumn и при смене какого-либо свойства столбцов или их расположения оповещает об этом событии слушателей TableColumnModelListener. Стандартная модель прекрасно справляется с оповещением слушателей, а столбцы хранит в динамическом массиве (векторе) Vector. Как правило, писать собственную модель столбцов таблицы «с нуля» или наследуя от стандартной модели не имеет особого смысла — стандартная модель позволяет динамически манипулировать столбцами и проводить с ними все необходимые операции. Также несложно с помощью слушателей TableColumnModelListener постоянно быть в курсе всех изменений в структуре столбцов таблицы.
А сейчас в несложном примере рассмотрим основные возможности стандартной модели столбцов таблицы и объектов TableColumn:
//UsingTableColumnModel.java
//Использование стандартной модели столбцов
//таблицы и объектов TableColumn
import javax.swing.*; import javax.swing.table.*; import java.awt.event.*; import java.awt.*;
import java.util.Enumeration;
public class UsingTableColumnModel extends JFrame { // модель столбцов
private TableColumnModel columnModel; // названия столбцов таблицы
538 |
ГЛАВА 17 |
private String[] columnNames = { "Имя", "Любимый цвет", "Напиток"
}; // данные для таблицы
private String[][] data = {
{"Иван", "Зеленый", "Апельсиновый сок"},
{"Александр", "Бежевый", "Зеленый чай"}
};
public UsingTableColumnModel() { super("UsingTableColumnModel"); setDefaultCloseOperation(EXIT_ON_CLOSE); // наша таблица
JTable table = new JTable(data, columnNames);
//получаем стандартную модель columnModel = table.getColumnModel();
//меняем размеры столбцов
Enumeration e = columnModel.getColumns(); while ( e.hasMoreElements() ) {
TableColumn column = (TableColumn)e.nextElement();
column.setMinWidth(30);
column.setMaxWidth(90);
}
// создадим пару кнопок
JPanel buttons = new JPanel();
JButton move = new JButton("Поменять местами"); move.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { // меняем местами первые два столбца columnModel.moveColumn(0, 1);
}
});
buttons.add(move);
JButton add = new JButton("Добавить"); add.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { // добавляем столбец
TableColumn newColumn =
new TableColumn(1, 100); newColumn.setHeaderValue("<html><b>Новый!"); columnModel.addColumn(newColumn);
Таблицы |
539 |
}
});
buttons.add(add);
// выводим окно на экран add(new JScrollPane(table)); add(buttons, "South"); setSize(400, 300); setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new UsingTableColumnModel(); } });
}
}
Мы создаем небольшое окно, в центре которого будет располагаться небольшая таблица JTable. Таблицу мы создаем на основе двухмерного массива с данными и массива с названиями столбцов. Все это нам уже прекрасно знакомо. Дальше интереснее: мы получаем стандартную модель столбцов таблицы методом getColumnModel() и можем приступать к работе с ней. Прежде всего, обратите внимание на то, как модель столбцов позволяет получить перечисление (Enumeration) имеющихся в данный момент в таблице столбцов и изменить их размеры. По умолчанию минимальным размером для любого столбца является 15 пикселов, предпочтительным — 75 пикселов, а максимальный размер его не ограничен. Однако столбцы у таблиц бывают разные, и эти значения на все случаи жизни не подходят, так что нет ничего удивительного в том, что чаще всего работа со столбцами сводится к настройке их размеров. В примере у нас нет особых требований к столбцам, так что мы можем сделать их любого понравившегося нам размера, видно, как меняются минимальный и максимальный размеры столбцов. Запустив программу с примером, вы сразу оцените разницу: если раньше свободное пространство в таблице было бы равномерно распределено между столбцами, то теперь, благодаря ограниченному максимальному размеру, свободное место просто ничем не занято — столбцы никогда не становятся длиннее своего максимального размера (и соответственно, короче минимального).
Далее пример показывает, каким образом модель выделения столбцов (в лице ее стандартной реализации) позволяет динамически манипулировать столбцами таблицы. В южную часть окна добавляется панель с двумя кнопками, к каждой из которых присоединен слушатель ActionListener. Первая кнопка меняет местами первый и второй столбцы таблицы, специально для этого в модели имеется удобный метод move(). Еще в самом первом примере данной главы мы отметили, что по умолчанию таблица позволяет нам мышью «тасовать» столбцы, меняя их местами. Подобное поведение обеспечивает именно модель столбцов TableColumnModel, и мы видим, что совсем нетрудно переставлять столбцы программно. Вторая кнопка показывает, как можно добавить в таблицу еще один столбец, даже если в модели основных данных TableModel этот столбец и не присутствует. В слушателе создается объект TableColumn, инкапсулирующий информацию о столбце таблицы. Мы использовали конструктор, который требует указать индекс столбца в модели данных TableModel (это обязательно, и такой столбец в модели данных должен присутствовать, иначе неизбежны исключения — столбцу будет нечего показывать) и предпочтительный размер столбца (остальные размеры задаются по