В предыдущем вопросе ( Преобразование modelRowIndex в viewRowIndex для отсортированного JTable ) я указал, что я пытался создать «простой» JTable, который использовал TableModel, чтобы связать ArrayList с JTable, используя TableModel. Моей целью было - и остается - сохранить все встроенные в Java функции JTable, которые позволяют редактировать ячейки, сортировать строки и переставлять столбцы. Благодаря вашей помощи этот функционал теперь работает.
Я сейчас пытаюсь добавить возможность вставки и удаления строк таблицы. (Обновленный) пример, который я привожу здесь, работает ... ЗА ИСКЛЮЧЕНИЕМ ... при определенной последовательности операций выдается исключение IndexOutOfBoundsException. Вот мой код:
package tableexample;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
public final class TableExample extends JFrame {
List<REItem> REList;
JTable tblREList;
JButton btnAddInsertRE, btnDeleteRE;
JScrollPane spMain;
JFrame frame;
Container pane;
public TableExample() {
// create and populate the ArrayList
REList = new ArrayList<>();
REList.add(new REItem("Template1", "Comment1"));
REList.add(new REItem("Template2", "Comment2"));
RETableModel retm = new RETableModel(REList); // Connect the List to the TableModel
// create GUI components
frame = new JFrame ("Table Example");
btnAddInsertRE = new JButton("Add/Insert");
btnDeleteRE = new JButton("Delete");
tblREList = new JTable(retm);
tblREList.setAutoCreateRowSorter(true);
spMain = new JScrollPane(tblREList);
// add button ActionListeners
btnAddInsertRE.addActionListener((ActionEvent evt) -> { btnAddInsertREActionPerformed(evt); });
btnDeleteRE.addActionListener((ActionEvent evt) -> { btnDeleteREActionPerformed(evt); });
// place GUI components and make the GUI visible
pane = frame.getContentPane();
pane.setLayout (null);
pane.add(btnAddInsertRE);
pane.add(btnDeleteRE);
pane.add(spMain);
btnAddInsertRE.setBounds (10, 10, 100, 25);
btnDeleteRE.setBounds (120, 10, 100, 25);
spMain.setBounds (10, 45, spMain.getPreferredSize().width, spMain.getPreferredSize().height);
frame.setSize(spMain.getWidth() + 40, spMain.getHeight() + 95);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
} // end TableExample constructor
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
TableExample notUsed = new TableExample();
});
} //end main
private void btnAddInsertREActionPerformed(ActionEvent evt) {
// Add a FileSelection object to the ArrayList
int r = tblREList.getSelectedRow(); // get row selection, if any
if (r < 0) { // no row selected
REList.add(new REItem("NewTemplate", "NewComment")); // append new item to end
r = REList.size()-1; // get index to new item
} else { // else no row selected
REList.add(r, new REItem("NewTemplate", "NewComment")); // insert above selected row
} // row selected or not
spMain.setViewportView(tblREList); // repaint the updated table
tblREList.getSelectionModel().setSelectionInterval(r, r); // select the new row
}
private void btnDeleteREActionPerformed(ActionEvent evt) {
int[] selRows = tblREList.getSelectedRows(); // see if any rows are selected
if (selRows.length>0) { // at least one row is selected
for (int r=selRows.length-1; r>=0; r--) { // delete each row, from the bottom up,
REList.remove(r); // so that indexes are correct and
} // don't change with each delete
tblREList.clearSelection(); // clear the row selection data
spMain.setViewportView(tblREList); // repaint the updated table
} else { // else no row(s) selected
JOptionPane.showMessageDialog(null, "Must select at least one item to delete");
} // no row selected
} // end btnDeleteREActionPerformed
public final class REItem {
String template;
String comment;
public REItem(String tmp, String cmt) {
this.template = tmp;
this.comment = cmt;
}
} // end class REItem
public class RETableModel extends AbstractTableModel {
private List<REItem> reList = new ArrayList();
private final String[] columnNames = { "Template", "Comment" };
public RETableModel(List<REItem> list){
this.reList = list;
}
@Override
public String getColumnName(int column){
return columnNames[column];
}
@Override
public int getRowCount() {
return reList.size();
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public Object getValueAt(int row, int column) {
switch (column) {
case 0: return reList.get(row).template;
case 1: return reList.get(row).comment;
}
return null; // default case
}
@Override
public boolean isCellEditable(int row, int column) {
return true;
}
@Override
public Class<?> getColumnClass(int column){
switch (column){
case 0: return String.class;
case 1: return String.class;
}
return null; // default case
}
@Override
public void setValueAt(Object value, int row, int column) {
switch (column) {
case 0: reList.get(row).template = value.toString(); break;
case 1: reList.get(row).comment = value.toString(); break;
}
// uncommenting the below often causes IndexOutOfBoundsException: Invalid range exception
fireTableCellUpdated(row, column);
} // end setValueAt
} // end RETableModel
} // end class TableExample
Проблема может быть воспроизведена следующим образом: Запустите приведенный выше пример, нажмите кнопку «Добавить / вставить», чтобы добавить новую строку в таблицу, щелкните заголовок любого столбца, чтобы заново отсортировать таблицу, затем нажмите «Удалить». Кнопка: генерируется «IndexOutOfBoundsException», указывающее, что индекс «строки», указанный методом getValueAt TableModel, имеет недостатки.
Я предполагаю, что проблема связана с необходимостью использования моего метода (ов) TableModel.getValueAt (и, возможно, .setValueAt ???) для преобразования между индексами столбцов TableModel и индексами столбцов View, но, на мой взгляд, я могу не понять, как или где сделать преобразование. Более того, этот вопрос ( Преобразовать modelRowIndex в viewRowIndex для отсортированного JTable ) указывает на то, что необходимо преобразование между TableModel и View-индексами строк, И что повторная сортировка таблицы ДОЛЖНА произойти до того, как будет выполнено преобразование индекса.
Как ни старайся, я не могу понять, как выполнить преобразование и / или как убедиться, что преобразование произойдет ПОСЛЕ ТОГО, КАК таблица обновлена и повторно отсортирована. Нужен ли мне слушатель? Если так, то как это должно выглядеть?
Можете ли вы дать некоторые разъяснения и помочь?