I asked a question about this a while ago, but I was ill-prepared.
Rather than explaining I will hope that someone doesn't mind compiling
some code that I've included below. I stuck it all in a file called
JTableTest.java, compiled and ran it in Netbeans.
To see the problem, click Add a couple of times, and then select, last
row and press delete. Click on one of the other cells, see the result.
Can anyone tell me what the problem is? If I remove the row
tc.setCellEditor(new JTextFieldTableCellEditor()); it doesn't throw the
exception.
Thanks
Lionel
/*
* JTableTest.java
*
* Created on 27 April 2007, 18:20
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package tciworks;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
/**
*
* @author Lionel van den Berg.
*/
public class JTableTest extends JFrame {
public JTableTest() {
JPanel contentPane = new JPanel();
contentPane.setLayout(new GridLayout(0, 1));
final TestTable table = new TestTable(new TestTableModel());
contentPane.add(table);
this.setContentPane(contentPane);
this.setSize(new Dimension(400, 400));
JButton addButton = new JButton("Add");
addButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
((TestTableModel)table.getModel()).addRow();
}
});
JButton deleteButton = new JButton("Delete");
deleteButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int rowNumber = table.getSelectedRow();
((TestTableModel)table.getModel()).deleteRow(rowNumber);
}
});
contentPane.add(addButton);
contentPane.add(deleteButton);
}
protected void processWindowEvent(WindowEvent e) {
super.processWindowEvent(e);
if(e.getID() == WindowEvent.WINDOW_CLOSING) {
System.exit(0);
}
}
public static void main(String []args) {
JTableTest test = new JTableTest();
test.setVisible(true);
}
}
/**
* The constructer is extended to set the rendering, cell sizes, text
position
* and so on.
*
* @author Lionel van den Berg
*/
class TestTable extends JTable {
public TestTable(AbstractTableModel tableModel) {
super(tableModel);
setRendering();
}
/**
* Sets the location of text in cells, fonts etc.
*
*/
private void setRendering() {
AbstractTableModel tableModel = (AbstractTableModel)getModel();
TableColumn tc;
DefaultTableCellRenderer crEditible = new
DefaultTableCellRenderer(),
crNotEditible = new DefaultTableCellRenderer();
crEditible.setHorizontalAlignment(DefaultTableCellRenderer.CENTER);
crNotEditible.setHorizontalAlignment(DefaultTableCellRenderer.CENTER);
crNotEditible.setBackground(new Color(230, 230, 230));
//all columns should have centralised text
for(int columnIndex = 0; columnIndex <
tableModel.getColumnCount();
columnIndex++) {
tc = getColumnModel().getColumn(columnIndex);
tc.setCellEditor(new JTextFieldTableCellEditor());
//if the column is editble, OR the whole table is not editible
//(tableModel.isEditable() (means we don't distinguish between
//cells). We use 0 for the row it's just a dummy row
because we
//know it applies to the whole column
if(tableModel.isCellEditable(0, columnIndex)) {
tc.setCellRenderer(crEditible);
} else {
tc.setCellRenderer(crNotEditible);
}
}
setHeaderBold();
}
/** Gets the number of columns in the table. */
public int getNumColumns() {
return getModel().getColumnCount();
}
/**
* Sets the headings of the table columns bold making them more visble.
*/
public void setHeaderBold() {
//set the headers bold
JTableHeader header = getTableHeader();
if(header == null) {
return;
}
final Font boldFont = header.getFont().deriveFont(Font.BOLD);
final TableCellRenderer headerRenderer =
header.getDefaultRenderer();
header.setDefaultRenderer(new TableCellRenderer() {
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus,
int row,
int column ) {
Component comp =
headerRenderer.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column);
comp.setFont(boldFont);
return comp;
}
});
}
}
class TestTableModel extends AbstractTableModel {
private String[] columnNames = {"Column 1", "Column 2", "Column 3"};
private ArrayList list = new ArrayList();
private int count = 0;
public TestTableModel() {
}
public int getRowCount() {
return list.size();
}
public int getColumnCount() {
return getColumnNames().length;
}
public String getColumnName(int column) {
return getColumnNames()[column];
}
public boolean isCellEditable(int row, int col) {
//last two columns not editible
if(col > 1) {
return false;
} else {
return true;
}
}
public boolean isColumnEditable(int col) {
//last two columns not editible
if(col > 1) {
return false;
} else {
return true;
}
}
/**
* Creates a new dose and adds it to the table model.
*/
public void addRow() {
list.add(new String[]{"row" + count + " val1",
"row" + count + " val2", "row" + count + " val3"});
fireTableRowsInserted(list.size() - 1, list.size() - 1);
}
public void deleteRow(int row) {
list.remove(row);
fireTableRowsDeleted(row, row);
}
public Object getValueAt(int rowIndex, int columnIndex) {
String []row = (String [])list.get(rowIndex);
return row[columnIndex];
}
public void setValueAt(Object updateValue, int rowIndex, int
colIndex) {
String []row = (String [])list.get(rowIndex);
row[colIndex] = (String)updateValue;
}
/**
* Returns the array of column names for this table.
*
* @return the array of column names for this table.
*/
public String[] getColumnNames() {
return columnNames;
}
}
class JTextFieldTableCellEditor extends AbstractCellEditor
implements TableCellEditor {
private JTextField tf = new JTextField();
public JTextFieldTableCellEditor() {
super();
}
public Object getCellEditorValue() {
return tf.getText();
}
public Component getTableCellEditorComponent(JTable table,
Object value, boolean isSelected, int row, int column) {
tf.setText(value.toString());
tf.addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent fe) {
tf.selectAll();
}
});
tf.selectAll();
return tf;
}
}
katjapanisch@yahoo.com - 27 Apr 2007 13:53 GMT
> Can anyone tell me what the problem is? If I remove the row
> tc.setCellEditor(new JTextFieldTableCellEditor()); it doesn't throw the
> exception.
If you select the row by clicking on an editable cell, your cell
editor is not stopped before the row is deleted.
Try stopping the active cell editor in your action listener before
deleting.
deleteButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int rowNumber = table.getSelectedRow();
TableCellEditor editor = table.getCellEditor();
if (editor != null) {
editor.stopCellEditing();
}
((TestTableModel)
table.getModel()).deleteRow(rowNumber);
}
});
Otherwise, the selection of a new cell after row deletion will stop
the cell editor in the deleted
row, and JTable.editingStopped tries to set the new value for the
edited cell in a row that no longer exists.
If you don't set a custom cell editor, DefaultCellEditor will be used.
Because DefaultCellEditor stops cell editing on actionPerformed,
the exception does not occur.
Katja
Lionel van den Berg - 28 Apr 2007 09:06 GMT
> Otherwise, the selection of a new cell after row deletion will stop
> the cell editor in the deleted
> row, and JTable.editingStopped tries to set the new value for the
> edited cell in a row that no longer exists.
Excellent. Thank you. I've added some new code in my custom JTable to
stop the editing an all is well :).
Thanks
Lionel.