Афоризм
У тебя ноги уже кончились, а платье еще не началось!
Лариса Гузеева
Последние статьи

 • Компонент JDatePicker
Описание и пример компонента JDatePicker
сентябрь 2019
 • Компонент Tree
Описание и пример дерева Tree
сентябрь 2019
 • Grid с навигатором
Описание и пример Gridp с навигатором
сентябрь 2019
 • Компонент Grid
Описание и пример Grid библиотеки base-gui
август 2019
 • Библиотека base-gui
Описание компонентов библиотеки base-gui
август 2019
 • Оператор SELECT
Использование SQL-оператора SELECT
август 2019
 • Сокеты в Android
Использование сокетов в Android
июль 2019
 • Многомодульный maven
Пример создания многомодульного maven проекта
июнь 2019
 • Maven плагин launch4j
Создание exe-файла из исполняемого jar
июнь 2019
в помощь разработчикам 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;
  2. Описание полей ExampleGridp :
    • Сменяемый набор данных для таблицы.
    • Параметры настройки таблицы.
  3. Создание интерфейса :
    • Метод создания таблицы.
    • Метод создания панели управления.
  4. Управление записями :
    • Метод добавления записи.
    • Метод удаления записи.
  5. Локализация таблицы

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 включают :

gridp Таблица с панелью навигации
pageconf Конфигуратор панели навигации
dao Хранилище данных
btnAdd, btnDel Кнопки добавления и удаления записей в наборе
locale Текущий объект локализации
data_idx Индекс модели данных
RECORDS_ON_PAGEКоличество записей на странице
milk 2-ой массив данных (1-ый массив в хранилище ModuleDAO)
added_idx Указатель добавляемой записи из массива data_add
data_add Массив добавляемых записей
tblXXX Поля с префиксом «tbl» – описание настроек таблицы для 1-го набора данных
milkXXX Поля с префиксом «milk» – описание настроек таблицы для 2-го набора данных

Конфигуратор панели навигации 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. Конфигуратор панели навигации определяет параметры набора данных таблицы : номер текущей страницы, количество записей на странице, общее количество записей и количество страниц. Назначение методов конфигуратора интуитивно понятноиз наименований :

  • setRecords_max – максимальное количество записей;
  • setPage_records – количество записей на странице;
  • setPages – общее количество страниц;
  • setPage – номер текущей страницы.

Листинг метода создания таблицы

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, согласно которому в класс включены методы управления набором данных на странице :

  • selectFirst – отобразить записи первой страницы;
  • selectPrev – отобразить записи предыдущей страницы;
  • selectNext – отобразить записи следующей страницы;
  • selectLast – отобразить записи последней страницы;
  • selectPage – отобразить записи определенной страницы.

Представленные методы вызываются при нажатии на кнопки управления панели навигации таблицы 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.

  Рейтинг@Mail.ru