410013796724260
• Webmoney
R335386147728
Z369087728698
в помощь разработчикам Swing-приложений
Пример таблицы Gridp с навигаторомВ статье рассмотрен пример создания таблицы с панелью навигации (example-gridp). В качестве интерфейсного компонента используется класс org.jabricks.basegui.grid.Gridp модуля base-gui. В примере продемонстрировано :
Управление настройками и данными таблицы осуществляться с помощью API таблицы (интерфейс org.jabricks.basegui.grid.IGridp). На следующем скриншоте представлен интерфейс примера после старта. Таблица с панелью навигации располагается в центральной части формы. В нижней части формы размещаются кнопки управления. 1. Описание примераПример включает 2 класса : ModuleDAO и ExampleGridp. Класс ModuleDAO имитирует хранилище исходных данных и содержит методы управления записями. Класс ExampleGridp формирует интерфейсное окно. Описание исходного кода примера, который можно скачать в конце страницы, разделим на несколько частей :
1. Хранилище данных ModuleDAOКласс ModuleDAO, организующий хранилище данных, играет важную роль, несмотря на то, что не является основной целью данной статьи. Конструктор класса ModuleDAO формирует коллекцию записей records типа List<Object[]>. Набор отображающих в интерфейсе данных представлен полем data. Запись дополнена еще одной колонкой, играющей роль идентификатора записи (COL_ID). Метод loadData(int offs, int limit) получает в качестве параметров смещение на начало набора offs и количество записей на странице limit, и возвращает массив данных типа Object[][]. Методы addRecord и deleteRecord используются для управления записями в наборе : добавление и удаления. Метод getMaxID возвращает идентификатор для новой записи. Метод getRecordsCount возвращает общее количество записей в наборе для их по-страничного разбиения и представления в таблице. public class ModuleDAO { private final int COL_ID = 4; private Object[][] data = {{ "Мороженое" , 5, 80, 400}, { "Пироженое" , 10, 45, 450}, { "Слива" , 2, 80, 160}, { "Виноград" , 3, 75, 225}, { "Байкал 1.0" , 10, 39, 390}, { "Coca-cola 1.5" , 5, 60, 300}, { "Coca-cola 2.0" , 5, 78, 390}, { "Яблоки" , 4, 60, 240}, { "Абрикосы" , 2, 90, 180}, { "Бананы" , 1, 60, 60}, { "Молоко 0.9" , 2, 55, 110}, { "Кефир 0.5" , 2, 55, 110}, { "Ряженка 0.5" , 2, 45, 90}}; private List<Object[]> records; // набор записей //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public ModuleDAO() { records = new ArrayList<Object[]>(); for (int i = 0; i < data.length; i++) { Object[] obj = new Object[COL_ID + 1]; for (int j = 0; j < data[i].length; j++) obj[j] = data[i][j]; obj[COL_ID] = (i + 1); // ID записи records.add(obj); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public Object[][] loadData(int offs, int limit) { int size = limit; if ((records.size() - offs) < limit) size = records.size() - offs; Object[][] objects = new Object[size][COL_ID + 1]; for (int i = offs; i < records.size(); i++) { Object[] obj = records.get(i); for (int j = 0; j < obj.length; j++) objects[i - offs][j] = obj[j]; if ((i - offs) == (limit - 1)) break; } return objects; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public Object[] addRecord(Object[] record) { Object[] obj = new Object[data[0].length + 1]; for (int i = 0; i < data[0].length; i++) obj[i] = record[i]; obj[COL_ID] = getMaxID(); records.add(obj); return obj; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public void deleteRecord(final int id) { for (int i = 0; i < records.size(); i++) { Object[] obj = records.get(i); if ((int) obj[COL_ID] == id) { records.remove(i); break; } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public int getRecordsCount() { return records.size(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private int getMaxID() { int id = 0; for (int i = 0; i < records.size(); i++) { Object[] obj = records.get(i); if ((int) obj[COL_ID] < id) id = (int) obj[COL_ID]; } return id + 1; } } 2. Описание полей ExampleGridpПоля интерфейсного класса ExampleGridp включают :
Конфигуратор панели навигации pageconf определяет количество записей на странице, общее количество записей, общее количество страниц и номер текущей страницы. В примере используются 2 набора записей. 1-ый набор записей получаем из хранилища, а второй набор описан полем milk. Поскольку наборы записей отличаются количеством полей, то для каждой модели данных таблицы используются свои настройки. Для записей из хранилища dao настройки таблицы описаны полями с префиксом «tbl» (tblXXX). Настройки 2-го набора записей описаны полями с префиксом «milk» (milkXXX). Индекс используемой модели данных data_idx указывает на загруженный набор записей. Только для 1-го набора записей разрешается выполнять транзакции : добавлять и удалять запись. При переключении на 2-ой набор записей кнопки выполнения транзакций btnAdd, btnDel блокируются. Листинг полей ExampleGridpЛистинг описания полей ExampleGridp имеет следующий вид : private IGridp gridp = null; private PaginatorConfig pageconf = null; private final int RECORDS_ON_PAGE = 8; private ModuleDAO dao = null; private JButton btnAdd = null; private JButton btnDel = null; private Locale locale = null; private int data_idx = 1; private int added_idx = 0; // Массивы данных private Object[][] milk = {{"Сметана", "кг", 1.2f}, {"Сыр" , "кг", 0.5f}, {"Творог" , "кг", 0.9f}, {"Молоко" , "л" , 2.5f}, {"Кефир" , "л" , 0.5f}, {"Ряженка", "л" , 0.9f}}; private Object[][] data_add = { { "Йогурт 0.5" , 4, 55, 220}, { "Йогурт 0.25", 6, 35, 210}, { "Йогурт 1.0" , 2, 95, 180}, { "Кефир 0.5" , 2, 45, 90}}; // Описание колонок private String [] tblNames = {"name", "quantity", "price", "cost"}; private String [] tblColumns = {"Наименование", "Количество", "Цена", "Стоимость"}; private Integer[] tblAlign = {JLabel.LEFT, JLabel.RIGHT, JLabel.RIGHT, JLabel.RIGHT}; private Class<?>[] tblClasses = {String.class, Integer.class, Integer.class, Integer.class}; private String [] milkNames = {"name", "measure", "quantity"}; private String [] milkColumns = {"Наименование", "Ед. измерения", "Количество"}; private Integer[] milk_align = {JLabel.LEFT, JLabel.CENTER, JLabel.RIGHT}; private Class<?>[] milk_classes = {String.class, String.class, Float.class}; 3.1. Метод создания таблицыПри создании таблицы Gridp наряду с описаниями (массивами) типов колонок, наименований колонок и заголовок колонок, конструктору передается родительский объект таблицы (this), реализующий интерфейс IPaginator. Методы данного интерфейса будут вызываться при постраничном перемещении по набору записей, т.е. при нажатии на кнопки управления панели навигации таблицы. Объект таблицы Gridp представляет собой компонент панели типа JPanel, реализующий интерфейс IGridp. Всё пространство панели занимает компонент JTable с панелью навигации, «обернутый» в скроллинг JScrollPane. После создания таблицы выполняется её настройка : определение выравнивания значений в ячейках таблицы и определение размеров колонок. Возможность перемещения колонок таблицы блокируется. Помните, что при разрешении перемещения колонок необходимо контролировать типы значений добавляемых и редактиремых записей. Вторым важным аспектом создания таблицы Gridp является настройка конфигуратора панели навигации типа PaginatorConfig, который можно получить методом getPaginatorConfig. Конфигуратор панели навигации определяет параметры набора данных таблицы : номер текущей страницы, количество записей на странице, общее количество записей и количество страниц. Назначение методов конфигуратора интуитивно понятноиз наименований :
Листинг метода создания таблицыprivate void createGrid() { gridp = new Gridp(this, tblClasses, tblNames, tblColumns); gridp.setColumnsAlign(tblAlign); gridp.setColumnsWidth(new int[] {220, 60, 60, 60}); gridp.setReorderingAllowed(false); // Определение конфигуратора int pages = dao.getRecordsCount()/RECORDS_ON_PAGE + 1; pageconf = gridp.getPaginatorConfig(); pageconf.setRecords_max(dao.getRecordsCount()); pageconf.setPages(pages); pageconf.setPage(1); pageconf.setPage_records(RECORDS_ON_PAGE); } Листинг метода смены набора данныхВ примере демонстрируется смена набора записей, имеющих различное количество полей. Вместе со сменой набора записей изменяется и модель данных. Данную функцию выполняет метод changeTableModel. В зависимости от значения индекса data_idx таблица настраивается либо на хранилище данных dao (ModuleDAO), либо на набор milk. Набор записей в таблицу загружается методом addRecords, который в качестве параметра получает данные в формате Object[][]. Метод loadData читает в хранилище определенный набор записей начиная со смещения offs. private void changeTableModel() { if (data_idx == 0) { gridp.setModel(tblClasses, tblNames, tblColumns); gridp.setColumnsAlign(tblAlign); gridp.setColumnsWidth(new int[] {220, 60, 60, 60}); pageconf.setRecords_max(dao.getRecordsCount()); int pages = dao.getRecordsCount()/RECORDS_ON_PAGE + 1; pageconf.setPages(pages); pageconf.setPage(1); pageconf.setPage_records(RECORDS_ON_PAGE); gridp.addRecords(loadData()); data_idx++; } else { gridp.setModel(milk_classes, milkNames, milkColumns); gridp.setColumnsAlign(milk_align); gridp.setColumnsWidth(new int[] {240, 110, 90}); pageconf.setRecords_max(milk.length); int pages = milk.length / RECORDS_ON_PAGE + 1; pageconf.setPages(pages); pageconf.setPage(1); pageconf.setPage_records(RECORDS_ON_PAGE); gridp.addRecords(milk); data_idx--; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private Object[][] loadData() { int offs = (pageconf.getPage() - 1) * RECORDS_ON_PAGE; return dao.loadData(offs, RECORDS_ON_PAGE); } 3.2. Метод создания панели управленияМетод createControls формирует панель управления и размещает на ней 4 кнопки управления. К кнопкам подключаются соответствующие обработчики событий. При нажатии на кнопку btnModel выполняется изменение набора записей в таблице. Кнопкой локализации btnLocale изменяется объект локализации locale и надписи в панели навигации таблицы. Кнопками управления транзакциями вызываются методы добавления и удаления записей. Если в таблицу загружаются данные не из хранилища, то кнопки управления транзакциями блокируются. private JPanel createControls() { JButton btnModel; JButton btnLocale; btnModel = new JButton("Изменение набора данных"); btnModel.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { changeTableModel(); btnAdd.setEnabled(data_idx == 1); btnDel.setEnabled(data_idx == 1); } }); btnLocale = new JButton("Локализация панели навигации"); btnLocale.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { changeLocale(); } }); btnAdd = new JButton("Добавить запись"); btnAdd.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { addRecord(); } }); btnDel = new JButton("Удалить запись"); btnDel.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { deleteRecord(); } }); // Панель кнопок управления JPanel pnlButtons = new JPanel(); pnlButtons.add(btnModel ); pnlButtons.add(btnLocale); pnlButtons.add(btnAdd ); pnlButtons.add(btnDel ); return pnlButtons; } 4. Управление записямиМетоды добавления addRecord и удаления deleteRecord записи по-разному реагируют на изменения набора данных. Так метод addRecord добавляет запись в конец набора (можно в любую позицию). Количество записей на странице увеличивается, несмотря на заданный размер. Это связано с тем, чтобы пользователь мог увидеть новую запись. При переходе на другую страницу количество отображаемых записей на странице нормализуется. Метод deleteRecord удаляет запись как из таблицы, так и из хранилища, и перегружает набор данных. 4.1. Добавление записиМетод addRecord вызывается при нажатии на кнопку «Добавить запись». В методе выполняется проверка наличия недобавленных записей (переменная added_idx); при положительном условии запись сначала добавляется в хранилище методом addRecord, который возвращает объект Object[] с идентификатором записи. После этого запись добавляется в конец таблицы на текущей странице. При необходимости можно использовать метод «addRecord (int row, Object[] record)» для добавления записи в конкретную позицию набора данных таблицы. После добавления записи указатель added_idx перемещается в следующую позицию. При добавлении записи компонент Gridp автоматически переопределяет параметры конфигуратора панели навигации, связанные с максимальным количеством записей и количеством страниц. private void addRecord() { if (added_idx < data_add.length) { Object[] obj = data_add[added_idx]; Object[] record = dao.addRecord (obj); gridp.addRecord(record); added_idx++; } } Метод «addRecords (PaginatorConfig, Object[][])» позволяет загрузить в таблицу набор записей. Если модель данных не пустая, т.е. таблица уже содержит записи, то они будут предварительно удалены, после чего в таблицу будет загружен новый массив записей. Данный метод используется в примере при смене набора данных. 4.2. Удаление записиМетод deleteRecord вызывается при нажатии на кнопку «Удалить запись». Удалить можно только выделенную запись. При удалении записи сначала определяется её идентификатор id. После этого запись удаляется из хранилищи и из таблицы, и выполняется обновление набора записей на странице. При удалении записи компонент Gridp автоматически переопределяет параметры конфигуратора панели навигации, связанные с максимальным количеством записей и количеством страниц. private void deleteRecord() { int row = gridp.getTable().getSelectedRow(); if (row >= 0) { // Модель данных GridpModel model; model = (GridpModel) gridp.getTable().getModel(); int id = (Integer) model.getTableData(). get(row).elementAt(4); dao.deleteRecord(id); gridp.delRecord(row); // Обновление записей на странице gridp.addRecords(loadData()); } } Метод «delRecords (final int[] rows)» позволяет удалить из таблицы несколько записей. В качестве параметра метод получает массив удаляемых строк. 5. Локализация таблицыЛокализация заголовков таблицы была описана в примере использования обычной Grid без панели навигации. Таблица с панелью навигации также имеет метод setLocale(Properties) для изменения заголовков колонок в режиме run-time, где свойства Properties содержат набор записей типа «ключ=значение». Кроме этого таблица Gridp содержит метод setLocale(Locale, Properties) для изменения надписей панели навигации и заголовков колонок. В примере используются только надписи для двух языков локализации (ru, en). Таблица Gridp включает также локализованные надписи для немецкого языка (de). Метод changeLocale вызывается в примере при нажатии на кнопку «Локализаци панели навигации». private void changeLocale() { if (locale.getLanguage().equalsIgnoreCase("ru")) locale = new Locale("en"); else locale = new Locale("ru"); gridp.setLocale(locale, null); } Листинг главного класса ExampleGridpНиже представлен листинг класса ExampleGridp в усеченном виде. Все переменные и методы, описанные выше, не включены в код. ExampleGridp реализует интерфейс IPaginator, согласно которому в класс включены методы управления набором данных на странице :
Представленные методы вызываются при нажатии на кнопки управления панели навигации таблицы Gridp. Исключением является метод selectPage(int), который вызывается при нажатии клавиши Enter в поле номера страницы. То есть, для выбора конкретной необходимо ввести номер и нажать Enter. public class ExampleGridp extends JFrame implements IPaginator { //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public ExampleGrid() { super("Gridp example"); setDefaultCloseOperation(EXIT_ON_CLOSE); locale = new Locale("ru"); dao = new ModuleDAO(); createGrid(); getContentPane().add(gridp.asWidget(), BorderLayout.CENTER); getContentPane().add(createControls(), BorderLayout.SOUTH ); // Загрузка данных gridp.addRecords(pageconf, loadData()); setSize(760, 360); setVisible(true); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Отобразить записи первой страницы */ @Override public void selectFirst() { pageconf.setPage(1); gridp.addRecords(loadData()); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Отобразить записи предыдущей страницы */ @Override public void selectPrev() { if (pageconf.getPage() > 1) { pageconf.setPage(pageconf.getPage() - 1); gridp.addRecords(loadData()); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Отобразить записи следующей страницы */ @Override public void selectNext() { if (pageconf.getPage() < pageconf.getPages()) { pageconf.setPage(pageconf.getPage() + 1); gridp.addRecords(loadData()); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Отобразить записи последней страницы */ @Override public void selectLast() { if (pageconf.getPage() < pageconf.getPages()) { pageconf.setPage(pageconf.getPages()); gridp.addRecords(loadData()); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Отобразить записи определенной страницы * @param page номер страницы */ @Override public void selectPage(int page) { if (data_idx == 0) return; int pages = pageconf.getPages(); if ((page >= 1) && (page <= pages)) { pageconf.setPage(page); gridp.addRecords(loadData()); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public static void main(String[] args) { new ExampleGrid(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ } Примечание : в методе selectPage(int) выполнена дополнительная проверка загруженной модели данных. Старт примераАрхив примера включает командный файл run-example-gridp.bat для старта example-gridp.jar в Windows из командной строки. Запускаемый example-gridp.jar (runable) может быть стартован не только из командной строки, но и обычным способом (двойным нажатием клавишей мыши), поскольку модуль lib/base-gui-1.0.0.jar включен в classpath манифеста META-INF/MANIFEST.MF. Скачать примерАрхивный файл base-gui-examples.zip включает данный пример (example-gridp) и другие примеры использования модуля base-gui. |