Reputation: 33
I have a JTable with JButton like this. first pic
if I click the "+" button,the table is like this. second pic
then, if I click the "-" button on the second line, it's ok.
now the table has only one row which was added by the "+" button.
so the question is that, I clicked the "-" button on the row,it throws an exception below:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 1 >= 1
at java.util.Vector.elementAt(Vector.java:477)
at javax.swing.table.DefaultTableModel.setValueAt(DefaultTableModel.java:664)
what happened?
this is my code:
public class TableDeleteButtonEditor extends AbstractCellEditor implements TableCellEditor{
/**
*
*/
private static final long serialVersionUID = 1L;
JButton button;
String label;
boolean isPushed;
Vector<Vector<String>> vPartsTypeData;
DefaultTableModel dtm;
JTable partsTypeValueTable;
public TableDeleteButtonEditor(Vector<Vector<String>> vPartsTypeData, DefaultTableModel dtm, JTable partsTypeValueTable) {
// TODO Auto-generated constructor stub
//this.setClickCountToStart(1);
this.vPartsTypeData = vPartsTypeData;
this.dtm = dtm;
this.partsTypeValueTable = partsTypeValueTable;
this.vPartsTypeData = vPartsTypeData;
button = new JButton();
int selectedRow = partsTypeValueTable.getSelectedRow();
System.out.println("selectedRow:"+selectedRow);
System.out.println("Count:"+vPartsTypeData.size());
button.addActionListener(new deleteButtonListener());
}
public Component getTableCellEditorComponent(final JTable table, Object value, boolean isSelected,int row, int column) {
if (isSelected) {
button.setFont(new Font("Arial",Font.PLAIN,30));
button.setForeground(table.getSelectionForeground());
button.setBackground(table.getSelectionBackground());
} else {
button.setFont(new Font("Arial",Font.PLAIN,30));
button.setForeground(table.getForeground());
button.setBackground(table.getBackground());
}
label = (value == null) ? "" : value.toString();
button.setText(label);
isPushed = true;
return button;
}
public Object getCellEditorValue() {
if (isPushed) {
}
isPushed = false;
return new String(label);
}
public boolean stopCellEditing() {
isPushed = false;
return super.stopCellEditing();
}
public class deleteButtonListener implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
System.out.println("-----");
int selectedRow = partsTypeValueTable.getSelectedRow();
//System.out.println("selectedRow:"+selectedRow);
//System.out.println("Count:"+vPartsTypeData.size());
dtm.removeRow(selectedRow-1);
//vPartsTypeData.remove(partsTypeValueTable.getSelectedRow());
System.out.println("tableCount:"+partsTypeValueTable.getRowCount());
//dtm.fireTableChanged(null);
partsTypeValueTable.setModel(dtm);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
partsTypeValueTable.repaint();
partsTypeValueTable.validate();
partsTypeValueTable.updateUI();
dtm.fireTableDataChanged();
}
});
}
}
}
Upvotes: 0
Views: 595
Reputation: 347244
So, a little bit of a look at the stack trace can lead us to a better understanding of what's going on...
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 1 >= 1
at java.util.Vector.elementAt(Vector.java:477)
at javax.swing.table.DefaultTableModel.setValueAt(DefaultTableModel.java:664)
at javax.swing.JTable.setValueAt(JTable.java:2741)
at javax.swing.JTable.editingStopped(JTable.java:4723)
at javax.swing.AbstractCellEditor.fireEditingStopped(AbstractCellEditor.java:141)
at javax.swing.AbstractCellEditor.stopCellEditing(AbstractCellEditor.java:85)
So, basically stopCellEditing
is triggering a call to setValueAt
, passing in the editing row
, column
and the result of getCellEditorValue
, but, since this occurs AFTER the ActionListener
has removed the row from the TableModel
, it breaks, as the row, which setValueAt
is trying to update simply no longer exists (or worse is a different row altogether).
It's not the responsibility of the TableCellEditor
to modify the TableModel
, instead, it should be reporting back to the TableModel
a state value which it can use to make decisions about what it should do.
A simplified version of the editor might look something like...
public class TableDeleteButtonEditor extends AbstractCellEditor implements TableCellEditor {
/**
*
*/
private static final long serialVersionUID = 1L;
JButton button;
boolean isPushed;
public TableDeleteButtonEditor() {
button = new JButton();
button.addActionListener(new DeleteButtonListener());
}
public Component getTableCellEditorComponent(final JTable table, Object value, boolean isSelected, int row, int column) {
if (isSelected) {
button.setFont(new Font("Arial", Font.PLAIN, 30));
button.setForeground(table.getSelectionForeground());
button.setBackground(table.getSelectionBackground());
} else {
button.setFont(new Font("Arial", Font.PLAIN, 30));
button.setForeground(table.getForeground());
button.setBackground(table.getBackground());
}
button.setText((value == null) ? "" : value.toString());
isPushed = false;
return button;
}
public Object getCellEditorValue() {
return isPushed;
}
public class DeleteButtonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
isPushed = true;
stopCellEditing();
}
}
}
The core functionality of the editor simply revolves around the state of the isPushed
value.
The TableModel
now needs to inspect this value when setValueAt
is called and respond to it
@Override
public void setValueAt(Object aValue, int row, int column) {
if (column == 0 && (aValue instanceof Boolean)) {
boolean pushed = (boolean) aValue;
if (pushed) {
removeRow(row);
}
}
}
And viola, row is now deleted and everybody is happy.
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
DefaultTableModel model = new DefaultTableModel(new String[]{"A"}, 0) {
@Override
public void setValueAt(Object aValue, int row, int column) {
if (column == 0 && (aValue instanceof Boolean)) {
boolean pushed = (boolean) aValue;
if (pushed) {
removeRow(row);
}
}
}
};
model.addRow(new Object[]{"-"});
model.addRow(new Object[]{"-"});
JTable table = new JTable(model);
TableColumn column = table.getColumnModel().getColumn(0);
column.setCellEditor(new TableDeleteButtonEditor());
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TableDeleteButtonEditor extends AbstractCellEditor implements TableCellEditor {
/**
*
*/
private static final long serialVersionUID = 1L;
JButton button;
boolean isPushed;
JTable partsTypeValueTable;
public TableDeleteButtonEditor() {
button = new JButton();
button.addActionListener(new DeleteButtonListener());
}
public Component getTableCellEditorComponent(final JTable table, Object value, boolean isSelected, int row, int column) {
partsTypeValueTable = table;
if (isSelected) {
button.setFont(new Font("Arial", Font.PLAIN, 30));
button.setForeground(table.getSelectionForeground());
button.setBackground(table.getSelectionBackground());
} else {
button.setFont(new Font("Arial", Font.PLAIN, 30));
button.setForeground(table.getForeground());
button.setBackground(table.getBackground());
}
button.setText((value == null) ? "" : value.toString());
isPushed = false;
return button;
}
public Object getCellEditorValue() {
return isPushed;
}
public class DeleteButtonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
isPushed = true;
stopCellEditing();
}
}
}
}
As I said, I don't like this approach, it's a personally thing, but as a user, I find it frustrating and much prefer something more like this for example.
However, you might like to also have a look at Table Button Column for another approach
Upvotes: 1