Question

In the example below I have a JTable a JList and two JButtons (add and remove). In the list are 6 items (Strings) when one clicks the add button a selected value is added to the table.
The Strings in the table are shown by using a custom renderer (a JPanel with a button and a label). The button's text and the label's text are changed to the value of the String.
All goes well until the editor makes his entry. The editor makes it possible to click the button so it's necessary.
When one adds a String to the table for the first time it gets displayed correctly, the row's height gets adjusted to the preferred height of the panel and the text is set for the button and the label.
When one removes the entry from the table by clicking the row and then clicking the remove button all goes as expected.
Now here comes the problem: If one adds a (different) String to the table the row height is and the text of the label and button aren't set (because both the renderer and the editor aren't called, I've checked using breakpoints).
Of course I do want the new row to be displayed using the custom renderer, but how do I do this?

package test;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.util.EventObject;
import javax.swing.AbstractAction;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.event.CellEditorListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

public class MainForm
        extends javax.swing.JFrame
{
    private JTable table;
    private JScrollPane tableScrollPane;
    private JList list;
    private JScrollPane listScrollPane;
    private JButton add;
    private JButton remove;

    public MainForm()
    {
        tableScrollPane = new JScrollPane();
        table = new JTable();
        listScrollPane = new JScrollPane();
        list = new JList();
        add = new JButton(new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                add();
            }
        });
        add.setText("add");
        remove = new JButton(new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                remove();
            }
        });
        remove.setText("remove");

        setLayout(new javax.swing.BoxLayout(getContentPane(), javax.swing.BoxLayout.LINE_AXIS));

        tableScrollPane.setViewportView(table);
        listScrollPane.setViewportView(list);

        add(tableScrollPane);

        DefaultTableModel model = new DefaultTableModel();
        model.addColumn("test");
        table.setModel(model);

        TableColumn col = table.getColumn("test");
        col.setCellRenderer(new CustomTableCellRenderer());
        col.setCellEditor(new CustomTableCellEditor());

        DefaultListModel listModel = new DefaultListModel();
        listModel.addElement("test1");
        listModel.addElement("test2");
        listModel.addElement("test3");
        listModel.addElement("test4");
        listModel.addElement("test5");
        listModel.addElement("test6");
        list.setModel(listModel);

        add(listScrollPane);
        add(add);
        add(remove);
    }

    private void add()
    {
        DefaultTableModel model = (DefaultTableModel) table.getModel();
        model.addRow(new Object[]
                {
                    list.getSelectedValue()
                });
    }

    private void remove()
    {
        int selectedRow = table.getSelectedRow();
        DefaultTableModel model = (DefaultTableModel) table.getModel();
        model.removeRow(selectedRow);
    }

    public static void main(String[] args)
    {
        new MainForm().setVisible(true);
    }

    public class CustomTableCellRenderer
            extends customPanel
            implements TableCellRenderer
    {
        public Component getTableCellRendererComponent(JTable table,
                                                       Object value,
                                                       boolean isSelected,
                                                       boolean hasFocus, int row,
                                                       int column)
        {
            setText((String) value);
            if (isSelected || hasFocus)
            {
                setBackground(UIManager.getColor("List.selectionBackground"));
                setForeground(UIManager.getColor("List.selectionForeground"));
            }
            else
            {
                setBackground(UIManager.getColor("Panel.background"));
                setForeground(UIManager.getColor("Panel.foreground"));
            }
            table.setRowHeight(row, (int)getPreferredSize().height);
            return this;
        }
    }

    public class CustomTableCellEditor
            extends customPanel
            implements TableCellEditor
    {
        Object value;

        public Component getTableCellEditorComponent(JTable table, Object value,
                                                     boolean isSelected, int row,
                                                     int column)
        {
            this.value = value;
            setText((String) value);
            setBackground(UIManager.getColor("List.selectionBackground"));
            setForeground(UIManager.getColor("List.selectionForeground"));
            table.setRowHeight(row, (int)getPreferredSize().height);
            return this;
        }

        public Object getCellEditorValue()
        {
            return value;
        }

        public boolean isCellEditable(EventObject anEvent)
        {
            return true;
        }

        public boolean shouldSelectCell(EventObject anEvent)
        {
            return true;
        }

        public boolean stopCellEditing()
        {
            setBackground(UIManager.getColor("Panel.background"));
            setForeground(UIManager.getColor("Panel.foreground"));
            return true;
        }

        public void cancelCellEditing()
        {
        }

        public void addCellEditorListener(CellEditorListener l)
        {
        }

        public void removeCellEditorListener(CellEditorListener l)
        {
        }
    }

    public class customPanel
            extends JPanel
    {
        private JLabel label;
        private JButton button;

        public customPanel()
        {
            label = new JLabel();
            button = new JButton();
            add(label);
            add(button);
        }

        public void setText(String text)
        {
            label.setText(text);
            button.setText(text);
        }
    }
}
Was it helpful?

Solution

You should never use:

table.setRowHeight(row, (int)getPreferredSize().height);

in a renderer. This will cause an infinite loop since whenever you change the row height the table will repaint() the row and when the renderer is invoked you will again change the row height....

Get rid of that line of code. Instead you can calculate the row height when you add the row to the table. Add this code to the bottom of your add() method:

int row = table.getRowCount() - 1;
Component comp = table.prepareRenderer(table.getCellRenderer(row, 0), row, 0);
int rowHeight = comp.getPreferredSize().height;
table.setRowHeight(row, rowHeight);

Update:

The code works fine without the custom editor. So the problem is the editor. Look at the source code of the AbstractCellEditor to see what happens when editing is stopped. It fires an event to notify the table so the editor can be removed. You don't have this code. So I suggest you extend the AbstractCellEditor instead of extending customPanel so you can easilty fire the appropriate event.

Also, I believe that clicking on a row will invoke the editor, so you need to remove the editor before you remove the row from the model. See Table Stop Editing for a couple of ways to do this.

OTHER TIPS

Implementing the addCellEditorListener(), removeCellEditorListener(), stopCellEditing() and cancelCellEditing() methods works like a charm. An editor needs to be 'disconnected' before removing the selected object.
In one of my tables I have a context menu to modify table entries and before removal I have to call cancelCellEditing() or stopCellEditing() else the table doesn't release the entry or it re-appears like before.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top