【问题标题】:How to remove a JTable column from both TableModel and TableColumnModel with setAutoCreateColumnsFromModel(false)?如何使用 setAutoCreateColumnsFromModel(false) 从 TableModel 和 TableColumnModel 中删除 JTable 列?
【发布时间】:2019-12-25 04:25:33
【问题描述】:

在最后查看我的 SSCCE 代码。我想要实现的是:

  • 数据模型和列模型中的列数相等
  • 使用setAutoCreateColumnsFromModel(false) 避免在数据/表模型添加或删除列时重新创建列模型
  • 能够移动列

大按钮在末尾添加一个新列。每个新列都有一个唯一标识符。

标题有一个右键菜单HeaderMenu 用于删除列。计时器调用table.tableModel.addRow() 在顶部插入新行。每列的数据由类Column 生成。在这个演示中,值只是一个带有列标识符的行计数器。

在实际表中(不是这个demo)

  • 每一列都是Column 的子类并生成有意义的数据
  • 菜单还包含向左/向右插入和替换。这是使用与演示添加/删除方法类似的方法并将列移动到所需位置来实现的
  • 数据模型可能包含十几列和超过一百万行
  • 可以以毫秒到几秒的时间间隔添加行,即性能很重要

此演示演示了删除列时会产生如下错误的问题:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 2 >= 2
at java.util.Vector.elementAt(Vector.java:477)
at javax.swing.table.DefaultTableModel.getValueAt(DefaultTableModel.java:649)
at javax.swing.JTable.getValueAt(JTable.java:2720)
at javax.swing.JTable.prepareRenderer(JTable.java:5712)
at javax.swing.plaf.basic.BasicTableUI.paintCell(BasicTableUI.java:2114)
...

请告知如何修复它。完整代码如下:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;

@SuppressWarnings("serial")
public class TableDemo extends JTable {

    private static class Column {

        private int rowsCounter = 0;
        private final String identifier;

        public Column(String identifier) {
            this.identifier = identifier;
        }

        private String nextCellValue() {
            return (rowsCounter++) + ", id: " + identifier;
        }
    }

    private static class MyTableModel extends DefaultTableModel {

        private final List<Column> columns = new ArrayList<>();
        private int nextColumnIdentifier = 0;

        private void addRow() {
            Object[] row = columns.stream().map(Column::nextCellValue).toArray();
            insertRow(0, row);
        }

        private TableColumn addColumn() {
            String identifier = String.valueOf(nextColumnIdentifier++);
            columns.add(new Column(identifier));
            addColumn(identifier);
            TableColumn tc = new TableColumn();
            tc.setIdentifier(identifier);
            tc.setHeaderValue(identifier);
            tc.setModelIndex(columns.size() - 1);
            return tc;
        }

        private void removeColumn(int idx) {
            columns.remove(idx);
            columnIdentifiers.remove(idx);
            for (Object row : dataVector) {
                ((Vector<?>) row).remove(idx);
            }
            fireTableStructureChanged();
        }
    }

    private static class HeaderMenu extends JPopupMenu {

        private int columnViewIndex;

        private HeaderMenu(final TableDemo table) {
            JMenuItem item = new JMenuItem("Delete column");
            item.addActionListener(e -> table.deleteColumn(columnViewIndex));
            add(item);

            final MouseAdapter ma = new MouseAdapter() {

                boolean dragged = false;

                @Override
                public void mouseReleased(MouseEvent e) {
                    if (!dragged && e.getButton() == MouseEvent.BUTTON3) {
                        final Point p = e.getPoint();
                        SwingUtilities.invokeLater(() -> {
                            columnViewIndex = table.columnAtPoint(p);
                            show(e.getComponent(), p.x, p.y);
                        });
                    }
                    dragged = false;
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    dragged = true;
                }
            };

            table.getTableHeader().addMouseListener(ma);
            table.getTableHeader().addMouseMotionListener(ma);
        }
    }

    private MyTableModel tableModel = new MyTableModel();

