Страницы

Поиск по вопросам

пятница, 10 января 2020 г.

Как правильно использовать AbstractTableModel и DefaultTableColumnModel

#java #swing #jtable


Имеется задача, построить таблицу, столбцы которой являются полями в классе CustomItem.

package com.kolos.Table;

import java.util.Date;

public class CustomItem {
    public Integer id;
    public String name;
    public Date date;
    public CustomItem(Integer id, String name, Date date) {
        this.id = id;
        this.name = name;
        this.date = date;
    }
}


Класс CustomColumnModel:

package com.kolos.Table;

import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.TableColumn;
import java.lang.reflect.Field;

public class CustomColumnModel extends DefaultTableColumnModel{
    public CustomColumnModel(Class tClass) {
        for (Field tField : tClass.getDeclaredFields()){
            TableColumn tColumn = new TableColumn();
            tColumn.setHeaderValue(tField.getName());
            tColumn.setModelIndex(getColumnCount());
            tColumn.setIdentifier(tField);
            tField.setAccessible(true);
            this.addColumn(tColumn);
        }
    }
}


Модель таблицы:

package com.kolos.Table;

import javax.swing.table.AbstractTableModel;
import java.lang.reflect.Field;
import java.util.Date;

public class CustomDataModel extends AbstractTableModel{

    private CustomColumnModel columnModel;
    private CustomItem[] dataList = new CustomItem[3];

    public CustomDataModel(CustomColumnModel columnModel) {
        this.dataList = new CustomItem[3];
        this.dataList[0] = new CustomItem(1, "Fist", new Date());
        this.dataList[1] = new CustomItem(2, "Second", new Date());
        this.dataList[2] = new CustomItem(3, "Third", new Date());
        this.columnModel = columnModel;
    }

    @Override
    public int getRowCount() {
        return this.dataList.length;
    }

    @Override
    public int getColumnCount() {
        return this.columnModel.getColumnCount();
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        try {
            Field tField = ((Field)     columnModel.getColumn(columnIndex).getIdentifier());
            return tField.get(dataList[rowIndex]);
        } catch (IllegalAccessException e) {
            return "IllegalAccessExeption";
        }
    }
}


Класс CustomTable:

package com.kolos.Table;

import javax.swing.*;

public class CustomTable extends JTable{
    private CustomDataModel dataModel;
    private CustomColumnModel columnModel;
    public CustomTable() {
        setAutoCreateColumnsFromModel(false);
        columnModel = new CustomColumnModel(CustomItem.class);
        setColumnModel(columnModel);
        dataModel = new CustomDataModel(columnModel);
        setModel(dataModel);
    }
}


Фрейм:

package com.kolos;

import com.kolos.Table.CustomTable;
import javax.swing.*;
import java.awt.*;

public class MainFrame extends JFrame{
    public MainFrame() {
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        this.setSize(new Dimension(800, 600));
        this.setLayout(new GridBagLayout());

        //create table
        JScrollPane scrollPane = new JScrollPane();
        CustomTable table = new CustomTable();
        scrollPane.setViewportView(table);

        this.add(scrollPane, new GridBagConstraints(
                0, 0, 1, 1, 1, 1, GridBagConstraints.NORTH, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0
        ));

    }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                MainFrame mainFrame = new MainFrame();
                mainFrame.setVisible(true);
            }
        });
    }

}


Проблема в том что при перетаскивании столбцов (например 2 -> 0) заголовки переносятся,
а значения из модели (получаемые методом getValueAt ) переносятся  непонятным образом.




Собственно почему так и как исправить?
    


Ответы

Ответ 1



Дело в том, что когда вы перемещаете столбцы, они реально меняются местами в списке столбцов ColumnModel, при этом в TableModel.getValueAt координаты ячейки приходят уже пересчитанными в пространство модели (значение поля TableColumn.modelIndex) Поэтому когда вы ставите третью колонку на место первой, для показа первой колонки вызывается getValueAt( rowIndex, 2 ), вы берете у модели столбцов колонку по индексу 2, но туда уже сдвинулась средняя колонка, которая отвечала за вывод имени. Мне кажется, лучше всего держать получение данных по номеру столбца прямо в модели таблицы. Заведите список Field для доступа к данным, заполните его в конструкторе модели, как вы сейчас делаете в CustomColumnModel, и возвращайте имя колонки в TableModel.getColumnName(int). А TableColumnModel вообще не трогать. public class CustomDataModel extends AbstractTableModel { private CustomItem[] dataList = new CustomItem[3]; private List fields = new ArrayList<>(); public CustomDataModel( Class tClass ) { this.dataList = new CustomItem[3]; this.dataList[0] = new CustomItem(1, "Fist", new Date()); this.dataList[1] = new CustomItem(2, "Second", new Date()); this.dataList[2] = new CustomItem(3, "Third", new Date()); for (Field tField : tClass.getDeclaredFields()){ tField.setAccessible(true); fields.add( tField ); } } @Override public int getRowCount() { return this.dataList.length; } @Override public int getColumnCount() { return this.fields.size(); } @Override public String getColumnName( int column ) { return fields.get( column ).getName(); } // можно возвращать тип столбца, тогда JTable будет выбирать соотв. TableCellRenderer // @Override // public Class getColumnClass( int column ) { // return fields.get( column ).getType(); // } @Override public Object getValueAt(int rowIndex, int columnIndex) { try { Field tField = fields.get( columnIndex ); return tField.get(dataList[rowIndex]); } catch (IllegalAccessException e) { return "IllegalAccessExeption"; } } } public class CustomTable extends JTable{ private CustomDataModel dataModel; public CustomTable() { setAutoCreateColumnsFromModel( true ); // не забудьте вернуть автосоздание dataModel = new CustomDataModel( CustomItem.class ); setModel( dataModel ); } } Либо вы можете в getValueAt искать нужную колонку по совпадению columnIndex и tableColumn.getModelIndex(), или воспользоваться методами jTable.convertColumnIndexToView( columnIndex ) (но нужно держать в модели ссылку на таблицу), или sun.swing.SwingUtilities2.convertColumnIndexToView( columnModel, columnIndex ), которые делают то же самое.

Комментариев нет:

Отправить комментарий