Таблицы |
565 |
super("CustomTableEditor"); setDefaultCloseOperation(EXIT_ON_CLOSE);
// создаем таблицу на основе стандартной модели
DefaultTableModel model =
new DefaultTableModel(data, columns) { // необходимо указать тип столбца
public Class getColumnClass(int column) { return data[0][column].getClass();
}
};
JTable table = new JTable(model); table.setRowHeight(20);
//указываем редактор для дат table.setDefaultEditor(
Date.class, new DateCellEditor());
//выводим окно на экран
getContentPane().add(new JScrollPane(table)); setSize(400, 300);
setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new CustomTableEditor(); } });
}
}
Здесь мы создаем таблицу JTable на основе стандартной модели, эта таблица затем разместится в панели прокрутки, которая, в свою очередь, разместится в центре панели содержимого окна. Данные для таблицы, как это уже не раз бывало, находятся в двух массивах: одномерный массив columns содержит названия столбцов, а двухмерный data — непосредственно данные ячеек. Обратите внимание, что второй столбец содержит даты, представленные объектами Date. Оба массива мы передаем в специальный конструктор стандартной модели DefaultTableModel и здесь же переопределяем метод модели getColumnClass(). Это необходимо для того, чтобы указать, что второй столбец нашей таблицы хранит объекты типа Date. Если мы этого не сделаем, то наш новый редактор не будет задействован для их редактирования. Переопределять метод isCellEditable() нам не потребовалось: стандартная модель по умолчанию считает, что редактировать можно все ячейки.
566 |
ГЛАВА 17 |
Далее мы регистрируем в таблице созданный нами новый редактор по умолчанию в качестве редактора для столбцов с данными типа Date. Видно, что для этого предназначен метод setDefaultEditor(). В дополнение ко всему высота ячеек таблицы немного увеличивается — это позволит счетчику JSpinner чувствовать себя в них более комфортно, поэтому им будет удобнее пользоваться. Запустив программу с примером, вы увидите, что первый столбец таблицы редактируется посредством обычного текстового поля, а вот для редактирования второго применяется наш новый редактор на основе счетчика JSpinner. Без всякого сомнения, это во много раз повышает удобство работы с датами в таблицах. Аналогичным образом вы сможете написать редакторы и для остальных нестандартных типов данных, редактировать которые средствами стандартных редакторов по тем или иным причинам неудобно.
Заголовок таблицы JTableHeader
Заголовок таблицы, реализованный в классе JTableHeader из пакета javax.swing.table, отображает названия столбцов, а также позволяет перетаскивать столбцы, меняя их местами, и менять их размеры. Мы уже знаем, что таблица JTable автоматически добавляет заголовок таблицы в качестве верхнего заголовка панели прокрутки, если вы добавляете таблицу в панель прокрутки. Если таблица будет размещена в любом другом контейнере, места для заголовка таблицы не найдется, поэтому подыскивать его придется самостоятельно.
Заголовок таблицы представляет собой самый обыкновенный компонент со своим UI-представителем, отвечающим за его внешний вид. Данные для работы заголовок таблицы черпает из модели столбцов таблицы TableColumnModel, которую необходимо передавать в конструктор класса JTableHeader или присоединять к заголовку позже. Фактически, заголовок таблицы выступает как дополнительный вид модели столбцов (наряду с таблицей JTable) — он позволяет перетаскивать столбцы и менять их размеры, меняя данные в модели. Никаких данных в самом заголовке не хранится — все находится в модели TableColumnModel (рассматривая модели столбцов TableColumnModel и объектов TableColumn мы уже выяснили, что именно в них хранится исчерпывающая информация о столбцах, в том числе и об их размерах и порядке следования на экране).
Со стандартным заголовком таблицы можно сделать немногое, но все предоставляемые нам возможности очень полезны и весьма часто востребованы. Для начала, вы можете включить или отключить перетаскивание столбцов и смену их размеров. Далее, заголовок таблицы обладает собственным отображающим объектом, который и отвечает за то, как выглядит название столбца и его оформление. По умолчанию отображающий объект для заголовка таблицы предоставляет UI-представитель класса JTableHeader, но это легко изменить, придав тем самым заголовку вашей таблицы уникальный узнаваемый вид (тем более, что мы хорошо знакомы с процессом написания отображающих объектов). Рассмотрим небольшой пример, в котором присоединим к заголовку таблицы собственный отображающий объект, а заодно настроим несколько дополнительных свойств заголовка:
//UsingTableHeader.java
//Настройка заголовка таблицы JTableHeader import javax.swing.*;
import javax.swing.table.*; import javax.swing.border.*; import java.awt.*;
public class UsingTableHeader extends JFrame { // данные для таблицы
Таблицы |
567 |
private String[][] data = {
{"Июнь", "+18 С" },
{"Июль", "+22 С" },
{"Август", "+19 С" }
}; // названия столбцов
private String[] columnNames = { "Месяц", "Средняя температура"
};
public UsingTableHeader() { super("UsingTableHeader"); setDefaultCloseOperation(EXIT_ON_CLOSE); // создаем таблицу
JTable table = new JTable(data, columnNames);
//настраиваем заголовок таблицы
JTableHeader header = table.getTableHeader(); header.setReorderingAllowed(false); header.setResizingAllowed(false);
//присоединяем отображающий объект header.setDefaultRenderer(new HeaderRenderer());
//добавляем таблицы в панель прокрутки
add(new JScrollPane(table)); setSize(400, 300); setVisible(true);
}
// специальный отображающий объект для заголовка class HeaderRenderer
extends DefaultTableCellRenderer {
// метод возвращает компонент для прорисовки public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
//получаем настроенную надпись от базового класса
JLabel label = (JLabel)super. getTableCellRendererComponent(table,
value, isSelected, hasFocus, row, column);
//настраиваем особую рамку и цвет фона label.setBackground(Color.gray); label.setBorder(border);
return label;
568 |
ГЛАВА 17 |
|
} |
|
private Border border = BorderFactory. |
|
createMatteBorder(16, 16, 16, 16, |
|
new ImageIcon("bullet.png")); |
|
} |
|
public static void main(String[] args) { |
|
SwingUtilities.invokeLater( |
|
new Runnable() { |
|
public void run() { new UsingTableHeader(); } }); |
} |
} |
|
В примере мы создаем небольшое окно, в центре которого разместится панель прокрутки с расположенной в ней таблицей JTable. Таблицу мы создаем на основе двух массивов: двумерный массив data хранит данные ячеек, а одномерный columnNames — названия столбцов. После создания таблицы мы получаем ее заголовок (для этого предназначен метод getTableHeader()) и принимаемся за его настройку. Для начала меняются два наиболее полезных свойства заголовка: свойство reorderingAllowed контролирует, будет ли доступна возможность перетаскивания столбцов, а свойство resizingAllowed позволяет включить (или, как в нашем примере, отключить) возможность смены размеров столбцов. Напоследок мы устанавливаем для заголовка свой отображающий объект (присоединить его позволяет метод setDefaultRenderer()). Собственно отображающий объект реализован во внутреннем классе HeaderRenderer и не представляет собой ничего хитрого: он унаследован от стандартного отображающего объекта DefaultTableCellRenderer, на который и перекладывается большая часть работы. К полученной в результате работы стандартного объекта надписи (а вы помните, что стандартный отображающий объект представляет собой именно надпись JLabel) прибавляется необычная рамка, составленная на основе небольшого значка.
После запуска программы с примером вы оцените настройку заголовка таблицы: помимо его необычного и красочного внешнего вида вы не сможете поменять столбцы местами или изменить их размеры. Только не забудьте, что стандартный заголовок таблицы применяется для операций сортировки и прорисовывает направление сортировки. Если в таблице включена сортировка и вам необходимо использовать нестандартный заголовок, придется позаботиться о прорисовке значка сортировки самостоятельно, узнавая об изменениях в сортировке с помощью слушателя объекта RowSorter.
Очень часто заголовок таблицы JTableHeader применяется для добавления к таблице операций для конкретного столбца. Для этого к заголовку таблицы присоединяется слушатель событий от мыши, следящий за щелчками на заголовках столбцов. После щелчка
Таблицы |
569 |
легко определить, на каком именно столбце он произошел (вызвав метод columnAtPoint() из класса JTableHeader), получить данные этого столбца (в этом поможет модель данных TableModel), как-либо применить их и дать сигнал таблице о том, что данные изменились и вид пора обновить, если это необходимо.
Резюме
Разнообразная двухмерная информация, особенно полученная из баз данных, их отображений на объекты средствами, подобными iBatis и Hibernate, или из компонентов Enterprise JavaBeans, — частый гость в приложениях. Благодаря таблице JTable, ее без преувеличения фантастической гибкости и безграничным возможностям настройки, вывод этой информации не станет для вас проблемой. Как мы убедились, читая материал этой главы, с таблицей JTable можно делать практически все. Чем больше знакомишься с компонентом JTable, тем более поражаешься глубинам его возможностей и убеждаешься в том, что таблица JTable иллюстрирует саму суть Swing, позволяя с элегантной простотой и мощью выводить на экран любые данные.