是否传递给getTableCellRendererComponent的行应该在边界内?

时间:2014-04-08 08:12:18

标签: java swing jtable tablecellrenderer

我注意到如果您将新模型设置为JTable,如果行数低于最高选定行,则可能会发生这种情况:

java.lang.IndexOutOfBoundsException: Row index 2 is out of bounds (0..1)
    at com.acme.MyTableModel.getRowObject(MyTableModel.java:184)
    at com.acme.MyCellRenderer.getTableCellRendererComponent(MyCellRenderer.java:144)
    at javax.swing.JTable$AccessibleJTable.getAccessibleChild(JTable.java:7039)
    at javax.swing.JTable$AccessibleJTable.getAccessibleAt(JTable.java:7426)
    at javax.swing.JTable$AccessibleJTable.valueChanged(JTable.java:6939)
    at javax.swing.DefaultListSelectionModel.fireValueChanged(DefaultListSelectionModel.java:184)
    at javax.swing.DefaultListSelectionModel.fireValueChanged(DefaultListSelectionModel.java:164)
    at javax.swing.DefaultListSelectionModel.fireValueChanged(DefaultListSelectionModel.java:211)
    at javax.swing.DefaultListSelectionModel.changeSelection(DefaultListSelectionModel.java:405)
    at javax.swing.DefaultListSelectionModel.changeSelection(DefaultListSelectionModel.java:415)
    at javax.swing.DefaultListSelectionModel.removeSelectionIntervalImpl(DefaultListSelectionModel.java:576)
    at javax.swing.DefaultListSelectionModel.clearSelection(DefaultListSelectionModel.java:420)
    at javax.swing.JTable.clearSelection(JTable.java:2117)
    at javax.swing.JTable.clearSelectionAndLeadAnchor(JTable.java:2125)
    at javax.swing.JTable.tableChanged(JTable.java:4370)
    at javax.swing.JTable.setModel(JTable.java:3688)

这是谁的错误? Swing是否应该在请求渲染器之前检查此索引,还是渲染器检查索引的工作?

编辑:

这是一个非常简化的例子,以防其他地方确实存在错误。按下按钮直到第5行可见,然后选择第5行并再次按下按钮。

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableRowSorter;

public class BreakMyTable implements Runnable {
    JTable table;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new BreakMyTable());
    }

    @Override
    public void run() {
        table = new JTable();
        installNewModel();

        TableColumnModel columnModel = new DefaultTableColumnModel();
        TableColumn column1 = new TableColumn(0, 100);
        column1.setCellRenderer(new CustomRenderer(table.getDefaultRenderer(Integer.class)));
        columnModel.addColumn(column1);
        TableColumn column2 = new TableColumn(1, 400);
        columnModel.addColumn(column2);
        table.setColumnModel(columnModel);
        table.setAutoCreateColumnsFromModel(false);

        JToolBar toolBar = new JToolBar();
        toolBar.setFloatable(false);
        toolBar.add(new AbstractAction("Replace model") {
            @Override
            public void actionPerformed(ActionEvent e) {
                installNewModel();
            }
        });

        JFrame frame = new JFrame();
        frame.setLayout(new BorderLayout());
        frame.add(toolBar, BorderLayout.PAGE_START);
        frame.add(new JScrollPane(table), BorderLayout.CENTER);
        frame.setSize(500, 400);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public void installNewModel() {
        int rows = table.getModel() == null ? 5 :
                   table.getModel().getRowCount() == 5 ? 6 : 5;

        DefaultTableModel model = new CustomModel(rows, 2);
        for (int row = rows - 1; row >= 0; row--) {
            model.setValueAt(row, row, 0);
        }
        table.setModel(model);
        table.setRowSorter(new TableRowSorter<>(model));
    }

    private class CustomModel extends DefaultTableModel {
        public CustomModel(int rowCount, int columnCount) {
            super(rowCount, columnCount);
        }

        // In the real implementation, getValueAt() is implemented by calling getRowObject().
        public Object getRowObject(int row) {
            if (row < 0 || row >= getRowCount()) {
                throw new IndexOutOfBoundsException("Out of bounds: " + row);
            }

            return getValueAt(row, 0);
        }
    }

    private class CustomRenderer implements TableCellRenderer {
        private final TableCellRenderer delegateRenderer;

        private CustomRenderer(TableCellRenderer delegateRenderer) {
            this.delegateRenderer = delegateRenderer;
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            int modelIndex = table.convertRowIndexToModel(row);
            Object rowObject = ((CustomModel) table.getModel()).getRowObject(modelIndex);
            // Would usually use the row object to decorate the cell somehow, e.g. add an icon.

            return delegateRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
        }
    }
}

我可以非常可靠地重现它,我已经知道解决方案是在渲染器中躲避NPE。我只想知道在收到渲染器之前是否应该检查传递给渲染器的值。如果是,那么我会认为我的解决方案是一种解决方法,并将向上游报告错误。如果渲染器应该检查它们,我会认为我的解决方案是一个错误修正。

0 个答案:

没有答案