    private TableDemo() {
        new HeaderMenu(this);
        setModel(tableModel);
        setAutoCreateColumnsFromModel(false);
        setDefaultEditor(Object.class, null);
    }

    private void addColumn() {
        TableColumn tc = tableModel.addColumn();
        addColumn(tc);
    }

    void deleteColumn(int idxView) {
        TableColumn tc = getColumnModel().getColumn(idxView);
        tableModel.removeColumn(tc.getModelIndex());
        removeColumn(tc);
    }

    private static void buildAndShowGui() {
        TableDemo table = new TableDemo();
        table.setPreferredScrollableViewportSize(new Dimension(800, 300));
        table.setFillsViewportHeight(true);
        JScrollPane tableScrollPane = new JScrollPane(table);
        JButton buttonAdd = new JButton("Add column");
        buttonAdd.addActionListener(e -> table.addColumn());
        int gaps = 10;
        JPanel panel = new JPanel(new BorderLayout(gaps, gaps));
        panel.setBorder(BorderFactory.createEmptyBorder(gaps, gaps, gaps, gaps));
        panel.add(buttonAdd, BorderLayout.NORTH);
        panel.add(tableScrollPane, BorderLayout.CENTER);
        JFrame frame = new JFrame(table.getClass().getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(panel);
        frame.pack();
        frame.setVisible(true);

        new Timer().schedule(new TimerTask() {

            @Override
            public void run() {
                SwingUtilities.invokeLater(() -> table.tableModel.addRow());
            }
        }, 500, 100);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> buildAndShowGui());
    }
}

【问题讨论】:

  • @TT.,确实如此。请参阅private TableColumn addColumn() 内的addColumn(identifier);
  • 现在已经运行了,还不能指望任何东西,有趣的是删除最后一列并没有给出异常。任何其他列都会导致异常。
  • @TT.,对,感谢您的参与。我的同事刚刚找到了问题和解决方案。我会尽快发布的

标签: java swing jtable


【解决方案1】:

问题是从表/数据模型中的某些modelIndex 中删除一列后,列模型中某些列的TableColumn#getModelIndex() 可能会移动1。下面是一个例子,假设表格有 3 列,下面的代码生成0 1 2

for (int i = 0; i < getColumnModel().getColumnCount(); i++) {
    System.out.print(getColumnModel().getColumn(i).getModelIndex() + " ");
}

然后在从数据模型和列模型中删除列1 后,此代码的输出变为:0 2。因此,JTable 在访问数据模型中不存在的列 2 时会生成 ArrayIndexOutOfBoundsException。这就是为什么删除最后一列不会产生错误的原因。

解决方法是将列的modelIndex移到已删除列的右侧。这可以通过自定义 TableColumnModel 来完成:

private static class MyColumnsModel extends DefaultTableColumnModel {

    private TableColumn deleteColumn(int idxView) {
        if (selectionModel != null) {
            selectionModel.removeIndexInterval(idxView, idxView);
        }
        TableColumn tc = tableColumns.remove(idxView);
        tc.removePropertyChangeListener(this);
        for (TableColumn tableColumn : tableColumns) {
            if (tableColumn.getModelIndex() > tc.getModelIndex()) {
                tableColumn.setModelIndex(tableColumn.getModelIndex() - 1);
            }
        }
        return tc;
    }
}

并与表的添加/删除方法如下:

private void addColumn() {
    TableColumn tc = tableModel.addColumn();
    addColumn(tc); // equal to columnsModel.addColumn(tc);
}

private void deleteColumn(int idxView) {
    TableColumn tc = columnsModel.deleteColumn(idxView);
    tableModel.removeColumn(tc.getModelIndex());
}

这里是完整的固定代码:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;

@SuppressWarnings("serial")
public class TableDemo extends JTable {

    private static class Column {

        private int rowsCounter = 0;
        private final String identifier;

        public Column(String identifier) {
            this.identifier = identifier;
        }

        private String nextCellValue() {
            return (rowsCounter++) + ", id: " + identifier;
        }
    }

    private static class MyTableModel extends DefaultTableModel {

        private final List<Column> columns = new ArrayList<>();
        private int nextColumnIdentifier = 0;

