Удаление строки JTable дает исключение ArrayOutofBounds -1
Я просматривал множество сайтов по этому поводу и пробовал разные вещи, но я в тупике. Был бы признателен за некоторую помощь. Работает условие, которое проверяет, установлен ли флажок (true), но как только я делаю model.removeRow(row), он выдает мне эту ошибку.
public class ExpenditurePanel extends JPanel implements TableModelListener{
private static final long serialVersionUID = 1L;
private static TableModel1 model;
private JTable table;
public ExpenditurePanel() {
model = new TableModel1();
table = new JTable(model);
table.setBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null, null));
table.setRowSelectionAllowed(false);
table.setFillsViewportHeight(true);
table.setCellSelectionEnabled(true);
table.setColumnSelectionAllowed(false);
model.addColumn("Name");
model.addColumn("Week");
model.addColumn("Fortnight");
model.addColumn("Month");
model.addColumn("Year");
model.addColumn("Remove");
Object[] default1 = {"Accomodation","","","","",false};
Object[] default2 = {"Food","","","","",false};
model.addRow(default1);
model.addRow(default2);
model.addTableModelListener(this);
this.add(new JScrollPane(table));
table.setRowSorter(null);
}
public static TableModel1 getModel(){
return model;
}
@Override
public void tableChanged(TableModelEvent e) {
int row = e.getFirstRow();
int column = e.getColumn();
model = (TableModel1) e.getSource();
String columnName = model.getColumnName(column);
Object data = model.getValueAt(row, column);
if(model.getValueAt(row,column) == Boolean.TRUE){
System.out.println("Condition working");
//this part keeps giving me error.
model.removeRow(row);
}
}
}
Вот что он выбрасывает:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.Vector.elementData(Unknown Source)
at java.util.Vector.elementAt(Unknown Source)
at javax.swing.table.DefaultTableModel.getValueAt(Unknown Source)
at studentBudget.ExpenditurePanel.tableChanged(ExpenditurePanel.java:53)
at javax.swing.table.AbstractTableModel.fireTableChanged(Unknown Source)
at javax.swing.table.AbstractTableModel.fireTableRowsDeleted(Unknown Source)
at javax.swing.table.DefaultTableModel.removeRow(Unknown Source)
at studentBudget.ExpenditurePanel.tableChanged(ExpenditurePanel.java:59)
at javax.swing.table.AbstractTableModel.fireTableChanged(Unknown Source)
at javax.swing.table.AbstractTableModel.fireTableCellUpdated(Unknown Source)
at javax.swing.table.DefaultTableModel.setValueAt(Unknown Source)
at javax.swing.JTable.setValueAt(Unknown Source)
at javax.swing.JTable.editingStopped(Unknown Source)
at javax.swing.AbstractCellEditor.fireEditingStopped(Unknown Source)
at javax.swing.DefaultCellEditor$EditorDelegate.stopCellEditing(Unknown Source)
at javax.swing.DefaultCellEditor.stopCellEditing(Unknown Source)
at javax.swing.DefaultCellEditor$EditorDelegate.actionPerformed(Unknown Source)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.JToggleButton$ToggleButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at javax.swing.plaf.basic.BasicTableUI$Handler.repostEvent(Unknown Source)
at javax.swing.plaf.basic.BasicTableUI$Handler.mouseReleased(Unknown Source)
at java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$000(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
4 ответа
Вы изменяете TableModel внутри TableModelListener, и это кажется мне немного рискованным и представляет риск для нежелательной рекурсии. Я бы пересмотрел ваш код, чтобы не делать этого, если это возможно. Если вы все еще застряли, то вы должны создать и опубликовать sscce.
Редактировать 1
Если я сделаю sscce из вашего кода и добавлю операторы println:
import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.event.*;
import javax.swing.table.*;
public class ExpenditurePanel extends JPanel implements TableModelListener {
private static final long serialVersionUID = 1L;
private static TableModel1 model;
private JTable table;
public ExpenditurePanel() {
model = new TableModel1();
table = new JTable(model);
table.setBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null,
null));
table.setRowSelectionAllowed(false);
table.setFillsViewportHeight(true);
table.setCellSelectionEnabled(true);
table.setColumnSelectionAllowed(false);
model.addColumn("Name");
model.addColumn("Week");
model.addColumn("Fortnight");
model.addColumn("Month");
model.addColumn("Year");
model.addColumn("Remove");
Object[] default1 = { "Accomodation", "", "", "", "", false };
Object[] default2 = { "Food", "", "", "", "", false };
model.addRow(default1);
model.addRow(default2);
model.addTableModelListener(this);
this.add(new JScrollPane(table));
table.setRowSorter(null);
}
public static TableModel1 getModel() {
return model;
}
@Override
public void tableChanged(TableModelEvent e) {
int row = e.getFirstRow();
int column = e.getColumn();
// **** printf added below
System.out.printf("[row: %d, column: %d]%n", row, column);
model = (TableModel1) e.getSource();
String columnName = model.getColumnName(column);
// **** println added below
System.out.println("columnName: " + columnName);
Object data = model.getValueAt(row, column);
if (model.getValueAt(row, column) == Boolean.TRUE) {
System.out.println("Condition working");
// *** AIOOBE called here
model.removeRow(row);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ExpenditurePanel panel = new ExpenditurePanel();
JFrame frame = new JFrame("foo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
});
}
}
// **** no idea how close this is to your code *****
class TableModel1 extends DefaultTableModel {
@Override
public Class<?> getColumnClass(int columnIndex) {
if (getRowCount() == 0) {
return super.getColumnClass(columnIndex);
}
Object value = getValueAt(0, columnIndex);
if (value == null) {
return super.getColumnClass(columnIndex);
}
return value.getClass();
}
}
Я получаю этот вывод:
[row: 0, column: 5]
columnName: Remove
Condition working
[row: 0, column: -1]
columnName:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.Vector.elementAt(Unknown Source)
...... etc ......
Итак, вы видите модель слушателя tableChanged(...)
метод вызывается дважды из-за рекурсии, возникающей из-за изменения прослушиваемого объекта внутри слушателя, и во второй раз, когда он вызывается, у него есть столбец -1, который вызывает ваш AIOOBE.
Редактировать 2
Потенциальное решение состоит в том, чтобы удалить слушателя до внесения изменений в модель, а затем повторно добавить слушателя после внесения изменения:
public void tableChanged(TableModelEvent e) {
int row = e.getFirstRow();
int column = e.getColumn();
System.out.printf("[row: %d, column: %d]%n", row, column);
model = (TableModel1) e.getSource();
String columnName = model.getColumnName(column);
System.out.println("columnName: " + columnName);
Object data = model.getValueAt(row, column);
if (model.getValueAt(row, column) == Boolean.TRUE) {
System.out.println("Condition working");
model.removeTableModelListener(this);
model.removeRow(row);
model.addTableModelListener(this);
}
}
Чтобы немного рассказать о правильном поиске в @Hover и предположении правильного варианта использования, такого как "у нас есть список заданий, если выполнено одно удаление этого задания из списка":
Основная проблема с вашим слушателем состоит в том, что он не проверяет, с каким типом изменений он уведомлен: вас интересует только обновление определенного столбца, а не какого-либо другого (например, удаление строки), которое вызывает ошибка при втором уведомлении: удаленное событие типа события имеет столбец -1
Решение состоит в том, чтобы добавить проверку и ничего не делать, если триггер не был обновлением для определенного столбца:
@Override
public void tableChanged(TableModelEvent e) {
if (!TableUtilities.isUpdate(e)) return;
...
}
Анализ TableModelEvent немного неудобен (не совсем интуитивно понятный, требует тщательного чтения javadoc TableModelEvent), который мне лень хранить в памяти, поэтому в SwingX есть служебный класс, для удобства соответствующие части скопированы здесь:
/**
* Returns a boolean indication whether the event represents a
* dataChanged type.
*
* @param e the event to examine.
* @return true if the event is of type dataChanged, false else.
*/
public static boolean isDataChanged(TableModelEvent e) {
if (e == null)
return false;
return e.getType() == TableModelEvent.UPDATE && e.getFirstRow() == 0
&& e.getLastRow() == Integer.MAX_VALUE;
}
/**
* Returns a boolean indication whether the event represents a
* update type.
*
* @param e the event to examine.
* @return true if the event is a true update, false
* otherwise.
*/
public static boolean isUpdate(TableModelEvent e) {
if (isStructureChanged(e))
return false;
return e.getType() == TableModelEvent.UPDATE
&& e.getLastRow() < Integer.MAX_VALUE;
}
/**
* Returns a boolean indication whether the event represents a
* insert type.
*
* @param e the event to examine
* @return true if the event is of type insert, false otherwise.
*/
public static boolean isInsert(TableModelEvent e) {
if (e == null) return false;
return TableModelEvent.INSERT == e.getType();
}
/**
* Returns a boolean indication whether the event represents a
* structureChanged type.
*
* @param e the event to examine.
* @return true if the event is of type structureChanged or null, false
* else.
*/
public static boolean isStructureChanged(TableModelEvent e) {
return e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW;
}
Просто сделай так:
clsController.getTable().setSortable( false );
model.removeRow( selectedModelRow );
clsController.getTable().setSortable( true );
и это работает!
Вы можете проверить его тип события. При этом вы можете пропустить содержимое метода tableChanged для добавления и удаления.
public void tableChanged(TableModelEvent e) {
int type = e.getType();
if (type != TableModelEvent.DELETE && type != TableModelEvent.INSERT) {
// operation only for update
}
}