Материал: Портянкин И. Swing

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Таблицы

525

public void run() { new UsingDefaultTableModel(); } });

}

}

В примере мы наследуем наш класс от окна с рамкой JFrame и размещаем в панели содержимого окна таблицу JTable, которая получает данные из созданной нами стандартной модели, а также две кнопки, позволяющие динамически, непосредственно во время работы программы, изменять данные модели (именно модели, код в теле слушателей не знает о существовании пользовательского интерфейса программы и о деталях таблицы JTable, он просто работает с данными, хранящимися в стандартной модели). Прежде всего создается сама стандартная модель DefaultTableModel. Мы использовали конструктор без параметров, а это означает, что после создания модель хранит нулевое количество строк и столбцов. Далее мы добавляем в модель значения строк методом addRow(), в который передаем массив со значениями, хранящимися в строке. Задействовать можно не только массив; перегруженная версия метода addRow() обеспечивает добавление строки, данные которой хранятся в векторе Vector. Заметьте, что перед добавлением в модель новых строк мы вызвали метод setColumnIdentifiers(), позволяющий задать названия (а значит и количество) столбцов таблицы. Если вызвать этот метод после добавления в модель DefaultTableModel новых строк, он посчитает, что в модели хранится нулевое количество столбцов (добавление строк не меняет хранимое в модели количество столбцов) и просто удалит все уже добавленные нами строки. Правда, можно задать количество столбцов с помощью метода setColumnCount(), не указывая их названий. Если вы вызовете этот метод перед добавлением в модель строк, последующий вызов метода setColumnIdentifiers() не удалит добавленные строки. Это общее правило работы со стандартной моделью таблицы DefaultTableModel: при вызове методов, изменяющих количество строк или столбцов, данные могут теряться, так что нужно следить за тем, чтобы новое количество строк или столбцов было достаточным для хранимых в модели данных, и заранее заботиться о правильности структуры таблицы.

После добавления в модель всех нужных данных мы передаем ее таблице JTable, которая затем разместится в панели прокрутки JScrollPane в центре панели содержимого (вы помните, что по умолчанию расположение в панели содержимого полярное). Далее создается пара кнопок add и remove, позволяющих динамически манипулировать данными модели. Мы присоединяем к ним слушателей и при щелчках на кнопках соответственно добавляем в модель новую строку и удаляем из нее последнюю строку. О таблице (или нескольких таблицах, или других видах) думать нам не придется — модель сама оповестит всех зарегистрированных в ней слушателей об изменениях в своих данных, а правильно отобразить эти изменения или провести какие-либо другие действия обязаны будут эти слушатели, заинтересованные в данных нашей модели.

526

ГЛАВА 17

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

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

Есть два способа создать свою модель данных для таблицы. Во-первых, можно «с нуля» реализовать описывающий модель данных таблицы интерфейс TableModel, обеспечив всю функциональность модели самостоятельно (в том числе придется реализовать поддержку слушателей). Во-вторых, можно унаследовать класс своей модели от абстрактного класса AbstractTableModel, в котором уже реализована поддержка слушателей и есть удобные методы для инициирования событий нужного вам вида. Второй способ проще и быстрее, так что мы выбираем его. Для того чтобы наша модель заработала, нужно совсем немного — написать три метода: методы getRowCount() и getColumnCount() позволяют таблице определять количество строк и столбцов, а основной метод модели getValueAt() возвращает значение ячейки с указанными номерами строки и столбца. Рассмотрим пример, в котором создадим простую модель на основе класса AbstractTableModel:

//SimpleTableModel.java

//Создание простой модели для таблицы import javax.swing.*;

import javax.swing.table.*; import java.awt.*;

public class SimpleTableModel extends JFrame { public SimpleTableModel() {

super("SimpleTableModel"); setDefaultCloseOperation(EXIT_ON_CLOSE); // создаем таблицу на основе нашей модели

Таблицы

527

JTable table = new JTable(new SimpleModel()); table.setRowHeight(32);

add(new JScrollPane(table)); // выводим окно на экран setSize(400, 300); setVisible(true);

}

// наша модель

class SimpleModel extends AbstractTableModel {

//количество строк public int getRowCount() {

return 100000;

}

//количество столбцов public int getColumnCount() {

return 3;

}

//тип данных, хранимых в столбце

public Class getColumnClass(int column) { switch (column) {

case 1: return Boolean.class; case 2: return Icon.class; default: return Object.class;

}

}

// данные в ячейке

public Object getValueAt(int row, int column) { boolean isEven = (row % 2 == 0);

// разные данные для разных стобцов switch (column) {

case 0: return "" + row; case 1: return isEven;

case 2: return new ImageIcon("Table.gif");

}

return "Пусто";

}

}

public static void main(String[] args) { SwingUtilities.invokeLater(

new Runnable() {

public void run() { new SimpleTableModel(); } });

528

ГЛАВА 17

}

}

Мы создаем небольшое окно, в панель содержимого которого добавляем таблицу, включенную в панель прокрутки JScrollPane. Данные для таблицы будет поставлять наша собственная модель SimpleModel, которую мы унаследовали от абстрактного класса AbstractTableModel. Модель мы передаем прямо в конструктор класса JTable (хотя вы можете использовать для присоединения модели и метод setModel()), этого достаточно, чтобы таблица начала отображать данные, хранимые моделью. Дополнительно мы немного увеличиваем высоту строк таблицы методом setRowHeight(). Как вы сможете увидеть при запуске программы с примером, это позволяет таблице лучше справиться с отображением значков (в модели есть столбец, данные которого представляют собой значки Icon).

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

