我注意到如果您将新模型设置为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。我只想知道在收到渲染器之前是否应该检查传递给渲染器的值。如果是,那么我会认为我的解决方案是一种解决方法,并将向上游报告错误。如果渲染器应该检查它们,我会认为我的解决方案是一个错误修正。