Текстовые компоненты |
515 |
Создать свой собственный рисующий объект для прорисовки фона за текстом очень просто — интерфейс HighlightPainter предусматривает всего лишь один метод paint(), в который вам передается объект для рисования Graphics и область, в который вы должны прорисовать фон. Используя механизм рисования фона для текстовых компонентов, вы можете создавать весьма удобные для пользователя режимы работы, подсказывая ему с помощью визуальных эффектов что-то, в чем он, скорее всего, в данный момент наиболее заинтересован, или разделяя на экране информацию различной структуры и предназначения.
Резюме
Текстовые компоненты Swing позволят организовать в своем приложении ввод и редактирование текста самыми разными способами: начиная от редактирования простого текста в одну строку и заканчивая составлением и отображением документов в известных форматах RTF и HTML. В данной главе мы разобрали базовые возможности текстовых компонентов Swing, на их основе вы сможете реализовать большую часть требуемых в приложениях операций. Если же вашей целью является мощный текстовый редактор с огромным спектром возможностей, то к вашим услугам пакет javax.swing.text: он содержит все, чтобы ваши фантазии можно было воплотить в жизнь.
С другой стороны, если речь заходит о мощных текстовых возможностях, вы можете начать с платформы NetBeans, основной целью которой является удобное редактирование текста, в том числе и структурированного. Скорее всего, если ваши цели и способности NetBeans совпадут, вы сэкономите на разработке очень много времени — реализация качественных текстовых компонентов требует усилий.
Глава 17. Таблицы
Пришло время познакомиться с одним из самых впечатляющих компонентов библиотеки Swing — таблицей JTable. Таблица JTable дает возможность с легкостью выводить двухмерную информацию, расположенную в виде строк и столбцов, достаточно легко настраивать и сортировать данные, выводить их в любом подходящем для вас виде, управлять заголовками таблицы и ее выделенными элементами и с небольшими усилиями делать еще многое другое. Бывают случаи, когда всю необходимую пользователю информацию вы можете разместить в нескольких таблицах, применяя остальные компоненты лишь в качестве вспомогательных, и с помощью тех же таблиц получать любую нужную информацию.
Впрочем, за все приходится платить. Это правило действует и в случае с таблицами. Чтобы полностью овладеть всей мощью класса JTable, придется изучить довольно много вспомогательных классов, узнать способы их взаимодействия, понять, как реализованы модели и почему они так реализованы, в каких ситуациях таблица ведет себя определенным образом, и многое другое. Вспомогательным классам таблицы JTable посвящен целый пакет (впечатляющего размера) javax.swing.table. Но пугаться не стоит, создать таблицу с некоторой информацией, полученной из любого источника, довольно просто и не требует особенного напряжения. Как всегда, Swing подстраивается под уровень пользователя: для получения простой таблицы потребуются простые действия, а все более сложные таблицы будут требовать от вас все больше и больше усердия. Начнем с самого простого.
Простые таблицы
Таблица JTable позволяет выводить двухмерные данные, записанные в виде строк и столбцов. Данные для таблицы поставляет специальная весьма гибкая модель, обязанности которой описаны в классе TableModel (стоит помнить, что практически все вспомогательные классы и интерфейсы для таблиц JTable находятся в пакете javax.swing.table). Она позволяет передать таблице все необходимые данные для верного вывода информации (количество строк и столбцов, названия столбцов, элемент, находящийся в определенном месте таблицы, тип данных, хранящийся в столбце, признак доступности для редактирования некоторого элемента), а также поддерживает слушателей, получающих уведомления об изменениях в данных модели. Всего этого хватает таблице JTable для отображения двухмерных данных любого типа.
Как и всегда в Swing, вам необязательно использовать для таблиц подготовленные и наполненные данными модели, вы можете передать в специальные конструкторы таблицы JTable массивы или векторы с информацией о ячейках таблицы и, при необходимости, о названиях ее столбцов. Довольно часто, особенно при создании простых программ или простых незатейливых таблиц, которые вряд ли будут в будущем расширяться или повторно использоваться, такой подход удобнее и быстрее. Учитывая при этом, что вы с легкостью можете получить значение любой ячейки таблицы методом getValueAt() и изменить значение любой ячейки методом setValueAt(), у вас и без моделей (хотя на самом деле они незримо работают за кулисами) есть возможность полностью управлять данными таблицы.
Таблица JTable, как и большинство сложных компонентов библиотеки Swing, реализует интерфейс Scrollable, так что вы смело можете добавлять ее в панель прокрутки JScrollPane, не заботясь о правильности прокрутки. При прокрутке полностью появля-
Таблицы |
517 |
ются следующий столбец или следующая строка таблицы, это удобно и интуитивно понятно пользователю. Интересно, что заголовок таблицы (специального вида надписи с названиями столбцов, реализованных классом JTableHeader) будет виден пользователю только при помещении таблицы в панель прокрутки, потому что таблица размещает компонент JTableHeader в виде заголовка панели прокрутки (как мы отмечали в главе 13, заголовок панели прокрутки хранится в ее свойстве rowHeaderView). Так что если вам нужно увидеть заголовок таблицы, придется включить таблицу в панель прокрутки или вручную разместить около верхней границы таблицы компонент JTableHeader. Чуть позже мы обсудим подробнее заголовок таблицы.
Ну а сейчас посмотрим, насколько просто можно создавать вполне работоспособные таблицы стандартного вида с помощью специальных конструкторов:
//SimpleTables.java
//Простые таблицы с помощью удобных конструкторов import javax.swing.*;
import java.util.*; import java.awt.*;
public class SimpleTables extends JFrame { // данные для таблиц
private Object[][] colors = new String[][] {
{"Красный", "Зеленый", "Синий" },
{"Желтый", "Оранжевый", "Белый" },
}; // названия заголовков столбцов
private Object[] colorsHeader = new String[] { "Цвет", "Еще цвет", "Тоже цвет"
};
public SimpleTables() { super("SimpleTables"); setDefaultCloseOperation(EXIT_ON_CLOSE); // несколько простых таблиц
JTable table1 = new JTable(colors, colorsHeader); JTable table2 = new JTable(5, 5);
//таблица на основе вектора, состоящего из векторов
Vector<Vector<String>> data =
new Vector<Vector<String>>(); Vector<String> row1 = new Vector<String>(); Vector<String> row2 = new Vector<String>();
//вектор с заголовками столбцов
Vector<String> columnNames = new Vector<String>(); // наполнение данными
for (int i=0; i<3; i++) { row1.add("Ячейка 1." + i); row2.add("Ячейка 2." + i);
518 |
ГЛАВА 17 |
columnNames.add("Столбец #" + i);
}
data.add(row1);
data.add(row2);
JTable table3 = new JTable(data, columnNames);
//добавляем таблицы в панель с тремя рядами setLayout(new GridLayout(3, 1));
add(new JScrollPane(table1)); add(new JScrollPane(table2)); add(table3);
//выводим окно на экран
setSize(350, 400); setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new SimpleTables(); } });
}
}
В примере мы наследуем от окна JFrame, указываем, что при закрытии окна нужно будет закончить работу приложения, и размещаем в панели содержимого несколько таблиц JTable, созданных с помощью удобных конструкторов. Первая таблица (table1) создается конструктором, принимающим два массива с данными. Первый массив должен быть двухмерным и хранить строки таблицы с данными некоторого типа. Мы использовали в качестве данных самое простое из всего возможного — строки String. Остается только вспомнить, как записываются в Java двухмерные массивы, довольно редкие гости обычных программ. Вы видите, что данные для первой таблицы (две строки с названиями цветов) описаны в двухмерном массиве colors. Второй передаваемый в конструктор массив одномерный и хранит названия столбцов таблицы, с ним все проще. Вторая таблица (table2) создается с помощью, пожалуй, самого простого конструктора, которому требуется указать лишь количество строк и столбцов в таблице. Все ячейки будут пустыми, а в качестве названий столбцов использованы латинские буквы, как во всех электронных таблицах. Такой конструктор требуется редко, но он может быть полезен там, где с помощью таблиц нужно получать информацию от пользователя или имитировать вид электронной таблицы. Ну и, наконец, третий конструктор работает с вектором (динамическим массивом) Vector из пакета java.util. Ему требуется передать два вектора. Первый должен хранить в себе такие же векторы, в которых, в свою очередь, должны храниться данные строк таблицы. Каждый вектор отвечает за свою строку таблицы. Второй вектор хранит обычные объекты — названия столбцов таблицы. В примере мы наполняем векторы строк и названий столбцов в цикле. Затем векторы строк добавляются в вектор, который будет хранить все данные таблицы (data), после чего таблицу table3 можно создавать.
Созданные три таблицы добавляются в окно, в котором мы установили табличное расположение GridLayout из трех рядов и одного столбца. Как нам известно, табличное расположение выделяет своим ячейкам одинаковое пространство. Последовательное расположение FlowLayout и вообще любое расположение, предоставляющее компонен-
Таблицы |
519 |
там все затребованное ими «предпочтительное» пространство, нам не подойдут, потому что таблицы, помещенные в панель прокрутки JScrollPane, очень требовательны к пространству, даже если в них хранится совсем немного данных. Учитывайте этот факт при создании пользовательского интерфейса и подбирайте такой менеджер расположения, который не позволит таблицам в панели прокрутки «отнять» у контейнера слишком много места. С другой стороны, таблицы без панелей прокрутки занимают ровно столько места, сколько им требуется для отображения своих данных. Заметьте, что мы используем панель прокрутки только для первых двух таблиц, а третью добавляем в панель напрямую. После запуска программы с примером вы увидите, что в такой таблице не отображается заголовок. Для вывода заголовка нужно либо добавить таблицу в панель прокрутки (в заголовке которой и окажется заголовок таблицы), либо ввести его в контейнер вручную. Как работать с заголовками таблиц, мы узнаем немного позже.
На заключительном этапе наше окно выводится на экран. Запустив программу с примером, вы убедитесь в том, что несколькими строками кода мы создали вполне работоспособные таблицы, достаточные для многих простых приложений. Разбирая пример, вы также увидите, что выделение в таблицах JTable по умолчанию работает построчно, выделять можно произвольное количество строк, все элементы считаются редактируемыми (начать редактирование позволяет двойной щелчок мыши, таким образом он действует для всех внешних видов, поставляемых с библиотекой Swing), а столбцы таблицы можно перетаскивать и менять местами (правда, делать это можно только с первыми двумя таблицами, у которых есть заголовки). Все это поддается настройке. Эти вопросы мы будем обсуждать постепенно на протяжении всей главы. Перетаскивание столбцов таблицы, с одной стороны, может быть удобным (те столбцы, с которыми он чаще работает, пользователь сделает первыми), однако, с другой стороны, может запутать и усложнить работу даже с простой таблицей (пользователю придется выяснять, какие столбцы были перемещены, где они теперь расположены и в каких логических отношениях они находятся с другими столбцами). Перетаскивание столбцов смотрится эффектно, но, как правило, во избежание путаницы его следует отключать, вскоре мы узнаем, как это делается. С другой стороны, программисту-клиенту таблицы не придется учитывать перемещения столбцов: их номера в таблице и ее моделях не меняются, как бы изощренно пользователь не тасовал столбцы.