Таблица 17.2. Основные методы модели данных таблицы TableModel

Название метода

Описание

getRowCount()*

Метод должен возвращать целое число — количество строк в та-

 

блице. Если вы изменяете количество строк, не забудьте опове-

 

стить об этом слушателей модели (чаще всего это таблицы или

 

их UI-представители), чтобы они смогли сразу же отобразить из-

 

менения. Это верно и для остальных данных модели — при их

 

изменении не забывайте оповещать слушателей

getColumnCount()*

Метод возвращает количество столбцов в вашей таблице в виде

 

целого (int) числа. Количество столбцов, как и количество строк,

 

также может изменяться по мере работы программы, и об этом

 

необходимо сообщать слушателям модели чтобы увидеть изме-

 

нения на экране

getValueAt(строка, столбец)*

Основной метод модели данных таблицы. Позволяет указать,

 

какие данные находятся в ячейке, определяемой по указанным

 

строке и столбцу. Данные могут иметь любой тип (возвращается

 

ссылка на базовый тип Object). Отсчет строк и столбцов, как не-

 

трудно догадаться, ведется с нуля

getColumnName(столбец)

Метод позволяет задать имя для столбца, которое отображается

 

в заголовке таблицы JTableHeader (как вы помните, заголовок та-

 

блицы появляется, только если последняя помещается в панель

 

прокрутки). Реализация в абстрактном классе AbstractTableModel

 

именует столбцы как в электронных таблицах: латинскими буква-

 

ми, сначала одиночными, затем сдвоенными, и т. д.

isCellEditable(строка, столбец)

Методпозволяетуказать, можнолиредактироватьячейкувтабли-

 

це с указанным местоположением. Если в вашей таблице есть ре-

 

дактируемые ячейки, не забудьте определить метод setValueAt(),

 

иначе как бы старательно пользователь не редактировал данные,

 

в модели (а значит, и на экране) изменения отражаться не будут

Таблицы

529

Таблица 17.2 (продолжение)

 

 

 

Название метода

Описание

 

 

setValueAt(значение, строка,

Метод используется для изменения значения некоторой ячейки

столбец)

таблицы. Реализуйте этот метод, если в вашей таблице есть ре-

 

дактируемые ячейки, иначе их значение невозможно будет поме-

 

нять

getColumnClass(столбец)

Один из самых интересных методов модели данных таблицы. По-

 

зволяет задать тип данных, хранимых в столбце (тип задается

 

в виде объекта Class). На основе типа данных столбца таблица

 

определяет, как следует отображать и редактировать эти данные.

 

Таблица JTable стандартно поддерживает несколько типов дан-

 

ных для столбцов, подробнее мы их вскоре обсудим

Как видно из кода примера, мы в нашей модели SimpleModel сразу же задаем количество строк и столбцов. Обратите внимание, какое гигантское количество строк в нашей модели, и как просто его задать. Используй мы построчное добавление в стандартную модель или двухмерные массивы, написание даже простой программы рисковало превратиться в сущий ад. В методе getValueAt() возвращается значение каждой ячейки таблицы: для первого столбца мы возвращаем номер строки, для второго — логическую переменную isEven, которая автоматически преобразуется языком в объект Boolean (она равна true для четных строк и false для нечетных), для третьего — значок ImageIcon, созданный на основе файла в формате GIF с небольшим изображением. Тут же возникает вопрос: «Как же таблица сможет это все отобразить?» Мы уже привыкли, что для правильного отображения нестандартных данных (отличных от обычных строк) приходится писать вспомогательные объекты, но как оказывается, таблица JTable способна справиться со многими объектами сама, нужно лишь правильно вызывать метод модели getColumnClass().

Как мы уже отметили в табл. 17.2, метод getColumnClass() позволяет указать тип (а это в Java эквивалентно классу, так что метод возвращает объект Class) данных столбца, так чтобы таблица соответствующим образом отображала и позволяла редактировать эти данные. Типы данных для столбцов, которые поддерживает таблица JTable, перечислены в табл. 17.3.

Таблица 17.3. Типы данных для столбцов таблиц

Тип данных

Описание

Number.class

Столбец с таким типом данных должен хранить данные в виде объ-

 

ектов Integer, Long, Byte и т. д., то есть в виде стандартных классов-

 

оболочек для целых чисел, унаследованных от базового класса

 

Number. Таблицапозаботитсяотом, чтобычислаотображалисьвпод-

 

ходящем виде (к примеру, числа в ячейке выравниваются по правому

 

краю, хотя это зависит от выбранного языка) и чтобы при редактиро-

 

вании пользователь мог вводить в качестве нового значения ячейки

 

только целые числа в подходящем диапазоне (в противном случае

 

пользователь не сможет закончить редактирование и вернуться к ра-

 

боте с таблицей)

Float.class или Double.

Если столбец имеет такой тип данных, то он должен хранить числа

class

с плавающей запятой, «обернутые» в объекты Float или Double. Та-

 

блица будет отображать эти объекты как числа с плавающей запятой

 

и соответствующим образом проводить редактирование, запрещая

 

пользователю ввод неподходящих значений

Источник: https://studfile.net/preview/15935977/