        private void addRow() {
            Object[] row = columns.stream().map(Column::nextCellValue).toArray();
            insertRow(0, row);
        }

        private TableColumn addColumn() {
            String identifier = String.valueOf(nextColumnIdentifier++);
            columns.add(new Column(identifier));
            addColumn(identifier);
            TableColumn tc = new TableColumn();
            tc.setIdentifier(identifier);
            tc.setHeaderValue(identifier);
            tc.setModelIndex(columns.size() - 1);
            return tc;
        }

        private void removeColumn(int idx) {
            columns.remove(idx);
            columnIdentifiers.remove(idx);
            for (Object row : dataVector) {
                ((Vector<?>) row).remove(idx);
            }
            fireTableStructureChanged();
        }
    }

    private static class MyColumnsModel extends DefaultTableColumnModel {

        private TableColumn deleteColumn(int idxView) {
            if (selectionModel != null) {
                selectionModel.removeIndexInterval(idxView, idxView);
            }
            TableColumn tc = tableColumns.remove(idxView);
            tc.removePropertyChangeListener(this);
            for (TableColumn tableColumn : tableColumns) {
                if (tableColumn.getModelIndex() > tc.getModelIndex()) {
                    tableColumn.setModelIndex(tableColumn.getModelIndex() - 1);
                }
            }
            return tc;
        }
    }

    private static class HeaderMenu extends JPopupMenu {

        private int columnViewIndex;

        private HeaderMenu(final TableDemo table) {
            JMenuItem item = new JMenuItem("Delete column");
            item.addActionListener(e -> table.deleteColumn(columnViewIndex));
            add(item);

            final MouseAdapter ma = new MouseAdapter() {

                boolean dragged = false;

                @Override
                public void mouseReleased(MouseEvent e) {
                    if (!dragged && e.getButton() == MouseEvent.BUTTON3) {
                        final Point p = e.getPoint();
                        SwingUtilities.invokeLater(() -> {
                            columnViewIndex = table.columnAtPoint(p);
                            show(e.getComponent(), p.x, p.y);
                        });
                    }
                    dragged = false;
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    dragged = true;
                }
            };

            table.getTableHeader().addMouseListener(ma);
            table.getTableHeader().addMouseMotionListener(ma);
        }
    }

    private MyTableModel tableModel = new MyTableModel();
    private MyColumnsModel columnsModel = new MyColumnsModel();

    private TableDemo() {
        new HeaderMenu(this);
        setModel(tableModel);
        setColumnModel(columnsModel);
        setAutoCreateColumnsFromModel(false);
        setDefaultEditor(Object.class, null);
    }

    private void addColumn() {
        TableColumn tc = tableModel.addColumn();
        addColumn(tc); // equal to columnsModel.addColumn(tc);
    }

    private void deleteColumn(int idxView) {
        TableColumn tc = columnsModel.deleteColumn(idxView);
        tableModel.removeColumn(tc.getModelIndex());
    }

    private static void buildAndShowGui() {
        TableDemo table = new TableDemo();
        table.setPreferredScrollableViewportSize(new Dimension(800, 300));
        table.setFillsViewportHeight(true);
        JScrollPane tableScrollPane = new JScrollPane(table);
        JButton buttonAdd = new JButton("Add column");
        buttonAdd.addActionListener(e -> table.addColumn());
        int gaps = 10;
        JPanel panel = new JPanel(new BorderLayout(gaps, gaps));
        panel.setBorder(BorderFactory.createEmptyBorder(gaps, gaps, gaps, gaps));
        panel.add(buttonAdd, BorderLayout.NORTH);
        panel.add(tableScrollPane, BorderLayout.CENTER);
        JFrame frame = new JFrame(table.getClass().getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(panel);
        frame.pack();
        frame.setVisible(true);

        new Timer().schedule(new TimerTask() {

            @Override
            public void run() {
                SwingUtilities.invokeLater(() -> table.tableModel.addRow());
            }
        }, 500, 100);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> buildAndShowGui());
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-01
    • 2010-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多